diff --git a/packages/editor-ui/src/main.ts b/packages/editor-ui/src/main.ts index 980bae1a06f1f..ff291537d2d33 100644 --- a/packages/editor-ui/src/main.ts +++ b/packages/editor-ui/src/main.ts @@ -10,6 +10,7 @@ import 'vue-json-pretty/lib/styles.css'; import '@jsplumb/browser-ui/css/jsplumbtoolkit.css'; import 'n8n-design-system/css/index.scss'; import './n8n-theme.scss'; +import './styles/autocomplete-theme.scss'; import '@fontsource/open-sans/latin-400.css'; import '@fontsource/open-sans/latin-600.css'; diff --git a/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts b/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts index 61683cd9ee049..5b5362acd3b4b 100644 --- a/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts +++ b/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts @@ -172,7 +172,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completions'); - expect(found).toHaveLength(extensions('string').length); + expect(found).toHaveLength(extensions('string').length + natives('string').length); expect(found.map((c) => c.label).every((l) => !l.endsWith('()'))); }); @@ -183,7 +183,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completions'); - expect(found).toHaveLength(extensions('number').length); + expect(found).toHaveLength(extensions('number').length + natives('number').length); expect(found.map((c) => c.label).every((l) => !l.endsWith('()'))); }); @@ -194,7 +194,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completions'); - expect(found).toHaveLength(extensions('array').length); + expect(found).toHaveLength(extensions('array').length + natives('array').length); expect(found.map((c) => c.label).every((l) => !l.endsWith('()'))); }); }); @@ -206,14 +206,16 @@ describe('Resolution-based completions', () => { test('should return completions for: {{ $input.| }}', () => { resolveParameterSpy.mockReturnValue($input); - expect(completions('{{ $input.| }}')).toHaveLength(Reflect.ownKeys($input).length); + expect(completions('{{ $input.| }}')).toHaveLength( + Reflect.ownKeys($input).length + natives('object').length, + ); }); test("should return completions for: {{ $('nodeName').| }}", () => { resolveParameterSpy.mockReturnValue($('Rename')); expect(completions('{{ $("Rename").| }}')).toHaveLength( - Reflect.ownKeys($('Rename')).length - ['pairedItem'].length, + Reflect.ownKeys($('Rename')).length + natives('object').length - ['pairedItem'].length, ); }); @@ -224,7 +226,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completion'); - expect(found).toHaveLength(1); + expect(found).toHaveLength(3); expect(found[0].label).toBe('json'); }); @@ -235,7 +237,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completion'); - expect(found).toHaveLength(1); + expect(found).toHaveLength(3); expect(found[0].label).toBe('json'); }); @@ -246,7 +248,7 @@ describe('Resolution-based completions', () => { if (!found) throw new Error('Expected to find completion'); - expect(found).toHaveLength(1); + expect(found).toHaveLength(3); expect(found[0].label).toBe('json'); }); @@ -261,7 +263,8 @@ describe('Resolution-based completions', () => { resolveParameterSpy.mockReturnValue($input.item.json); expect(completions('{{ $input.item.| }}')).toHaveLength( - Object.keys($input.item.json).length + extensions('object').length, + Object.keys($input.item.json).length + + (extensions('object').length + natives('object').length), ); }); @@ -269,7 +272,8 @@ describe('Resolution-based completions', () => { resolveParameterSpy.mockReturnValue($input.first().json); expect(completions('{{ $input.first().| }}')).toHaveLength( - Object.keys($input.first().json).length + extensions('object').length, + Object.keys($input.first().json).length + + (extensions('object').length + natives('object').length), ); }); @@ -277,7 +281,8 @@ describe('Resolution-based completions', () => { resolveParameterSpy.mockReturnValue($input.last().json); expect(completions('{{ $input.last().| }}')).toHaveLength( - Object.keys($input.last().json).length + extensions('object').length, + Object.keys($input.last().json).length + + (extensions('object').length + natives('object').length), ); }); @@ -285,33 +290,41 @@ describe('Resolution-based completions', () => { resolveParameterSpy.mockReturnValue($input.all()[0].json); expect(completions('{{ $input.all()[0].| }}')).toHaveLength( - Object.keys($input.all()[0].json).length + extensions('object').length, + Object.keys($input.all()[0].json).length + + (extensions('object').length + natives('object').length), ); }); test('should return completions for: {{ $input.item.json.str.| }}', () => { resolveParameterSpy.mockReturnValue($input.item.json.str); - expect(completions('{{ $input.item.json.str.| }}')).toHaveLength(extensions('string').length); + expect(completions('{{ $input.item.json.str.| }}')).toHaveLength( + extensions('string').length + natives('string').length, + ); }); test('should return completions for: {{ $input.item.json.num.| }}', () => { resolveParameterSpy.mockReturnValue($input.item.json.num); - expect(completions('{{ $input.item.json.num.| }}')).toHaveLength(extensions('number').length); + expect(completions('{{ $input.item.json.num.| }}')).toHaveLength( + extensions('number').length + natives('number').length, + ); }); test('should return completions for: {{ $input.item.json.arr.| }}', () => { resolveParameterSpy.mockReturnValue($input.item.json.arr); - expect(completions('{{ $input.item.json.arr.| }}')).toHaveLength(extensions('array').length); + expect(completions('{{ $input.item.json.arr.| }}')).toHaveLength( + extensions('array').length + natives('array').length, + ); }); test('should return completions for: {{ $input.item.json.obj.| }}', () => { resolveParameterSpy.mockReturnValue($input.item.json.obj); expect(completions('{{ $input.item.json.obj.| }}')).toHaveLength( - Object.keys($input.item.json.obj).length + extensions('object').length, + Object.keys($input.item.json.obj).length + + (extensions('object').length + natives('object').length), ); }); }); diff --git a/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts b/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts index 72d93075a8799..6f8392bf9f77d 100644 --- a/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts +++ b/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts @@ -1,4 +1,4 @@ -import { ExpressionExtensions, NativeMethods, IDataObject } from 'n8n-workflow'; +import { ExpressionExtensions, NativeMethods, IDataObject, DocMetadata } from 'n8n-workflow'; import { DateTime } from 'luxon'; import { i18n } from '@/plugins/i18n'; import { resolveParameter } from '@/mixins/workflowHelpers'; @@ -15,6 +15,10 @@ import { } from './utils'; import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete'; import type { ExtensionTypeName, FnToDoc, Resolved } from './types'; +import { sanitizeHtml } from '@/utils'; +import { NativeDoc } from 'n8n-workflow/src/Extensions/Extensions'; + +type AutocompleteOptionType = 'function' | 'keyword'; /** * Resolution-based completions offered according to datatype. @@ -111,11 +115,14 @@ function datatypeOptions(resolved: Resolved, toResolve: string) { } export const natives = (typeName: ExtensionTypeName): Completion[] => { - const natives = NativeMethods.find((ee) => ee.typeName.toLowerCase() === typeName); + const natives: NativeDoc = NativeMethods.find((ee) => ee.typeName.toLowerCase() === typeName); if (!natives) return []; - return toOptions(natives.functions, typeName); + const nativeProps = natives.properties ? toOptions(natives.properties, typeName, 'keyword') : []; + const nativeMethods = toOptions(natives.functions, typeName, 'function'); + + return [...nativeProps, ...nativeMethods]; }; export const extensions = (typeName: ExtensionTypeName) => { @@ -132,50 +139,116 @@ export const extensions = (typeName: ExtensionTypeName) => { return toOptions(fnToDoc, typeName); }; -export const toOptions = (fnToDoc: FnToDoc, typeName: ExtensionTypeName) => { +export const toOptions = ( + fnToDoc: FnToDoc, + typeName: ExtensionTypeName, + optionType: AutocompleteOptionType = 'function', +) => { return Object.entries(fnToDoc) .sort((a, b) => a[0].localeCompare(b[0])) .map(([fnName, fn]) => { const option: Completion = { - label: fnName + '()', - type: 'function', + label: optionType === 'function' ? fnName + '()' : fnName, + type: optionType, }; option.info = () => { const tooltipContainer = document.createElement('div'); + tooltipContainer.classList.add('autocomplete-info-container'); if (!fn.doc?.description) return null; - tooltipContainer.style.display = 'flex'; - tooltipContainer.style.flexDirection = 'column'; - tooltipContainer.style.paddingTop = 'var(--spacing-4xs)'; - tooltipContainer.style.paddingBottom = 'var(--spacing-4xs)'; + const header = + optionType === 'function' + ? createFunctionHeader(typeName, fn) + : createPropHeader(typeName, fn); + header.classList.add('autocomplete-info-header'); + tooltipContainer.appendChild(header); - const header = document.createElement('div'); - header.style.marginBottom = 'var(--spacing-2xs)'; + const descriptionBody = document.createElement('div'); + descriptionBody.classList.add('autocomplete-info-description'); + const descriptionText = document.createElement('p'); + descriptionText.innerHTML = sanitizeHtml( + fn.doc.description.replace(/`(.*?)`/g, '$1'), + ); + descriptionBody.appendChild(descriptionText); + if (fn.doc.docURL) { + const descriptionLink = document.createElement('a'); + descriptionLink.setAttribute('target', '_blank'); + descriptionLink.setAttribute('href', fn.doc.docURL); + descriptionLink.innerText = i18n.autocompleteUIValues['docLinkLabel'] || 'Learn more'; + descriptionLink.addEventListener('mousedown', (event: MouseEvent) => { + // This will prevent documentation popup closing before click + // event gets to links + event.preventDefault(); + }); + descriptionLink.classList.add('autocomplete-info-doc-link'); + descriptionBody.appendChild(descriptionLink); + } + tooltipContainer.appendChild(descriptionBody); - const typeNameSpan = document.createElement('span'); - typeNameSpan.innerHTML = typeName.slice(0, 1).toUpperCase() + typeName.slice(1) + '.'; + return tooltipContainer; + }; - const functionNameSpan = document.createElement('span'); - functionNameSpan.innerHTML = fn.doc.name + '()'; - functionNameSpan.style.fontWeight = 'var(--font-weight-bold)'; + return option; + }); +}; - const returnTypeSpan = document.createElement('span'); - returnTypeSpan.innerHTML = ': ' + fn.doc.returnType; +const createFunctionHeader = (typeName: string, fn: { doc?: DocMetadata | undefined }) => { + const header = document.createElement('div'); + if (fn.doc) { + const typeNameSpan = document.createElement('span'); + typeNameSpan.innerHTML = typeName.slice(0, 1).toUpperCase() + typeName.slice(1) + '.'; + + const functionNameSpan = document.createElement('span'); + functionNameSpan.classList.add('autocomplete-info-name'); + functionNameSpan.innerHTML = `${fn.doc.name}`; + let functionArgs = '('; + if (fn.doc.args) { + functionArgs += fn.doc.args + .map((arg) => { + let argString = `${arg.name}`; + if (arg.type) { + argString += `: ${arg.type}`; + } + return argString; + }) + .join(', '); + } + functionArgs += ')'; + const argsSpan = document.createElement('span'); + argsSpan.classList.add('autocomplete-info-name-args'); + argsSpan.innerText = functionArgs; + + const returnTypeSpan = document.createElement('span'); + returnTypeSpan.innerHTML = ': ' + fn.doc.returnType; + + header.appendChild(typeNameSpan); + header.appendChild(functionNameSpan); + header.appendChild(argsSpan); + header.appendChild(returnTypeSpan); + } + return header; +}; - header.appendChild(typeNameSpan); - header.appendChild(functionNameSpan); - header.appendChild(returnTypeSpan); +const createPropHeader = (typeName: string, property: { doc?: DocMetadata | undefined }) => { + const header = document.createElement('div'); + if (property.doc) { + const typeNameSpan = document.createElement('span'); + typeNameSpan.innerHTML = typeName.slice(0, 1).toUpperCase() + typeName.slice(1) + '.'; - tooltipContainer.appendChild(header); - tooltipContainer.appendChild(document.createTextNode(fn.doc.description)); + const propNameSpan = document.createElement('span'); + propNameSpan.classList.add('autocomplete-info-name'); + propNameSpan.innerText = property.doc.name; - return tooltipContainer; - }; + const returnTypeSpan = document.createElement('span'); + returnTypeSpan.innerHTML = ': ' + property.doc.returnType; - return option; - }); + header.appendChild(typeNameSpan); + header.appendChild(propNameSpan); + header.appendChild(returnTypeSpan); + } + return header; }; const objectOptions = (toResolve: string, resolved: IDataObject) => { diff --git a/packages/editor-ui/src/plugins/i18n/index.ts b/packages/editor-ui/src/plugins/i18n/index.ts index d3b3f590826d4..524b8e9905e45 100644 --- a/packages/editor-ui/src/plugins/i18n/index.ts +++ b/packages/editor-ui/src/plugins/i18n/index.ts @@ -480,6 +480,10 @@ export class I18nClass { invalid: this.baseText('codeNodeEditor.completer.luxon.dateTimeStaticMethods.invalid'), isDateTime: this.baseText('codeNodeEditor.completer.luxon.dateTimeStaticMethods.isDateTime'), }; + + autocompleteUIValues: Record = { + docLinkLabel: this.baseText('expressionEdit.learnMore'), + }; } export const i18nInstance = new VueI18n({ diff --git a/packages/editor-ui/src/styles/autocomplete-theme.scss b/packages/editor-ui/src/styles/autocomplete-theme.scss new file mode 100644 index 0000000000000..489fb78bfa20b --- /dev/null +++ b/packages/editor-ui/src/styles/autocomplete-theme.scss @@ -0,0 +1,45 @@ +.cm-tooltip-autocomplete { + background-color: var(--color-background-xlight) !important; + + li .cm-completionLabel { + color: var(--color-success); + } +} + +.ͼ2 .cm-tooltip-autocomplete ul li[aria-selected] .cm-completionLabel { + color: var(--color-text-xlight) !important; + font-weight: 600; +} + +.autocomplete-info-container { + display: flex; + flex-direction: column; + padding: var(--spacing-4xs) 0; +} + +.cm-completionInfo { + background-color: var(--color-background-xlight) !important; + + .autocomplete-info-header { + color: var(--color-text-dark); + line-height: var(--font-line-height-compact); + margin-bottom: var(--spacing-2xs); + } + + .autocomplete-info-name { + color: var(--color-success); + font-weight: var(--font-weight-bold); + } + + .autocomplete-info-description { + code { + background-color: var(--color-background-base); + padding: 0 2px; + } + p { + line-height: var(--font-line-height-compact); + margin-top: 0; + margin-bottom: var(--spacing-4xs); + } + } +} diff --git a/packages/workflow/src/Extensions/Extensions.ts b/packages/workflow/src/Extensions/Extensions.ts index ca4d2ff011ede..a5ec7575c86de 100644 --- a/packages/workflow/src/Extensions/Extensions.ts +++ b/packages/workflow/src/Extensions/Extensions.ts @@ -4,10 +4,17 @@ export interface ExtensionMap { functions: Record; } +export type NativeDoc = { + typeName: string; + properties?: Record; + functions: Record; +}; + export type DocMetadata = { name: string; returnType: string; description?: string; aliases?: string[]; - args?: unknown[]; + args?: Array<{ name: string; type?: string }>; + docURL?: string; }; diff --git a/packages/workflow/src/NativeMethods/Array.methods.ts b/packages/workflow/src/NativeMethods/Array.methods.ts new file mode 100644 index 0000000000000..7b9017040844d --- /dev/null +++ b/packages/workflow/src/NativeMethods/Array.methods.ts @@ -0,0 +1,205 @@ +import type { NativeDoc } from '@/Extensions/Extensions'; + +export const arrayMethods: NativeDoc = { + typeName: 'Array', + properties: { + length: { + doc: { + name: 'length', + description: 'Returns the number of elements in the Array.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length', + returnType: 'number', + }, + }, + }, + functions: { + concat: { + doc: { + name: 'concat', + description: 'Merges two or more arrays into one array.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat', + returnType: 'Array', + args: [ + { name: 'arr1', type: 'Array' }, + { name: 'arr2', type: 'Array' }, + { name: '...' }, + { name: 'arrN', type: 'Array' }, + ], + }, + }, + filter: { + doc: { + name: 'filter', + description: 'Returns an array only containing the elements that pass the test `fn`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter', + returnType: 'Array', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + find: { + doc: { + name: 'find', + description: + 'Returns the first element in the provided array that passes the test `fn`. If no values satisfy the testing function, `undefined` is returned.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find', + returnType: 'Array|undefined', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + findIndex: { + doc: { + name: 'findIndex', + description: + 'Returns the index of the first element in an array that passes the test `fn`. If none are found, -1 is returned.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex', + returnType: 'number', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + findLast: { + doc: { + name: 'findLast', + description: 'Returns the value of the last element that passes the test `fn`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast', + returnType: 'Element|undefined', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + findLastIndex: { + doc: { + name: 'findLastIndex', + description: + 'Returns the index of the last element that satisfies the provided testing function. If none are found, -1 is returned.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex', + returnType: 'number', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + indexOf: { + doc: { + name: 'indexOf', + description: + 'Returns the first index at which a given element can be found in the array, or -1 if it is not present.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf', + returnType: 'number', + args: [ + { name: 'searchElement', type: 'string|number' }, + { name: 'fromIndex?', type: 'number' }, + ], + }, + }, + includes: { + doc: { + name: 'includes', + description: 'Checks if an array includes a certain value among its entries.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes', + returnType: 'boolean', + args: [ + { name: 'searchElement', type: 'Element' }, + { name: 'fromIndex?', type: 'number' }, + ], + }, + }, + join: { + doc: { + name: 'join', + description: + 'Returns a string that concatenates all of the elements in an array, separated by `separator`, which defaults to comma.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join', + returnType: 'Array', + args: [{ name: 'separator?', type: 'string' }], + }, + }, + map: { + doc: { + name: 'map', + description: 'Returns an array containing the results of calling `fn` on every element.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map', + returnType: 'Array', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + reverse: { + doc: { + name: 'reverse', + description: + 'Reverses an array and returns it. The first array element now becomes the last, and the last array element becomes the first.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse', + returnType: 'Array', + }, + }, + reduce: { + doc: { + name: 'reduce', + description: + 'Executes a "reducer" function `fn` on each element of the array. Passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce', + returnType: 'any', + args: [{ name: 'fn', type: 'Function' }], + }, + }, + slice: { + doc: { + name: 'slice', + description: + 'Returns a section of an Array. `end` defaults to the length of the Array if not given.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice', + returnType: 'Array', + args: [ + { name: 'start', type: 'number' }, + { name: 'end?', type: 'number' }, + ], + }, + }, + sort: { + doc: { + name: 'sort', + description: + 'Returns a sorted array. The default sort order is ascending, built upon converting the elements into strings.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort', + returnType: 'Array', + args: [{ name: 'fn?', type: 'Function' }], + }, + }, + splice: { + doc: { + name: 'splice', + description: 'Changes the contents of an array by removing or replacing existing elements.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice', + returnType: 'Array', + args: [ + { name: 'start', type: 'number' }, + { name: 'deleteCount?', type: 'number' }, + { name: 'item1?', type: 'Element' }, + { name: '...' }, + { name: 'itemN?', type: 'Element' }, + ], + }, + }, + toString: { + doc: { + name: 'toString', + description: 'Returns a string representing the specified array and its elements.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString', + returnType: 'string', + }, + }, + }, +}; diff --git a/packages/workflow/src/NativeMethods/Number.methods.ts b/packages/workflow/src/NativeMethods/Number.methods.ts new file mode 100644 index 0000000000000..f2c8d9884b06e --- /dev/null +++ b/packages/workflow/src/NativeMethods/Number.methods.ts @@ -0,0 +1,28 @@ +import type { NativeDoc } from '@/Extensions/Extensions'; + +export const numberMethods: NativeDoc = { + typeName: 'Number', + functions: { + toFixed: { + doc: { + name: 'toFixed', + description: + 'Formats a number using fixed-point notation. `digits` defaults to null if not given.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed', + returnType: 'string', + args: [{ name: 'digits?', type: 'number' }], + }, + }, + toPrecision: { + doc: { + name: 'toPrecision', + description: 'Returns a string representing the number to the specified precision.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision', + returnType: 'string', + args: [{ name: 'precision?', type: 'number' }], + }, + }, + }, +}; diff --git a/packages/workflow/src/NativeMethods/Object.Methods.ts b/packages/workflow/src/NativeMethods/Object.Methods.ts new file mode 100644 index 0000000000000..41739de8b159f --- /dev/null +++ b/packages/workflow/src/NativeMethods/Object.Methods.ts @@ -0,0 +1,27 @@ +import type { NativeDoc } from '@/Extensions/Extensions'; + +export const objectMethods: NativeDoc = { + typeName: 'Object', + functions: { + keys: { + doc: { + name: 'keys', + description: + "Returns an array of a given object's own enumerable string-keyed property names.", + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys', + returnType: 'Array', + }, + }, + values: { + doc: { + name: 'values', + description: + "Returns an array of a given object's own enumerable string-keyed property values.", + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values', + returnType: 'Array', + }, + }, + }, +}; diff --git a/packages/workflow/src/NativeMethods/String.methods.ts b/packages/workflow/src/NativeMethods/String.methods.ts index f8a9273774799..7eca69b3dde78 100644 --- a/packages/workflow/src/NativeMethods/String.methods.ts +++ b/packages/workflow/src/NativeMethods/String.methods.ts @@ -1,13 +1,223 @@ -export const stringMethods = { +import type { NativeDoc } from '@/Extensions/Extensions'; + +export const stringMethods: NativeDoc = { typeName: 'String', + properties: { + length: { + doc: { + name: 'length', + description: 'Returns the number of characters in the string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length', + returnType: 'number', + }, + }, + }, functions: { - // @TODO_NEXT_PHASE: Populate below and cover other datatypes - // trim: { - // doc: { - // name: 'trim', - // description: 'Removes whitespace from both ends of a string and returns a new string', - // returnType: 'string', - // }, - // }, + concat: { + doc: { + name: 'concat', + description: 'Concatenates the string arguments to the calling string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat', + returnType: 'string', + }, + }, + endsWith: { + doc: { + name: 'endsWith', + description: 'Checks if a string ends with `searchString`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith', + returnType: 'boolean', + args: [{ name: 'searchString', type: 'string' }], + }, + }, + indexOf: { + doc: { + name: 'indexOf', + description: 'Returns the index of the first occurrence of `searchString`', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf', + returnType: 'number', + args: [ + { name: 'searchString', type: 'string' }, + { name: 'position?', type: 'number' }, + ], + }, + }, + lastIndexOf: { + doc: { + name: 'lastIndexOf', + description: 'Returns the index of the last occurrence of `searchString`', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf', + returnType: 'number', + args: [ + { name: 'searchString', type: 'string' }, + { name: 'position?', type: 'number' }, + ], + }, + }, + match: { + doc: { + name: 'match', + description: 'Retrieves the result of matching a string against a regular expression.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match', + returnType: 'Array', + args: [{ name: 'regexp', type: 'string|RegExp' }], + }, + }, + includes: { + doc: { + name: 'includes', + description: 'Checks if `searchString` may be found within the calling string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes', + returnType: 'boolean', + args: [ + { name: 'searchString', type: 'string' }, + { name: 'position?', type: 'number' }, + ], + }, + }, + replace: { + doc: { + name: 'replace', + description: + 'Returns a string with matches of a `pattern` replaced by a `replacement`. If `pattern` is a string, only the first occurrence will be replaced.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace', + returnType: 'string', + args: [ + { name: 'pattern', type: 'string|RegExp' }, + { name: 'replacement', type: 'string' }, + ], + }, + }, + replaceAll: { + doc: { + name: 'replaceAll', + description: 'Returns a string with matches of a `pattern` replaced by a `replacement`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll', + returnType: 'string', + args: [ + { name: 'pattern', type: 'string|RegExp' }, + { name: 'replacement', type: 'string' }, + ], + }, + }, + search: { + doc: { + name: 'search', + description: 'Returns a string that matches `pattern` within the given string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search', + returnType: 'string', + args: [{ name: 'pattern', type: 'string|RegExp' }], + }, + }, + slice: { + doc: { + name: 'slice', + description: + 'Returns a section of a string. `indexEnd` defaults to the length of the string if not given.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice', + returnType: 'string', + args: [ + { name: 'indexStart', type: 'number' }, + { name: 'indexEnd?', type: 'number' }, + ], + }, + }, + split: { + doc: { + name: 'split', + description: + 'Returns the substrings that result from dividing the given string with `separator`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split', + returnType: 'Array', + args: [ + { name: 'separator', type: 'string|RegExp' }, + { name: 'limit?', type: 'number' }, + ], + }, + }, + startsWith: { + doc: { + name: 'startsWith', + description: 'Checks if the string begins with `searchString`.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith', + returnType: 'boolean', + args: [ + { name: 'searchString', type: 'string' }, + { name: 'position?', type: 'number' }, + ], + }, + }, + substring: { + doc: { + name: 'substring', + description: + 'Returns the part of the string from the start index up to and excluding the end index, or to the end of the string if no end index is supplied.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring', + returnType: 'string', + args: [ + { name: 'indexStart', type: 'number' }, + { name: 'indexEnd?', type: 'number' }, + ], + }, + }, + toLowerCase: { + doc: { + name: 'toLowerCase', + description: 'Formats a string to lowercase. Example: "this is lowercase”.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase', + returnType: 'string', + }, + }, + toUpperCase: { + doc: { + name: 'toUpperCase', + description: 'Formats a string to lowercase. Example: "THIS IS UPPERCASE”.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase', + returnType: 'string', + }, + }, + trim: { + doc: { + name: 'trim', + description: 'Removes whitespace from both ends of a string and returns a new string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim', + returnType: 'string', + }, + }, + trimEnd: { + doc: { + name: 'trimEnd', + description: 'Removes whitespace from the end of a string and returns a new string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd', + returnType: 'string', + }, + }, + trimStart: { + doc: { + name: 'trimStart', + description: 'Removes whitespace from the beginning of a string and returns a new string.', + docURL: + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart', + returnType: 'string', + }, + }, }, }; diff --git a/packages/workflow/src/NativeMethods/index.ts b/packages/workflow/src/NativeMethods/index.ts index dbc5785b52de1..9f96784ac5fe0 100644 --- a/packages/workflow/src/NativeMethods/index.ts +++ b/packages/workflow/src/NativeMethods/index.ts @@ -1,5 +1,9 @@ import { stringMethods } from './String.methods'; +import { arrayMethods } from './Array.methods'; +import { numberMethods } from './Number.methods'; +import { objectMethods } from './Object.Methods'; +import type { NativeDoc } from '@/Extensions/Extensions'; -const NATIVE_METHODS = [stringMethods]; +const NATIVE_METHODS: NativeDoc[] = [stringMethods, arrayMethods, numberMethods, objectMethods]; export { NATIVE_METHODS as NativeMethods };