mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
fix(editor): Fix code node displays lint messages in wrong location (#13664)
This commit is contained in:
@@ -94,11 +94,11 @@
|
||||
"xss": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@n8n/eslint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@pinia/testing": "^0.1.6",
|
||||
"@types/dateformat": "^3.0.0",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
@@ -111,6 +111,7 @@
|
||||
"@vitejs/plugin-vue": "catalog:frontend",
|
||||
"@vitest/coverage-v8": "catalog:frontend",
|
||||
"browserslist-to-esbuild": "^2.1.1",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"miragejs": "^0.1.48",
|
||||
"unplugin-icons": "^0.19.0",
|
||||
"unplugin-vue-components": "^0.27.2",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import 'fake-indexeddb/auto';
|
||||
import { configure } from '@testing-library/vue';
|
||||
import 'core-js/proposals/set-methods-v2';
|
||||
|
||||
@@ -64,20 +65,21 @@ Object.defineProperty(window, 'matchMedia', {
|
||||
});
|
||||
|
||||
class Worker {
|
||||
onmessage: (message: string) => void;
|
||||
onmessage = vi.fn();
|
||||
|
||||
url: string;
|
||||
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
this.onmessage = () => {};
|
||||
}
|
||||
|
||||
postMessage(message: string) {
|
||||
postMessage = vi.fn((message: string) => {
|
||||
this.onmessage(message);
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener() {}
|
||||
addEventListener = vi.fn();
|
||||
|
||||
terminate = vi.fn();
|
||||
}
|
||||
|
||||
Object.defineProperty(window, 'Worker', {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Text, type Extension } from '@codemirror/state';
|
||||
import { EditorView, hoverTooltip } from '@codemirror/view';
|
||||
import * as Comlink from 'comlink';
|
||||
import { NodeConnectionType, type CodeExecutionMode, type INodeExecutionData } from 'n8n-workflow';
|
||||
import { ref, toRef, toValue, watch, type MaybeRefOrGetter } from 'vue';
|
||||
import { onBeforeUnmount, ref, toRef, toValue, watch, type MaybeRefOrGetter } from 'vue';
|
||||
import type { LanguageServiceWorker, RemoteLanguageServiceWorkerInit } from '../types';
|
||||
import { typescriptCompletionSource } from './completions';
|
||||
import { typescriptWorkerFacet } from './facet';
|
||||
@@ -33,11 +33,13 @@ export function useTypescript(
|
||||
const { debounce } = useDebounce();
|
||||
const activeNodeName = ndvStore.activeNodeName;
|
||||
const worker = ref<Comlink.Remote<LanguageServiceWorker>>();
|
||||
const webWorker = ref<Worker>();
|
||||
|
||||
async function createWorker(): Promise<Extension> {
|
||||
const { init } = Comlink.wrap<RemoteLanguageServiceWorkerInit>(
|
||||
new Worker(new URL('../worker/typescript.worker.ts', import.meta.url), { type: 'module' }),
|
||||
);
|
||||
webWorker.value = new Worker(new URL('../worker/typescript.worker.ts', import.meta.url), {
|
||||
type: 'module',
|
||||
});
|
||||
const { init } = Comlink.wrap<RemoteLanguageServiceWorkerInit>(webWorker.value);
|
||||
worker.value = await init(
|
||||
{
|
||||
id: toValue(id),
|
||||
@@ -125,6 +127,10 @@ export function useTypescript(
|
||||
forceParse(editor);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (webWorker.value) webWorker.value.terminate();
|
||||
});
|
||||
|
||||
return {
|
||||
createWorker,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { WorkerInitOptions } from '../types';
|
||||
import { worker } from './typescript.worker';
|
||||
import { type ChangeSet, EditorState } from '@codemirror/state';
|
||||
|
||||
async function createWorker({
|
||||
doc,
|
||||
options,
|
||||
}: { doc?: string; options?: Partial<WorkerInitOptions> } = {}) {
|
||||
const defaultDoc = `
|
||||
function myFunction(){
|
||||
if (true){
|
||||
const myObj = {test: "value"}
|
||||
}
|
||||
}
|
||||
|
||||
return $input.all();`;
|
||||
const state = EditorState.create({ doc: doc ?? defaultDoc });
|
||||
|
||||
const tsWorker = worker.init(
|
||||
{
|
||||
allNodeNames: [],
|
||||
content: state.doc.toJSON(),
|
||||
id: 'id',
|
||||
inputNodeNames: [],
|
||||
mode: 'runOnceForAllItems',
|
||||
variables: [],
|
||||
...options,
|
||||
},
|
||||
async () => ({
|
||||
json: { path: '', type: 'string', value: '' },
|
||||
binary: [],
|
||||
params: { path: '', type: 'string', value: '' },
|
||||
}),
|
||||
);
|
||||
return await tsWorker;
|
||||
}
|
||||
|
||||
describe('Typescript Worker', () => {
|
||||
it('should return diagnostics', async () => {
|
||||
const tsWorker = await createWorker();
|
||||
|
||||
expect(tsWorker.getDiagnostics()).toEqual([
|
||||
{
|
||||
from: 10,
|
||||
markClass: 'cm-faded',
|
||||
message: "'myFunction' is declared but its value is never read.",
|
||||
severity: 'warning',
|
||||
to: 20,
|
||||
},
|
||||
{
|
||||
from: 47,
|
||||
markClass: 'cm-faded',
|
||||
message: "'myObj' is declared but its value is never read.",
|
||||
severity: 'warning',
|
||||
to: 52,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should accept updates from the client and buffer them', async () => {
|
||||
const tsWorker = await createWorker();
|
||||
// Add if statement and remove indentation
|
||||
const changes = [
|
||||
[75, [0, '', ''], 22],
|
||||
[76, [0, '', ''], 22],
|
||||
[77, [0, ' if (true){', ' const myObj = {test: "value"}', ' }'], 22],
|
||||
[77, [1], 13, [2], 30, [2], 23],
|
||||
];
|
||||
|
||||
vi.useFakeTimers({ toFake: ['setTimeout', 'queueMicrotask', 'nextTick'] });
|
||||
|
||||
for (const change of changes) {
|
||||
tsWorker.updateFile(change as unknown as ChangeSet);
|
||||
}
|
||||
|
||||
expect(tsWorker.getDiagnostics()).toHaveLength(2);
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
vi.runAllTicks();
|
||||
|
||||
expect(tsWorker.getDiagnostics()).toHaveLength(3);
|
||||
expect(tsWorker.getDiagnostics()).toEqual([
|
||||
{
|
||||
from: 10,
|
||||
markClass: 'cm-faded',
|
||||
message: "'myFunction' is declared but its value is never read.",
|
||||
severity: 'warning',
|
||||
to: 20,
|
||||
},
|
||||
{
|
||||
from: 47,
|
||||
markClass: 'cm-faded',
|
||||
message: "'myObj' is declared but its value is never read.",
|
||||
severity: 'warning',
|
||||
to: 52,
|
||||
},
|
||||
{
|
||||
from: 96,
|
||||
markClass: 'cm-faded',
|
||||
message: "'myObj' is declared but its value is never read.",
|
||||
severity: 'warning',
|
||||
to: 101,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return completions', async () => {
|
||||
const doc = 'return $input.';
|
||||
const tsWorker = await createWorker({ doc });
|
||||
|
||||
const completionResult = await tsWorker.getCompletionsAtPos(doc.length);
|
||||
assert(completionResult !== null);
|
||||
|
||||
const completionLabels = completionResult.result.options.map((c) => c.label);
|
||||
expect(completionLabels).toContain('all()');
|
||||
expect(completionLabels).toContain('first()');
|
||||
});
|
||||
});
|
||||
@@ -28,7 +28,7 @@ import { until } from '@vueuse/core';
|
||||
|
||||
self.process = { env: {} } as NodeJS.Process;
|
||||
|
||||
const worker: LanguageServiceWorkerInit = {
|
||||
export const worker: LanguageServiceWorkerInit = {
|
||||
async init(options, nodeDataFetcher) {
|
||||
const loadedNodeTypesMap: Map<string, { type: string; typeName: string }> = reactive(new Map());
|
||||
|
||||
@@ -157,11 +157,11 @@ const worker: LanguageServiceWorkerInit = {
|
||||
});
|
||||
|
||||
const applyChangesToCode = bufferChangeSets((bufferedChanges) => {
|
||||
bufferedChanges.iterChanges((start, end, _fromNew, _toNew, text) => {
|
||||
bufferedChanges.iterChanges((start, end, fromNew, _toNew, text) => {
|
||||
const length = end - start;
|
||||
|
||||
env.updateFile(codeFileName, text.toString(), {
|
||||
start: editorPositionToTypescript(start),
|
||||
start: editorPositionToTypescript(fromNew),
|
||||
length,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user