Skip to content

Commit

Permalink
make it all work again
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy committed May 2, 2023
1 parent fe10294 commit cf8b461
Show file tree
Hide file tree
Showing 23 changed files with 528 additions and 963 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2420,10 +2420,10 @@ export function getExecuteFunctions(
if (additionalData.sendMessageToUI) {
args = args.map((arg) => {
// prevent invalid dates from being logged as null
if (arg?.isLuxonDateTime && arg?.invalidReason) return { ...arg };
if (arg.isLuxonDateTime && arg.invalidReason) return { ...arg };

// log valid dates in human readable format, as in browser
if (arg?.isLuxonDateTime) return new Date(arg.ts).toString();
if (arg.isLuxonDateTime) return new Date(arg.ts).toString();
if (arg instanceof Date) return arg.toString();

return arg;
Expand Down
74 changes: 36 additions & 38 deletions packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
:class="['code-node-editor', $style['code-node-editor-container']]"
:class="['code-node-editor', $style['code-node-editor-container'], language]"
@mouseover="onMouseOver"
@mouseout="onMouseOut"
ref="codeNodeEditorContainer"
Expand All @@ -23,13 +23,16 @@ import type { PropType } from 'vue';
import { mapStores } from 'pinia';
import mixins from 'vue-typed-mixins';
import type { LanguageSupport } from '@codemirror/language';
import type { Extension } from '@codemirror/state';
import { Compartment, EditorState, EditorStateConfig } from '@codemirror/state';
import { Compartment, EditorState } from '@codemirror/state';
import type { ViewUpdate } from '@codemirror/view';
import { EditorView } from '@codemirror/view';
import { javascript } from '@codemirror/lang-javascript';
import { json } from '@codemirror/lang-json';
import { python } from '@codemirror/lang-python';
import type { CodeExecutionMode, CodeNodeEditorLanguage } from 'n8n-workflow';
import { CODE_EXECUTION_MODES, CODE_LANGUAGES } from 'n8n-workflow';
import { workflowHelpers } from '@/mixins/workflowHelpers'; // for json field completions
import { ASK_AI_MODAL_KEY, CODE_NODE_TYPE } from '@/constants';
Expand All @@ -39,36 +42,23 @@ import { useSettingsStore } from '@/stores/settings';
import Modal from '@/components/Modal.vue';
import { readOnlyEditorExtensions, writableEditorExtensions } from './baseExtensions';
import {
ALL_ITEMS_PLACEHOLDER,
CODE_LANGUAGES,
CODE_MODES,
EACH_ITEM_PLACEHOLDER,
} from './constants';
import { CODE_PLACEHOLDERS } from './constants';
import { linterExtension } from './javaScript/linter';
import { completerExtension } from './javaScript/completer';
import { codeNodeEditorTheme } from './theme';
import type { CodeLanguage, CodeMode } from './types';
const placeholders: Partial<Record<CodeLanguage, Record<CodeMode, string>>> = {
javaScript: {
runOnceForAllItems: ALL_ITEMS_PLACEHOLDER,
runOnceForEachItem: EACH_ITEM_PLACEHOLDER,
},
};
export default mixins(linterExtension, completerExtension, workflowHelpers).extend({
name: 'code-node-editor',
components: { Modal },
props: {
mode: {
type: String as PropType<CodeMode>,
validator: (value: CodeMode): boolean => CODE_MODES.includes(value),
type: String as PropType<CodeExecutionMode>,
validator: (value: CodeExecutionMode): boolean => CODE_EXECUTION_MODES.includes(value),
},
language: {
type: String as PropType<CodeLanguage>,
default: 'javaScript' as CodeLanguage,
validator: (value: CodeLanguage): boolean => CODE_LANGUAGES.includes(value),
type: String as PropType<CodeNodeEditorLanguage>,
default: 'javaScript' as CodeNodeEditorLanguage,
validator: (value: CodeNodeEditorLanguage): boolean => CODE_LANGUAGES.includes(value),
},
isReadOnly: {
type: Boolean,
Expand All @@ -81,22 +71,29 @@ export default mixins(linterExtension, completerExtension, workflowHelpers).exte
data() {
return {
editor: null as EditorView | null,
languageCompartment: new Compartment(),
linterCompartment: new Compartment(),
isDefault: false,
isEditorHovered: false,
isEditorFocused: false,
};
},
watch: {
mode(newMode, previousMode: CodeMode) {
mode(newMode, previousMode: CodeExecutionMode) {
this.reloadLinter();
if (this.content.trim() === placeholders[this.language]?.[previousMode]) {
if (this.content.trim() === CODE_PLACEHOLDERS[this.language]?.[previousMode]) {
this.refreshPlaceholder();
}
},
language() {
this.refreshPlaceholder();
language(newLanguage, previousLanguage: CodeNodeEditorLanguage) {
if (this.content.trim() === CODE_PLACEHOLDERS[previousLanguage]?.[this.mode]) {
this.refreshPlaceholder();
}
const [languageSupport] = this.languageExtensions;
this.editor?.dispatch({
effects: this.languageCompartment.reconfigure(languageSupport),
});
},
},
computed: {
Expand All @@ -110,7 +107,17 @@ export default mixins(linterExtension, completerExtension, workflowHelpers).exte
return this.editor.state.doc.toString();
},
placeholder(): string {
return placeholders[this.language]?.[this.mode] ?? '';
return CODE_PLACEHOLDERS[this.language]?.[this.mode] ?? '';
},
languageExtensions(): [LanguageSupport, ...Extension[]] {
switch (this.language) {
case 'json':
return [json()];
case 'javaScript':
return [javascript(), this.autocompletionExtension()];
case 'python':
return [python()];
}
},
},
methods: {
Expand Down Expand Up @@ -241,17 +248,8 @@ export default mixins(linterExtension, completerExtension, workflowHelpers).exte
);
}
switch (language) {
case 'json':
extensions.push(json());
break;
case 'javaScript':
extensions.push(javascript(), this.autocompletionExtension());
break;
case 'python':
extensions.push(python());
break;
}
const [languageSupport, ...otherExtensions] = this.languageExtensions;
extensions.push(this.languageCompartment.of(languageSupport), ...otherExtensions);
const state = EditorState.create({
doc: this.value || this.placeholder,
Expand Down
43 changes: 24 additions & 19 deletions packages/editor-ui/src/components/CodeNodeEditor/constants.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
import { STICKY_NODE_TYPE } from '@/constants';
import type { Diagnostic } from '@codemirror/lint';
import type { CodeExecutionMode, CodeNodeEditorLanguage } from 'n8n-workflow';

export const NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION = [STICKY_NODE_TYPE];

export const DEFAULT_LINTER_SEVERITY: Diagnostic['severity'] = 'error';

export const DEFAULT_LINTER_DELAY_IN_MS = 300;

/**
* Length of the start of the script wrapper, used as offset for the linter to find a location in source text.
*/
export const OFFSET_FOR_SCRIPT_WRAPPER = 'module.exports = async function() {'.length;

export const ALL_ITEMS_PLACEHOLDER = `
// Loop over input items and add a new field
// called 'myNewField' to the JSON of each one
export const CODE_PLACEHOLDERS: Partial<
Record<CodeNodeEditorLanguage, Record<CodeExecutionMode, string>>
> = {
javaScript: {
runOnceForAllItems: `
// Loop over input items and add a new field called 'myNewField' to the JSON of each one
for (const item of $input.all()) {
item.json.myNewField = 1;
}
return $input.all();
`.trim();

export const EACH_ITEM_PLACEHOLDER = `
// Add a new field called 'myNewField' to the
// JSON of the item
return $input.all();`.trim(),
runOnceForEachItem: `
// Add a new field called 'myNewField' to the JSON of the item
$input.item.json.myNewField = 1;
return $input.item;
`.trim();

export const CODE_LANGUAGES = ['javaScript', 'json', 'python'] as const;
export const CODE_MODES = ['runOnceForAllItems', 'runOnceForEachItem'] as const;
return $input.item;`.trim(),
},
python: {
runOnceForAllItems: `
# Loop over input items and add a new field called 'myNewField' to the JSON of each one
for item in _input.all():
item.json.myNewField = 1
return _input.all()`.trim(),
runOnceForEachItem: `
# Add a new field called 'myNewField' to the JSON of the item
_input.item.json.myNewField = 1
return _input.item`.trim(),
},
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Vue from 'vue';
import { NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION } from '@/components/CodeNodeEditor//constants';
import { NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION } from '@/components/CodeNodeEditor/constants';
import { addVarType } from '@/components/CodeNodeEditor/utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { INodeUi } from '@/Interface';
import type { CodeNodeEditorMixin } from '@/components/CodeNodeEditor/types';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows';

function getAutocompletableNodeNames(nodes: INodeUi[]) {
function getAutoCompletableNodeNames(nodes: INodeUi[]) {
return nodes
.filter((node: INodeUi) => !NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION.includes(node.type))
.map((node: INodeUi) => node.name);
Expand Down Expand Up @@ -104,7 +104,7 @@ export const baseCompletions = (Vue as CodeNodeEditorMixin).extend({
const options: Completion[] = TOP_LEVEL_COMPLETIONS_IN_BOTH_MODES.map(addVarType);

options.push(
...getAutocompletableNodeNames(this.workflowsStore.allNodes).map((nodeName) => {
...getAutoCompletableNodeNames(this.workflowsStore.allNodes).map((nodeName) => {
return {
label: `$('${nodeName}')`,
type: 'variable',
Expand Down Expand Up @@ -142,7 +142,7 @@ export const baseCompletions = (Vue as CodeNodeEditorMixin).extend({

if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;

const options: Completion[] = getAutocompletableNodeNames(this.workflowsStore.allNodes).map(
const options: Completion[] = getAutoCompletableNodeNames(this.workflowsStore.allNodes).map(
(nodeName) => {
return {
label: `$('${nodeName}')`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { addVarType } from '../utils';
import { addVarType } from '@/components/CodeNodeEditor/utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import type { CodeNodeEditorMixin } from '@/components/CodeNodeEditor/types';
import { useEnvironmentsStore } from '@/stores';

const escape = (str: string) => str.replace('$', '\\$');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import { jsonParseLinter } from '@codemirror/lang-json';
import type { EditorView } from '@codemirror/view';
import * as esprima from 'esprima-next';
import type { Node } from 'estree';
import type { CodeNodeEditorLanguage } from 'n8n-workflow';

import {
DEFAULT_LINTER_DELAY_IN_MS,
DEFAULT_LINTER_SEVERITY,
} from '@/components/CodeNodeEditor/constants';
import { DEFAULT_LINTER_DELAY_IN_MS, DEFAULT_LINTER_SEVERITY } from '../constants';
import { OFFSET_FOR_SCRIPT_WRAPPER } from './constants';
import { walk } from '../../../utils';

import type { CodeLanguage, CodeNodeEditorMixin, RangeNode } from '../types';
import { walk } from '../utils';
import type { CodeNodeEditorMixin, RangeNode } from '../types';

export const linterExtension = (Vue as CodeNodeEditorMixin).extend({
methods: {
createLinter(language: CodeLanguage) {
createLinter(language: CodeNodeEditorLanguage) {
switch (language) {
case 'javaScript':
return createLinter(this.lintSource, { delay: DEFAULT_LINTER_DELAY_IN_MS });
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/components/CodeNodeEditor/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { tags } from '@lezer/highlight';
const BASE_STYLING = {
fontSize: '0.8em',
fontFamily: "Menlo, Consolas, 'DejaVu Sans Mono', monospace !important",
maxHeight: '400px',
tooltip: {
maxWidth: '300px',
lineHeight: '1.3em',
Expand Down Expand Up @@ -70,6 +71,7 @@ export const codeNodeEditorTheme = ({ isReadOnly }: ThemeSettings) => [
? 'var(--color-code-background-readonly)'
: 'var(--color-code-gutterBackground)',
color: 'var(--color-code-gutterForeground)',
borderRadius: 'var(--border-radius-base)',
},
'.cm-tooltip': {
maxWidth: BASE_STYLING.tooltip.maxWidth,
Expand Down
8 changes: 2 additions & 6 deletions packages/editor-ui/src/components/CodeNodeEditor/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import type { EditorView } from '@codemirror/view';
import type { I18nClass } from '@/plugins/i18n';
import type { Workflow } from 'n8n-workflow';
import type { Workflow, CodeExecutionMode } from 'n8n-workflow';
import type { Node } from 'estree';
import type { CODE_LANGUAGES, CODE_MODES } from './constants';

export type CodeNodeEditorMixin = Vue.VueConstructor<
Vue & {
$locale: I18nClass;
editor: EditorView | null;
mode: 'runOnceForAllItems' | 'runOnceForEachItem';
mode: CodeExecutionMode;
getCurrentWorkflow(): Workflow;
}
>;

export type RangeNode = Node & { range: [number, number] };

export type CodeLanguage = (typeof CODE_LANGUAGES)[number];
export type CodeMode = (typeof CODE_MODES)[number];
3 changes: 2 additions & 1 deletion packages/editor-ui/src/components/ParameterInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ import type {
INodePropertyCollection,
NodeParameterValueType,
EditorType,
CodeNodeMode,
CodeNodeEditorLanguage,
} from 'n8n-workflow';
import { NodeHelpers } from 'n8n-workflow';
Expand Down Expand Up @@ -744,7 +745,7 @@ export default mixins(
},
editorLanguage(): CodeNodeEditorLanguage {
if (this.editorType === 'json' || this.parameter.type === 'json') return 'json';
return 'javaScript';
return (this.getArgument('editorLanguage') as CodeNodeEditorLanguage) ?? 'javaScript';
},
parameterOptions():
| Array<INodePropertyOptions | INodeProperties | INodePropertyCollection>
Expand Down
1 change: 0 additions & 1 deletion packages/editor-ui/src/components/RunData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,6 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers, pinData).exten
.dataContainer {
position: relative;
height: 100%;
overflow-y: auto;
&:hover {
.actions-group {
Expand Down
Loading

0 comments on commit cf8b461

Please sign in to comment.