mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
perf(editor): Add lint rules for optimization-friendly syntax (#11681)
This commit is contained in:
parent
7b20c1e93d
commit
88295c7049
|
@ -178,9 +178,9 @@ export const useLinter = (
|
|||
message: i18n.baseText('codeNodeEditor.linter.allItems.unavailableProperty'),
|
||||
actions: [
|
||||
{
|
||||
name: 'Remove',
|
||||
name: 'Fix',
|
||||
apply(view) {
|
||||
view.dispatch({ changes: { from: start - '.'.length, to: end } });
|
||||
view.dispatch({ changes: { from: start, to: end, insert: 'first()' } });
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -559,6 +559,76 @@ export const useLinter = (
|
|||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Lint for `$(variable)` usage where variable is not a string, in both modes.
|
||||
*
|
||||
* $(nodeName) -> <no autofix>
|
||||
*/
|
||||
const isDollarSignWithVariable = (node: Node) =>
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === '$' &&
|
||||
node.arguments.length === 1 &&
|
||||
((node.arguments[0].type !== 'Literal' && node.arguments[0].type !== 'TemplateLiteral') ||
|
||||
(node.arguments[0].type === 'TemplateLiteral' && node.arguments[0].expressions.length > 0));
|
||||
|
||||
type TargetCallNode = RangeNode & {
|
||||
callee: { name: string };
|
||||
arguments: Array<{ type: string }>;
|
||||
};
|
||||
|
||||
walk<TargetCallNode>(ast, isDollarSignWithVariable).forEach((node) => {
|
||||
const [start, end] = getRange(node);
|
||||
|
||||
lintings.push({
|
||||
from: start,
|
||||
to: end,
|
||||
severity: 'warning',
|
||||
message: i18n.baseText('codeNodeEditor.linter.bothModes.dollarSignVariable'),
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Lint for $("myNode").item access in runOnceForAllItems mode
|
||||
*
|
||||
* $("myNode").item -> $("myNode").first()
|
||||
*/
|
||||
if (toValue(mode) === 'runOnceForEachItem') {
|
||||
type DollarItemNode = RangeNode & {
|
||||
property: { name: string; type: string } & RangeNode;
|
||||
};
|
||||
|
||||
const isDollarNodeItemAccess = (node: Node) =>
|
||||
node.type === 'MemberExpression' &&
|
||||
!node.computed &&
|
||||
node.object.type === 'CallExpression' &&
|
||||
node.object.callee.type === 'Identifier' &&
|
||||
node.object.callee.name === '$' &&
|
||||
node.object.arguments.length === 1 &&
|
||||
node.object.arguments[0].type === 'Literal' &&
|
||||
node.property.type === 'Identifier' &&
|
||||
node.property.name === 'item';
|
||||
|
||||
walk<DollarItemNode>(ast, isDollarNodeItemAccess).forEach((node) => {
|
||||
const [start, end] = getRange(node.property);
|
||||
|
||||
lintings.push({
|
||||
from: start,
|
||||
to: end,
|
||||
severity: 'warning',
|
||||
message: i18n.baseText('codeNodeEditor.linter.eachItem.preferFirst'),
|
||||
actions: [
|
||||
{
|
||||
name: 'Fix',
|
||||
apply(view) {
|
||||
view.dispatch({ changes: { from: start, to: end, insert: 'first()' } });
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return lintings;
|
||||
}
|
||||
|
||||
|
|
|
@ -447,7 +447,7 @@
|
|||
"codeNodeEditor.linter.allItems.itemCall": "`item` is a property to access, not a method to call. Did you mean `.item` without brackets?",
|
||||
"codeNodeEditor.linter.allItems.itemMatchingNoArg": "`.itemMatching()` expects an item index to be passed in as its argument.",
|
||||
"codeNodeEditor.linter.allItems.unavailableItem": "Legacy `item` is only available in the 'Run Once for Each Item' mode.",
|
||||
"codeNodeEditor.linter.allItems.unavailableProperty": "`.item` is only available in the 'Run Once for Each Item' mode.",
|
||||
"codeNodeEditor.linter.allItems.unavailableProperty": "`.item` is only available in the 'Run Once for Each Item' mode. Use `.first()` instead.",
|
||||
"codeNodeEditor.linter.allItems.unavailableVar": "is only available in the 'Run Once for Each Item' mode.",
|
||||
"codeNodeEditor.linter.bothModes.directAccess.firstOrLastCall": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
|
||||
"codeNodeEditor.linter.bothModes.directAccess.itemProperty": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
|
||||
|
@ -458,7 +458,9 @@
|
|||
"codeNodeEditor.linter.eachItem.returnArray": "Code doesn't return an object. Array found instead. Please return an object representing the output item",
|
||||
"codeNodeEditor.linter.eachItem.unavailableItems": "Legacy `items` is only available in the 'Run Once for All Items' mode.",
|
||||
"codeNodeEditor.linter.eachItem.unavailableMethod": "Method `$input.{method}()` is only available in the 'Run Once for All Items' mode.",
|
||||
"codeNodeEditor.linter.eachItem.preferFirst": "Prefer `.first()` over `.item` so n8n can optimize execution",
|
||||
"codeNodeEditor.linter.bothModes.syntaxError": "Syntax error",
|
||||
"codeNodeEditor.linter.bothModes.dollarSignVariable": "Use a string literal instead of a variable so n8n can optimize execution.",
|
||||
"codeNodeEditor.askAi.placeholder": "Tell AI what you want the code to achieve. You can reference input data fields using dot notation (e.g. user.email)",
|
||||
"codeNodeEditor.askAi.intro": "Hey AI, generate JavaScript code that...",
|
||||
"codeNodeEditor.askAi.help": "Help",
|
||||
|
|
Loading…
Reference in a new issue