@n8n/eslint-plugin-community-nodes 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/LICENSE.md +88 -0
  3. package/LICENSE_EE.md +27 -0
  4. package/dist/plugin.d.ts +61 -0
  5. package/dist/plugin.d.ts.map +1 -0
  6. package/dist/plugin.js +51 -0
  7. package/dist/plugin.js.map +1 -0
  8. package/dist/rules/credential-password-field.d.ts +3 -0
  9. package/dist/rules/credential-password-field.d.ts.map +1 -0
  10. package/dist/rules/credential-password-field.js +97 -0
  11. package/dist/rules/credential-password-field.js.map +1 -0
  12. package/dist/rules/credential-test-required.d.ts +3 -0
  13. package/dist/rules/credential-test-required.d.ts.map +1 -0
  14. package/dist/rules/credential-test-required.js +79 -0
  15. package/dist/rules/credential-test-required.js.map +1 -0
  16. package/dist/rules/icon-validation.d.ts +3 -0
  17. package/dist/rules/icon-validation.d.ts.map +1 -0
  18. package/dist/rules/icon-validation.js +131 -0
  19. package/dist/rules/icon-validation.js.map +1 -0
  20. package/dist/rules/index.d.ts +13 -0
  21. package/dist/rules/index.d.ts.map +1 -0
  22. package/dist/rules/index.js +23 -0
  23. package/dist/rules/index.js.map +1 -0
  24. package/dist/rules/no-credential-reuse.d.ts +3 -0
  25. package/dist/rules/no-credential-reuse.d.ts.map +1 -0
  26. package/dist/rules/no-credential-reuse.js +62 -0
  27. package/dist/rules/no-credential-reuse.js.map +1 -0
  28. package/dist/rules/no-deprecated-workflow-functions.d.ts +3 -0
  29. package/dist/rules/no-deprecated-workflow-functions.d.ts.map +1 -0
  30. package/dist/rules/no-deprecated-workflow-functions.js +144 -0
  31. package/dist/rules/no-deprecated-workflow-functions.js.map +1 -0
  32. package/dist/rules/no-restricted-globals.d.ts +3 -0
  33. package/dist/rules/no-restricted-globals.d.ts.map +1 -0
  34. package/dist/rules/no-restricted-globals.js +58 -0
  35. package/dist/rules/no-restricted-globals.js.map +1 -0
  36. package/dist/rules/no-restricted-imports.d.ts +3 -0
  37. package/dist/rules/no-restricted-imports.d.ts.map +1 -0
  38. package/dist/rules/no-restricted-imports.js +80 -0
  39. package/dist/rules/no-restricted-imports.js.map +1 -0
  40. package/dist/rules/node-usable-as-tool.d.ts +3 -0
  41. package/dist/rules/node-usable-as-tool.d.ts.map +1 -0
  42. package/dist/rules/node-usable-as-tool.js +57 -0
  43. package/dist/rules/node-usable-as-tool.js.map +1 -0
  44. package/dist/rules/package-name-convention.d.ts +3 -0
  45. package/dist/rules/package-name-convention.d.ts.map +1 -0
  46. package/dist/rules/package-name-convention.js +52 -0
  47. package/dist/rules/package-name-convention.js.map +1 -0
  48. package/dist/rules/resource-operation-pattern.d.ts +3 -0
  49. package/dist/rules/resource-operation-pattern.d.ts.map +1 -0
  50. package/dist/rules/resource-operation-pattern.js +77 -0
  51. package/dist/rules/resource-operation-pattern.js.map +1 -0
  52. package/dist/utils/ast-utils.d.ts +25 -0
  53. package/dist/utils/ast-utils.d.ts.map +1 -0
  54. package/dist/utils/ast-utils.js +117 -0
  55. package/dist/utils/ast-utils.js.map +1 -0
  56. package/dist/utils/file-utils.d.ts +12 -0
  57. package/dist/utils/file-utils.d.ts.map +1 -0
  58. package/dist/utils/file-utils.js +154 -0
  59. package/dist/utils/file-utils.js.map +1 -0
  60. package/dist/utils/index.d.ts +3 -0
  61. package/dist/utils/index.d.ts.map +1 -0
  62. package/dist/utils/index.js +3 -0
  63. package/dist/utils/index.js.map +1 -0
  64. package/package.json +46 -0
  65. package/src/plugin.ts +55 -0
  66. package/src/rules/credential-password-field.test.ts +231 -0
  67. package/src/rules/credential-password-field.ts +123 -0
  68. package/src/rules/credential-test-required.test.ts +147 -0
  69. package/src/rules/credential-test-required.ts +99 -0
  70. package/src/rules/icon-validation.test.ts +196 -0
  71. package/src/rules/icon-validation.ts +158 -0
  72. package/src/rules/index.ts +24 -0
  73. package/src/rules/no-credential-reuse.test.ts +226 -0
  74. package/src/rules/no-credential-reuse.ts +81 -0
  75. package/src/rules/no-deprecated-workflow-functions.test.ts +117 -0
  76. package/src/rules/no-deprecated-workflow-functions.ts +166 -0
  77. package/src/rules/no-restricted-globals.test.ts +135 -0
  78. package/src/rules/no-restricted-globals.ts +71 -0
  79. package/src/rules/no-restricted-imports.test.ts +181 -0
  80. package/src/rules/no-restricted-imports.ts +86 -0
  81. package/src/rules/node-usable-as-tool.test.ts +80 -0
  82. package/src/rules/node-usable-as-tool.ts +70 -0
  83. package/src/rules/package-name-convention.test.ts +112 -0
  84. package/src/rules/package-name-convention.ts +63 -0
  85. package/src/rules/resource-operation-pattern.test.ts +216 -0
  86. package/src/rules/resource-operation-pattern.ts +97 -0
  87. package/src/utils/ast-utils.ts +179 -0
  88. package/src/utils/file-utils.ts +204 -0
  89. package/src/utils/index.ts +2 -0
  90. package/tsconfig.json +11 -0
  91. package/tsconfig.tsbuildinfo +1 -0
  92. package/vite.config.ts +4 -0
@@ -0,0 +1,117 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { NoDeprecatedWorkflowFunctionsRule } from './no-deprecated-workflow-functions.js';
3
+
4
+ const ruleTester = new RuleTester();
5
+
6
+ ruleTester.run('no-deprecated-workflow-functions', NoDeprecatedWorkflowFunctionsRule, {
7
+ valid: [
8
+ {
9
+ name: 'using recommended functions and types',
10
+ code: `
11
+ import { IHttpRequestOptions } from 'n8n-workflow';
12
+
13
+ const requestOptions: IHttpRequestOptions = {
14
+ method: 'GET',
15
+ url: 'https://example.com',
16
+ };
17
+
18
+ const response1 = await this.helpers.httpRequest(requestOptions);
19
+ const response2 = await this.helpers.httpRequestWithAuthentication.call(this, 'oAuth2Api', {
20
+ method: 'POST',
21
+ url: 'https://api.example.com/data',
22
+ });`,
23
+ },
24
+ {
25
+ name: 'functions with similar names should not trigger',
26
+ code: `
27
+ import { request } from 'axios';
28
+
29
+ const result = await this.helpers.requestSomething();
30
+ const response = await request('https://api.example.com');
31
+ const config = { request: 'some value' };
32
+
33
+ // Other objects with helpers property should not trigger
34
+ const otherObject = {
35
+ helpers: {
36
+ request: () => 'not n8n',
37
+ requestWithAuthentication: () => 'not n8n'
38
+ }
39
+ };
40
+ const result2 = otherObject.helpers.request();`,
41
+ },
42
+ {
43
+ name: 'types with same name from other modules should not trigger',
44
+ code: `
45
+ import { IRequestOptions } from 'some-other-package';
46
+
47
+ function test(options: IRequestOptions) {
48
+ return options.url;
49
+ }`,
50
+ },
51
+ ],
52
+ invalid: [
53
+ {
54
+ name: 'deprecated request functions',
55
+ code: `
56
+ const response1 = await this.helpers.request('https://example.com/1');
57
+ const response2 = await this.helpers.requestWithAuthentication.call(this, 'oauth', options);
58
+ const response3 = await this.helpers.requestOAuth2.call(this, 'google', options);`,
59
+ errors: [
60
+ {
61
+ messageId: 'deprecatedRequestFunction',
62
+ data: { functionName: 'request', replacement: 'httpRequest' },
63
+ },
64
+ {
65
+ messageId: 'deprecatedRequestFunction',
66
+ data: {
67
+ functionName: 'requestWithAuthentication',
68
+ replacement: 'httpRequestWithAuthentication',
69
+ },
70
+ },
71
+ {
72
+ messageId: 'deprecatedRequestFunction',
73
+ data: { functionName: 'requestOAuth2', replacement: 'httpRequestWithAuthentication' },
74
+ },
75
+ ],
76
+ },
77
+ {
78
+ name: 'deprecated types',
79
+ code: `
80
+ import { IRequestOptions } from 'n8n-workflow';
81
+
82
+ function makeRequest(options: IRequestOptions): Promise<any> {
83
+ return this.helpers.request(options);
84
+ }`,
85
+ errors: [
86
+ {
87
+ messageId: 'deprecatedType',
88
+ data: { typeName: 'IRequestOptions', replacement: 'IHttpRequestOptions' },
89
+ },
90
+ {
91
+ messageId: 'deprecatedType',
92
+ data: { typeName: 'IRequestOptions', replacement: 'IHttpRequestOptions' },
93
+ },
94
+ {
95
+ messageId: 'deprecatedRequestFunction',
96
+ data: { functionName: 'request', replacement: 'httpRequest' },
97
+ },
98
+ ],
99
+ },
100
+ {
101
+ name: 'functions without replacement',
102
+ code: `
103
+ const result = await this.helpers.copyBinaryFile();
104
+ return this.helpers.prepareOutputData([{ json: response }]);`,
105
+ errors: [
106
+ {
107
+ messageId: 'deprecatedWithoutReplacement',
108
+ data: { functionName: 'copyBinaryFile' },
109
+ },
110
+ {
111
+ messageId: 'deprecatedWithoutReplacement',
112
+ data: { functionName: 'prepareOutputData' },
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ });
@@ -0,0 +1,166 @@
1
+ import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
2
+
3
+ const DEPRECATED_FUNCTIONS = {
4
+ request: 'httpRequest',
5
+ requestWithAuthentication: 'httpRequestWithAuthentication',
6
+ requestOAuth1: 'httpRequestWithAuthentication',
7
+ requestOAuth2: 'httpRequestWithAuthentication',
8
+ copyBinaryFile: null,
9
+ prepareOutputData: null,
10
+ } as const;
11
+
12
+ const DEPRECATED_TYPES = {
13
+ IRequestOptions: 'IHttpRequestOptions',
14
+ } as const;
15
+
16
+ function isDeprecatedFunctionName(name: string): name is keyof typeof DEPRECATED_FUNCTIONS {
17
+ return name in DEPRECATED_FUNCTIONS;
18
+ }
19
+
20
+ function isDeprecatedTypeName(name: string): name is keyof typeof DEPRECATED_TYPES {
21
+ return name in DEPRECATED_TYPES;
22
+ }
23
+
24
+ export const NoDeprecatedWorkflowFunctionsRule = ESLintUtils.RuleCreator.withoutDocs({
25
+ meta: {
26
+ type: 'problem',
27
+ docs: {
28
+ description: 'Disallow usage of deprecated functions and types from n8n-workflow package',
29
+ },
30
+ messages: {
31
+ deprecatedRequestFunction:
32
+ "'{{ functionName }}' is deprecated. Use '{{ replacement }}' instead for better authentication support and consistency.",
33
+ deprecatedFunction: "'{{ functionName }}' is deprecated and should be avoided. {{ message }}",
34
+ deprecatedType: "'{{ typeName }}' is deprecated. Use '{{ replacement }}' instead.",
35
+ deprecatedWithoutReplacement:
36
+ "'{{ functionName }}' is deprecated and should be removed or replaced with alternative implementation.",
37
+ },
38
+ schema: [],
39
+ },
40
+ defaultOptions: [],
41
+ create(context) {
42
+ const n8nWorkflowTypes = new Set<string>();
43
+
44
+ return {
45
+ ImportDeclaration(node) {
46
+ if (node.source.value === 'n8n-workflow') {
47
+ node.specifiers.forEach((specifier) => {
48
+ if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier') {
49
+ n8nWorkflowTypes.add(specifier.local.name);
50
+ }
51
+ });
52
+ }
53
+ },
54
+
55
+ MemberExpression(node) {
56
+ if (node.property.type === 'Identifier' && isDeprecatedFunctionName(node.property.name)) {
57
+ if (!isThisHelpersAccess(node)) {
58
+ return;
59
+ }
60
+
61
+ const functionName = node.property.name;
62
+ const replacement = DEPRECATED_FUNCTIONS[functionName];
63
+
64
+ if (replacement) {
65
+ const messageId = functionName.includes('request')
66
+ ? 'deprecatedRequestFunction'
67
+ : 'deprecatedFunction';
68
+
69
+ context.report({
70
+ node: node.property,
71
+ messageId,
72
+ data: {
73
+ functionName,
74
+ replacement,
75
+ message: getDeprecationMessage(functionName),
76
+ },
77
+ });
78
+ } else {
79
+ context.report({
80
+ node: node.property,
81
+ messageId: 'deprecatedWithoutReplacement',
82
+ data: {
83
+ functionName,
84
+ },
85
+ });
86
+ }
87
+ }
88
+ },
89
+
90
+ TSTypeReference(node) {
91
+ if (
92
+ node.typeName.type === 'Identifier' &&
93
+ isDeprecatedTypeName(node.typeName.name) &&
94
+ n8nWorkflowTypes.has(node.typeName.name)
95
+ ) {
96
+ const typeName = node.typeName.name;
97
+ const replacement = DEPRECATED_TYPES[typeName];
98
+
99
+ context.report({
100
+ node: node.typeName,
101
+ messageId: 'deprecatedType',
102
+ data: {
103
+ typeName,
104
+ replacement,
105
+ },
106
+ });
107
+ }
108
+ },
109
+
110
+ ImportSpecifier(node) {
111
+ // Check if this import is from n8n-workflow by looking at the parent ImportDeclaration
112
+ const importDeclaration = node.parent;
113
+ if (
114
+ importDeclaration?.type === 'ImportDeclaration' &&
115
+ importDeclaration.source.value === 'n8n-workflow' &&
116
+ node.imported.type === 'Identifier' &&
117
+ isDeprecatedTypeName(node.imported.name)
118
+ ) {
119
+ const typeName = node.imported.name;
120
+ const replacement = DEPRECATED_TYPES[typeName];
121
+
122
+ context.report({
123
+ node: node.imported,
124
+ messageId: 'deprecatedType',
125
+ data: {
126
+ typeName,
127
+ replacement,
128
+ },
129
+ });
130
+ }
131
+ },
132
+ };
133
+ },
134
+ });
135
+
136
+ /**
137
+ * Check if the MemberExpression follows the this.helpers.* pattern
138
+ */
139
+ function isThisHelpersAccess(node: TSESTree.MemberExpression): boolean {
140
+ if (node.object?.type === 'MemberExpression') {
141
+ const outerObject = node.object;
142
+ return (
143
+ outerObject.object?.type === 'ThisExpression' &&
144
+ outerObject.property?.type === 'Identifier' &&
145
+ outerObject.property.name === 'helpers'
146
+ );
147
+ }
148
+ return false;
149
+ }
150
+
151
+ function getDeprecationMessage(functionName: string): string {
152
+ switch (functionName) {
153
+ case 'request':
154
+ return 'Use httpRequest for better type safety and consistency.';
155
+ case 'requestWithAuthentication':
156
+ case 'requestOAuth1':
157
+ case 'requestOAuth2':
158
+ return 'Use httpRequestWithAuthentication which provides unified authentication handling.';
159
+ case 'copyBinaryFile':
160
+ return 'This function has been removed. Handle binary data directly.';
161
+ case 'prepareOutputData':
162
+ return 'This function is deprecated. Return data directly from execute method.';
163
+ default:
164
+ return 'This function is deprecated and should be avoided.';
165
+ }
166
+ }
@@ -0,0 +1,135 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { NoRestrictedGlobalsRule } from './no-restricted-globals.js';
3
+
4
+ const ruleTester = new RuleTester();
5
+
6
+ ruleTester.run('no-restricted-globals', NoRestrictedGlobalsRule, {
7
+ valid: [
8
+ {
9
+ code: 'const result = someFunction();',
10
+ },
11
+ {
12
+ code: 'window.setTimeout(() => {}, 1000);',
13
+ },
14
+ {
15
+ code: 'const obj = { global: "allowed" };',
16
+ },
17
+ {
18
+ code: 'function process() { return "allowed"; }',
19
+ },
20
+ {
21
+ code: 'console.clearInterval;',
22
+ },
23
+ {
24
+ code: 'const obj = { __dirname: "allowed", Buffer: "allowed", require: "allowed" };',
25
+ },
26
+ {
27
+ code: 'function globalThis() { return "allowed"; }',
28
+ },
29
+ {
30
+ code: 'const helper = require("./helper");',
31
+ },
32
+ {
33
+ name: 'variable declarations should be allowed',
34
+ code: 'const process = "my-process"; let global = "my-global";',
35
+ },
36
+ {
37
+ name: 'function parameters should be allowed',
38
+ code: 'function test(process, global, setTimeout) { return process; }',
39
+ },
40
+ {
41
+ name: 'arrow function parameters should be allowed',
42
+ code: 'const fn = (process, global) => process + global;',
43
+ },
44
+ {
45
+ name: 'destructuring should be allowed',
46
+ code: 'const { process, global } = someObject; const [setTimeout] = someArray;',
47
+ },
48
+ {
49
+ name: 'class methods should be allowed',
50
+ code: 'class MyClass { process() {} global = "value"; }',
51
+ },
52
+ {
53
+ name: 'import should be allowed',
54
+ code: 'import { process } from "./utils";',
55
+ },
56
+ {
57
+ name: 'function expressions should be allowed',
58
+ code: 'const fn = function process() {}; const fn2 = function global() {};',
59
+ },
60
+ {
61
+ name: 'locally declared variables should not trigger false positives',
62
+ code: `
63
+ const process = require('process');
64
+ const global = {};
65
+ const setTimeout = () => {};
66
+ function clearInterval() {}
67
+ let setInterval;
68
+ var __dirname = '/path';
69
+ const __filename = 'file.js';
70
+ `,
71
+ },
72
+ ],
73
+ invalid: [
74
+ {
75
+ code: 'const pid = process.pid;',
76
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'process' } }],
77
+ },
78
+ {
79
+ code: 'global.myVar = "test";',
80
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'global' } }],
81
+ },
82
+ {
83
+ code: 'setTimeout(() => {}, 1000);',
84
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'setTimeout' } }],
85
+ },
86
+ {
87
+ code: 'clearInterval(timer);',
88
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'clearInterval' } }],
89
+ },
90
+ {
91
+ code: 'clearTimeout(timer);',
92
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'clearTimeout' } }],
93
+ },
94
+ {
95
+ code: 'setInterval(() => {}, 1000);',
96
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'setInterval' } }],
97
+ },
98
+ {
99
+ code: `
100
+ const fn = () => {
101
+ process.exit(0);
102
+ global.something = true;
103
+ };`,
104
+ errors: [
105
+ { messageId: 'restrictedGlobal', data: { name: 'process' } },
106
+ { messageId: 'restrictedGlobal', data: { name: 'global' } },
107
+ ],
108
+ },
109
+ {
110
+ name: 'SECURITY: __dirname usage',
111
+ code: 'const currentDir = __dirname;',
112
+ errors: [{ messageId: 'restrictedGlobal', data: { name: '__dirname' } }],
113
+ },
114
+ {
115
+ name: 'SECURITY: __filename usage',
116
+ code: 'console.log(__filename);',
117
+ errors: [{ messageId: 'restrictedGlobal', data: { name: '__filename' } }],
118
+ },
119
+ {
120
+ name: 'SECURITY: globalThis usage',
121
+ code: 'globalThis.myVar = "test";',
122
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'globalThis' } }],
123
+ },
124
+ {
125
+ name: 'SECURITY: setImmediate usage',
126
+ code: 'setImmediate(() => {});',
127
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'setImmediate' } }],
128
+ },
129
+ {
130
+ name: 'SECURITY: clearImmediate usage',
131
+ code: 'clearImmediate(immediate);',
132
+ errors: [{ messageId: 'restrictedGlobal', data: { name: 'clearImmediate' } }],
133
+ },
134
+ ],
135
+ });
@@ -0,0 +1,71 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import type { TSESLint } from '@typescript-eslint/utils';
3
+
4
+ const restrictedGlobals = [
5
+ 'clearInterval',
6
+ 'clearTimeout',
7
+ 'global',
8
+ 'globalThis',
9
+ 'process',
10
+ 'setInterval',
11
+ 'setTimeout',
12
+ 'setImmediate',
13
+ 'clearImmediate',
14
+ '__dirname',
15
+ '__filename',
16
+ ];
17
+
18
+ export const NoRestrictedGlobalsRule = ESLintUtils.RuleCreator.withoutDocs({
19
+ meta: {
20
+ type: 'problem',
21
+ docs: {
22
+ description: 'Disallow usage of restricted global variables in community nodes.',
23
+ },
24
+ messages: {
25
+ restrictedGlobal: "Use of restricted global '{{ name }}' is not allowed",
26
+ },
27
+ schema: [],
28
+ },
29
+ defaultOptions: [],
30
+ create(context) {
31
+ function checkReference(ref: TSESLint.Scope.Reference, name: string) {
32
+ const { parent } = ref.identifier;
33
+
34
+ // Skip property access (like console.process - we want process.exit but not obj.process)
35
+ if (
36
+ parent?.type === 'MemberExpression' &&
37
+ parent.property === ref.identifier &&
38
+ !parent.computed
39
+ ) {
40
+ return;
41
+ }
42
+
43
+ context.report({
44
+ node: ref.identifier,
45
+ messageId: 'restrictedGlobal',
46
+ data: { name },
47
+ });
48
+ }
49
+
50
+ return {
51
+ Program() {
52
+ const globalScope = context.sourceCode.getScope(context.sourceCode.ast);
53
+
54
+ const allReferences = [
55
+ ...globalScope.variables
56
+ .filter(
57
+ (variable) => restrictedGlobals.includes(variable.name) && variable.defs.length === 0, // No definitions means it's a global
58
+ )
59
+ .flatMap((variable) =>
60
+ variable.references.map((ref) => ({ ref, name: variable.name })),
61
+ ),
62
+ ...globalScope.through
63
+ .filter((ref) => restrictedGlobals.includes(ref.identifier.name))
64
+ .map((ref) => ({ ref, name: ref.identifier.name })),
65
+ ];
66
+
67
+ allReferences.forEach(({ ref, name }) => checkReference(ref, name));
68
+ },
69
+ };
70
+ },
71
+ });
@@ -0,0 +1,181 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { NoRestrictedImportsRule } from './no-restricted-imports.js';
3
+
4
+ const ruleTester = new RuleTester();
5
+
6
+ ruleTester.run('no-restricted-imports', NoRestrictedImportsRule, {
7
+ valid: [
8
+ {
9
+ code: 'import { WorkflowExecuteMode } from "n8n-workflow";',
10
+ },
11
+ {
12
+ code: 'import _ from "lodash";',
13
+ },
14
+ {
15
+ code: 'import moment from "moment";',
16
+ },
17
+ {
18
+ code: 'import pLimit from "p-limit";',
19
+ },
20
+ {
21
+ code: 'import { DateTime } from "luxon";',
22
+ },
23
+ {
24
+ code: 'import { z } from "zod";',
25
+ },
26
+ {
27
+ code: 'import crypto from "crypto";',
28
+ },
29
+ {
30
+ code: 'import crypto from "node:crypto";',
31
+ },
32
+ {
33
+ code: 'import { helper } from "./helper";',
34
+ },
35
+ {
36
+ code: 'import { utils } from "../utils";',
37
+ },
38
+ {
39
+ code: 'const helper = require("./helper");',
40
+ },
41
+ {
42
+ code: 'const utils = require("../utils");',
43
+ },
44
+ {
45
+ code: 'const _ = require("lodash");',
46
+ },
47
+ {
48
+ code: 'require.resolve("lodash");',
49
+ },
50
+ {
51
+ code: 'require.resolve("./helper");',
52
+ },
53
+ {
54
+ code: 'require.resolve("../utils");',
55
+ },
56
+ {
57
+ code: 'const workflow = await import("n8n-workflow");',
58
+ },
59
+ {
60
+ code: 'import("lodash").then((_) => {});',
61
+ },
62
+ {
63
+ code: 'const helper = await import("./helper");',
64
+ },
65
+ {
66
+ code: 'import("../utils").then((utils) => {});',
67
+ },
68
+ {
69
+ code: 'import(`lodash`).then((_) => {});',
70
+ },
71
+ {
72
+ code: 'require(`./helper`);',
73
+ },
74
+ {
75
+ code: 'require.resolve(`n8n-workflow`);',
76
+ },
77
+ {
78
+ code: 'const workflow = await import(`n8n-workflow`);',
79
+ },
80
+ ],
81
+ invalid: [
82
+ {
83
+ code: 'import fs from "fs";',
84
+ errors: [{ messageId: 'restrictedImport', data: { modulePath: 'fs' } }],
85
+ },
86
+ {
87
+ code: 'import path from "path";',
88
+ errors: [{ messageId: 'restrictedImport', data: { modulePath: 'path' } }],
89
+ },
90
+ {
91
+ code: 'import express from "express";',
92
+ errors: [{ messageId: 'restrictedImport', data: { modulePath: 'express' } }],
93
+ },
94
+ {
95
+ code: 'import axios from "axios";',
96
+ errors: [{ messageId: 'restrictedImport', data: { modulePath: 'axios' } }],
97
+ },
98
+ {
99
+ code: 'import { Client } from "@elastic/elasticsearch";',
100
+ errors: [{ messageId: 'restrictedImport', data: { modulePath: '@elastic/elasticsearch' } }],
101
+ },
102
+ {
103
+ code: 'const fs = require("fs");',
104
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'fs' } }],
105
+ },
106
+ {
107
+ code: 'const path = require("path");',
108
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'path' } }],
109
+ },
110
+ {
111
+ code: 'const express = require("express");',
112
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'express' } }],
113
+ },
114
+ {
115
+ code: 'require.resolve("fs");',
116
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'fs' } }],
117
+ },
118
+ {
119
+ code: 'require.resolve("express");',
120
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'express' } }],
121
+ },
122
+ {
123
+ code: 'const resolved = require.resolve("axios");',
124
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'axios' } }],
125
+ },
126
+ {
127
+ code: `
128
+ import fs from "fs";
129
+ import path from "path";
130
+ import { WorkflowExecuteMode } from "n8n-workflow";`,
131
+ errors: [
132
+ { messageId: 'restrictedImport', data: { modulePath: 'fs' } },
133
+ { messageId: 'restrictedImport', data: { modulePath: 'path' } },
134
+ ],
135
+ },
136
+ {
137
+ code: `
138
+ const fs = require("fs");
139
+ const express = require("express");
140
+ const lodash = require("lodash");`,
141
+ errors: [
142
+ { messageId: 'restrictedRequire', data: { modulePath: 'fs' } },
143
+ { messageId: 'restrictedRequire', data: { modulePath: 'express' } },
144
+ ],
145
+ },
146
+ {
147
+ code: 'const fs = await import("fs");',
148
+ errors: [{ messageId: 'restrictedDynamicImport', data: { modulePath: 'fs' } }],
149
+ },
150
+ {
151
+ code: 'import("path").then((path) => {});',
152
+ errors: [{ messageId: 'restrictedDynamicImport', data: { modulePath: 'path' } }],
153
+ },
154
+ {
155
+ code: 'const express = await import("express");',
156
+ errors: [{ messageId: 'restrictedDynamicImport', data: { modulePath: 'express' } }],
157
+ },
158
+ {
159
+ code: 'const path = require(`path`);',
160
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'path' } }],
161
+ },
162
+ {
163
+ code: 'require.resolve(`express`);',
164
+ errors: [{ messageId: 'restrictedRequire', data: { modulePath: 'express' } }],
165
+ },
166
+ {
167
+ code: 'const axios = await import(`axios`);',
168
+ errors: [{ messageId: 'restrictedDynamicImport', data: { modulePath: 'axios' } }],
169
+ },
170
+ {
171
+ code: `
172
+ const fs = await import("fs");
173
+ import("axios").then((axios) => {});
174
+ const workflow = await import("n8n-workflow");`,
175
+ errors: [
176
+ { messageId: 'restrictedDynamicImport', data: { modulePath: 'fs' } },
177
+ { messageId: 'restrictedDynamicImport', data: { modulePath: 'axios' } },
178
+ ],
179
+ },
180
+ ],
181
+ });