mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix: Extension being too eager and making calls when it shouldn't (#5232)
fix: extension being too eager and making calls when it shouldn't
This commit is contained in:
parent
832fb87954
commit
09bdd96d29
|
@ -74,28 +74,6 @@ export const hasNativeMethod = (method: string): boolean => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* recast's types aren't great and we need to use a lot of anys
|
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const findParent = <T>(path: T, matcher: (path: T) => boolean): T | undefined => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
let parent = path.parentPath;
|
|
||||||
while (parent) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
||||||
if (matcher(parent)) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
parent = parent.parentPath;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function to inject an extender function call into the AST of an expression.
|
* A function to inject an extender function call into the AST of an expression.
|
||||||
* This uses recast to do the transform.
|
* This uses recast to do the transform.
|
||||||
|
@ -115,66 +93,53 @@ export const extendTransform = (expression: string): { code: string } | undefine
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
visit(ast, {
|
visit(ast, {
|
||||||
visitIdentifier(path) {
|
visitCallExpression(path) {
|
||||||
this.traverse(path);
|
this.traverse(path);
|
||||||
if (path.node.name === '$if') {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const callPath: any = findParent(path, (p) => p.value?.type === 'CallExpression');
|
|
||||||
if (!callPath || callPath.value?.type !== 'CallExpression') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callPath.node.arguments.length < 2) {
|
if (
|
||||||
|
path.node.callee.type === 'MemberExpression' &&
|
||||||
|
path.node.callee.property.type === 'Identifier' &&
|
||||||
|
isExpressionExtension(path.node.callee.property.name)
|
||||||
|
) {
|
||||||
|
path.replace(
|
||||||
|
types.builders.callExpression(types.builders.identifier(EXPRESSION_EXTENDER), [
|
||||||
|
path.node.callee.object,
|
||||||
|
types.builders.stringLiteral(path.node.callee.property.name),
|
||||||
|
types.builders.arrayExpression(path.node.arguments),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
path.node.callee.type === 'Identifier' &&
|
||||||
|
path.node.callee.name === '$if' &&
|
||||||
|
path.node.arguments.every((v) => v.type !== 'SpreadElement')
|
||||||
|
) {
|
||||||
|
if (path.node.arguments.length < 2) {
|
||||||
throw new ExpressionExtensionError(
|
throw new ExpressionExtensionError(
|
||||||
'$if requires at least 2 parameters: test, value_if_true[, and value_if_false]',
|
'$if requires at least 2 parameters: test, value_if_true[, and value_if_false]',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
const test = path.node.arguments[0];
|
||||||
const test = callPath.node.arguments[0];
|
const consequent = path.node.arguments[1];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
const consequent = callPath.node.arguments[1];
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
const alternative =
|
const alternative =
|
||||||
callPath.node.arguments[2] === undefined
|
path.node.arguments[2] === undefined
|
||||||
? types.builders.booleanLiteral(false)
|
? types.builders.booleanLiteral(false)
|
||||||
: callPath.node.arguments[2];
|
: path.node.arguments[2];
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument
|
path.replace(
|
||||||
callPath.replace(types.builders.conditionalExpression(test, consequent, alternative));
|
types.builders.conditionalExpression(
|
||||||
}
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||||
},
|
test as any,
|
||||||
});
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||||
|
consequent as any,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||||
visit(ast, {
|
alternative as any,
|
||||||
visitIdentifier(path) {
|
),
|
||||||
this.traverse(path);
|
|
||||||
if (
|
|
||||||
isExpressionExtension(path.node.name) &&
|
|
||||||
// types.namedTypes.MemberExpression.check(path.parent)
|
|
||||||
path.parentPath?.value?.type === 'MemberExpression'
|
|
||||||
) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const callPath: any = findParent(path, (p) => p.value?.type === 'CallExpression');
|
|
||||||
|
|
||||||
if (!callPath || callPath.value?.type !== 'CallExpression') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
||||||
callPath.replace(
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
||||||
types.builders.callExpression(types.builders.identifier(EXPRESSION_EXTENDER), [
|
|
||||||
path.parentPath.value.object,
|
|
||||||
types.builders.stringLiteral(path.node.name),
|
|
||||||
// eslint-disable-next-line
|
|
||||||
types.builders.arrayExpression(callPath.node.arguments),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument
|
||||||
return print(ast);
|
return print(ast);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -98,6 +98,16 @@ describe('tmpl Expression Parser', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Edge cases', () => {
|
||||||
|
test("Nested member access with name of function inside a function doesn't result in function call", () => {
|
||||||
|
expect(evaluate('={{ Math.floor([1, 2, 3, 4].length + 10) }}')).toEqual(14);
|
||||||
|
|
||||||
|
expect(extendTransform('Math.floor([1, 2, 3, 4].length + 10)')?.code).toBe(
|
||||||
|
'extend(Math, "floor", [[1, 2, 3, 4].length + 10])',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Non dot extensions', () => {
|
describe('Non dot extensions', () => {
|
||||||
test('min', () => {
|
test('min', () => {
|
||||||
expect(evaluate('={{ min(1, 2, 3, 4, 5, 6) }}')).toEqual(1);
|
expect(evaluate('={{ min(1, 2, 3, 4, 5, 6) }}')).toEqual(1);
|
||||||
|
|
Loading…
Reference in a new issue