invar-tools 1.17.2__py3-none-any.whl → 1.17.3__py3-none-any.whl
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.
- invar/node_tools/.gitignore +10 -6
- invar/node_tools/eslint-plugin/rules/__tests__/behavior.test.js +1321 -0
- invar/node_tools/eslint-plugin/rules/__tests__/behavior.test.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/e2e-scenarios.test.js +414 -0
- invar/node_tools/eslint-plugin/rules/__tests__/e2e-scenarios.test.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/function-lengths.js +142 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/function-lengths.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/has-io-imports.js +15 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/has-io-imports.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/valid-small.js +27 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/core/valid-small.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/exported-functions.js +43 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/exported-functions.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/shell/with-io.js +27 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/shell/with-io.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/tests/large.test.js +260 -0
- invar/node_tools/eslint-plugin/rules/__tests__/fixtures/tests/large.test.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/max-file-lines.js +105 -0
- invar/node_tools/eslint-plugin/rules/max-file-lines.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/max-function-lines.js +133 -0
- invar/node_tools/eslint-plugin/rules/max-function-lines.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-any-in-schema.js +39 -0
- invar/node_tools/eslint-plugin/rules/no-any-in-schema.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-empty-schema.js +69 -0
- invar/node_tools/eslint-plugin/rules/no-empty-schema.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-impure-calls-in-core.js +52 -0
- invar/node_tools/eslint-plugin/rules/no-impure-calls-in-core.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-io-in-core.js +99 -0
- invar/node_tools/eslint-plugin/rules/no-io-in-core.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-pure-logic-in-shell.js +197 -0
- invar/node_tools/eslint-plugin/rules/no-pure-logic-in-shell.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-redundant-type-schema.js +99 -0
- invar/node_tools/eslint-plugin/rules/no-redundant-type-schema.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/no-runtime-imports.js +66 -0
- invar/node_tools/eslint-plugin/rules/no-runtime-imports.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/require-complete-validation.js +104 -0
- invar/node_tools/eslint-plugin/rules/require-complete-validation.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js +81 -0
- invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/require-schema-validation.js +308 -0
- invar/node_tools/eslint-plugin/rules/require-schema-validation.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/shell-complexity.js +273 -0
- invar/node_tools/eslint-plugin/rules/shell-complexity.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/shell-result-type.js +138 -0
- invar/node_tools/eslint-plugin/rules/shell-result-type.js.map +1 -0
- invar/node_tools/eslint-plugin/rules/thin-entry-points.js +174 -0
- invar/node_tools/eslint-plugin/rules/thin-entry-points.js.map +1 -0
- invar/node_tools/eslint-plugin/utils/layer-detection.js +91 -0
- invar/node_tools/eslint-plugin/utils/layer-detection.js.map +1 -0
- invar/node_tools/eslint-plugin/utils/math-example.js +31 -0
- invar/node_tools/eslint-plugin/utils/math-example.js.map +1 -0
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/METADATA +1 -1
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/RECORD +58 -8
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/WHEEL +0 -0
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.17.2.dist-info → invar_tools-1.17.3.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-empty-schema
|
|
3
|
+
*
|
|
4
|
+
* Detect Zod schemas that match everything, providing false security.
|
|
5
|
+
*
|
|
6
|
+
* Detects:
|
|
7
|
+
* - z.object({}) with no properties
|
|
8
|
+
* - .passthrough() calls (defeats validation)
|
|
9
|
+
* - .loose() calls (ignores unknown properties)
|
|
10
|
+
*/
|
|
11
|
+
export const noEmptySchema = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Forbid empty or permissive Zod schemas that defeat validation',
|
|
16
|
+
recommended: true,
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
messages: {
|
|
20
|
+
emptyObject: 'Empty z.object({}) accepts any object. Add properties or use z.record()',
|
|
21
|
+
passthrough: 'Schema with .passthrough() bypasses unknown property validation. Remove or use .strict()',
|
|
22
|
+
loose: 'Schema with .loose() ignores unknown properties. Remove or use .strict()',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
create(context) {
|
|
26
|
+
return {
|
|
27
|
+
CallExpression(node) {
|
|
28
|
+
const callee = node.callee;
|
|
29
|
+
// Check for z.object({})
|
|
30
|
+
if (callee.type === 'MemberExpression' &&
|
|
31
|
+
callee.object.type === 'Identifier' &&
|
|
32
|
+
callee.object.name === 'z' &&
|
|
33
|
+
callee.property.type === 'Identifier' &&
|
|
34
|
+
callee.property.name === 'object') {
|
|
35
|
+
const args = node.arguments;
|
|
36
|
+
if (args.length === 1 && args[0].type === 'ObjectExpression') {
|
|
37
|
+
const properties = args[0].properties;
|
|
38
|
+
if (properties.length === 0) {
|
|
39
|
+
context.report({
|
|
40
|
+
node: node,
|
|
41
|
+
messageId: 'emptyObject',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check for .passthrough() calls
|
|
47
|
+
if (callee.type === 'MemberExpression' &&
|
|
48
|
+
callee.property.type === 'Identifier' &&
|
|
49
|
+
callee.property.name === 'passthrough') {
|
|
50
|
+
context.report({
|
|
51
|
+
node: node,
|
|
52
|
+
messageId: 'passthrough',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Check for .loose() calls
|
|
56
|
+
if (callee.type === 'MemberExpression' &&
|
|
57
|
+
callee.property.type === 'Identifier' &&
|
|
58
|
+
callee.property.name === 'loose') {
|
|
59
|
+
context.report({
|
|
60
|
+
node: node,
|
|
61
|
+
messageId: 'loose',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
export default noEmptySchema;
|
|
69
|
+
//# sourceMappingURL=no-empty-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-empty-schema.js","sourceRoot":"","sources":["../../src/rules/no-empty-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,+DAA+D;YAC5E,WAAW,EAAE,IAAI;SAClB;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,WAAW,EAAE,yEAAyE;YACtF,WAAW,EAAE,0FAA0F;YACvG,KAAK,EAAE,0EAA0E;SAClF;KACF;IAED,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,MAAM,GAAI,IAAuB,CAAC,MAAM,CAAC;gBAE/C,yBAAyB;gBACzB,IACE,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBAClC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACnC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,GAAG;oBAC1B,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBACrC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,QAAQ,EACjC,CAAC;oBACD,MAAM,IAAI,GAAI,IAAuB,CAAC,SAAS,CAAC;oBAChD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;wBAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,UAAwB,CAAC;wBACpD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;4BAC5B,OAAO,CAAC,MAAM,CAAC;gCACb,IAAI,EAAE,IAA4B;gCAClC,SAAS,EAAE,aAAa;6BACzB,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,iCAAiC;gBACjC,IACE,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBAClC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBACrC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,aAAa,EACtC,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAA4B;wBAClC,SAAS,EAAE,aAAa;qBACzB,CAAC,CAAC;gBACL,CAAC;gBAED,2BAA2B;gBAC3B,IACE,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBAClC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBACrC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,EAChC,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAA4B;wBAClC,SAAS,EAAE,OAAO;qBACnB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-impure-calls-in-core
|
|
3
|
+
*
|
|
4
|
+
* Forbid Core functions calling Shell functions.
|
|
5
|
+
* Core should be pure - no imports from shell/ directories.
|
|
6
|
+
*
|
|
7
|
+
* Detects:
|
|
8
|
+
* - Imports from ../shell/ in core/ files
|
|
9
|
+
* - Imports from shell/ in core/ files
|
|
10
|
+
*/
|
|
11
|
+
export const noImpureCallsInCore = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Forbid Core functions calling Shell functions (imports from shell/)',
|
|
16
|
+
recommended: true,
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
messages: {
|
|
20
|
+
shellImportInCore: 'Core file importing from Shell: "{{source}}". Core must be pure - move I/O logic to Shell or extract pure logic.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
const filename = context.filename || context.getFilename();
|
|
25
|
+
// Only check files in core/ directories
|
|
26
|
+
const isCore = /[/\\]core[/\\]/.test(filename);
|
|
27
|
+
if (!isCore) {
|
|
28
|
+
return {}; // Skip non-core files
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
ImportDeclaration(node) {
|
|
32
|
+
const importNode = node;
|
|
33
|
+
if (importNode.source && importNode.source.type === 'Literal') {
|
|
34
|
+
const source = String(importNode.source.value);
|
|
35
|
+
// Check if importing from shell/
|
|
36
|
+
// Matches: ../shell/*, ../../shell/*, /shell/*, shell/*
|
|
37
|
+
if (/[/\\]shell[/\\]/.test(source) || /^shell[/\\]/.test(source)) {
|
|
38
|
+
context.report({
|
|
39
|
+
node: node,
|
|
40
|
+
messageId: 'shellImportInCore',
|
|
41
|
+
data: {
|
|
42
|
+
source: source,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export default noImpureCallsInCore;
|
|
52
|
+
//# sourceMappingURL=no-impure-calls-in-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-impure-calls-in-core.js","sourceRoot":"","sources":["../../src/rules/no-impure-calls-in-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,CAAC,MAAM,mBAAmB,GAAoB;IAClD,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,qEAAqE;YAClF,WAAW,EAAE,IAAI;SAClB;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,iBAAiB,EACf,kHAAkH;SACrH;KACF;IAED,MAAM,CAAC,OAAO;QACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QAE3D,wCAAwC;QACxC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC,CAAC,sBAAsB;QACnC,CAAC;QAED,OAAO;YACL,iBAAiB,CAAC,IAAI;gBACpB,MAAM,UAAU,GAAG,IAAoC,CAAC;gBAExD,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9D,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAE/C,iCAAiC;oBACjC,wDAAwD;oBACxD,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;wBACjE,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,IAA4B;4BAClC,SAAS,EAAE,mBAAmB;4BAC9B,IAAI,EAAE;gCACJ,MAAM,EAAE,MAAM;6BACf;yBACF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-io-in-core
|
|
3
|
+
*
|
|
4
|
+
* Forbid I/O imports in /core/ directories.
|
|
5
|
+
* This enforces the Core/Shell separation pattern.
|
|
6
|
+
*/
|
|
7
|
+
const IO_MODULES = new Set([
|
|
8
|
+
'fs',
|
|
9
|
+
'fs/promises',
|
|
10
|
+
'node:fs',
|
|
11
|
+
'node:fs/promises',
|
|
12
|
+
'path',
|
|
13
|
+
'node:path',
|
|
14
|
+
'http',
|
|
15
|
+
'https',
|
|
16
|
+
'node:http',
|
|
17
|
+
'node:https',
|
|
18
|
+
'net',
|
|
19
|
+
'node:net',
|
|
20
|
+
'child_process',
|
|
21
|
+
'node:child_process',
|
|
22
|
+
'readline',
|
|
23
|
+
'node:readline',
|
|
24
|
+
'process',
|
|
25
|
+
'node:process',
|
|
26
|
+
]);
|
|
27
|
+
const IO_PACKAGE_PATTERNS = [
|
|
28
|
+
/^axios/,
|
|
29
|
+
/^node-fetch/,
|
|
30
|
+
/^got$/,
|
|
31
|
+
/^superagent/,
|
|
32
|
+
/^request/,
|
|
33
|
+
/^pg$/,
|
|
34
|
+
/^mysql/,
|
|
35
|
+
/^mongodb/,
|
|
36
|
+
/^redis/,
|
|
37
|
+
/^ioredis/,
|
|
38
|
+
/^@aws-sdk\//,
|
|
39
|
+
/^@vercel\//,
|
|
40
|
+
];
|
|
41
|
+
function isIoModule(source) {
|
|
42
|
+
if (IO_MODULES.has(source))
|
|
43
|
+
return true;
|
|
44
|
+
return IO_PACKAGE_PATTERNS.some(pattern => pattern.test(source));
|
|
45
|
+
}
|
|
46
|
+
function isInCoreDirectory(filename) {
|
|
47
|
+
// Normalize to lowercase and forward slashes for consistent cross-platform matching
|
|
48
|
+
const normalized = filename.replace(/\\/g, '/').toLowerCase();
|
|
49
|
+
return normalized.includes('/core/');
|
|
50
|
+
}
|
|
51
|
+
export const noIoInCore = {
|
|
52
|
+
meta: {
|
|
53
|
+
type: 'problem',
|
|
54
|
+
docs: {
|
|
55
|
+
description: 'Forbid I/O imports in /core/ directories',
|
|
56
|
+
recommended: true,
|
|
57
|
+
},
|
|
58
|
+
schema: [],
|
|
59
|
+
messages: {
|
|
60
|
+
ioInCore: 'I/O module "{{module}}" is not allowed in /core/ directory. Move I/O to /shell/ or inject as parameter.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
create(context) {
|
|
64
|
+
const filename = context.filename || context.getFilename();
|
|
65
|
+
if (!isInCoreDirectory(filename)) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
ImportDeclaration(node) {
|
|
70
|
+
const source = node.source.value;
|
|
71
|
+
if (typeof source === 'string' && isIoModule(source)) {
|
|
72
|
+
context.report({
|
|
73
|
+
node: node,
|
|
74
|
+
messageId: 'ioInCore',
|
|
75
|
+
data: { module: source },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
CallExpression(node) {
|
|
80
|
+
// Check for require() calls
|
|
81
|
+
if (node.callee.type === 'Identifier' &&
|
|
82
|
+
node.callee.name === 'require' &&
|
|
83
|
+
node.arguments.length > 0 &&
|
|
84
|
+
node.arguments[0].type === 'Literal') {
|
|
85
|
+
const source = node.arguments[0].value;
|
|
86
|
+
if (typeof source === 'string' && isIoModule(source)) {
|
|
87
|
+
context.report({
|
|
88
|
+
node: node,
|
|
89
|
+
messageId: 'ioInCore',
|
|
90
|
+
data: { module: source },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export default noIoInCore;
|
|
99
|
+
//# sourceMappingURL=no-io-in-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-io-in-core.js","sourceRoot":"","sources":["../../src/rules/no-io-in-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-pure-logic-in-shell
|
|
3
|
+
*
|
|
4
|
+
* Warn when Shell functions contain pure logic that should be in Core.
|
|
5
|
+
* Shell functions should perform I/O operations, not pure computations.
|
|
6
|
+
*
|
|
7
|
+
* Heuristics for detecting pure logic:
|
|
8
|
+
* - No async/await usage
|
|
9
|
+
* - No I/O-related API calls (fs, http, fetch, db, etc.)
|
|
10
|
+
* - No Result type in return
|
|
11
|
+
* - Function body has more than 3 statements (substantial logic)
|
|
12
|
+
*/
|
|
13
|
+
// I/O-related identifiers that indicate impure operations
|
|
14
|
+
// Note: Result/Success/Failure are NOT I/O indicators - they're just return type wrappers
|
|
15
|
+
// that can be used with pure logic. Only actual I/O operations should be listed here.
|
|
16
|
+
const IO_IDENTIFIERS = [
|
|
17
|
+
'fs',
|
|
18
|
+
'readFile',
|
|
19
|
+
'writeFile',
|
|
20
|
+
'fetch',
|
|
21
|
+
'axios',
|
|
22
|
+
'http',
|
|
23
|
+
'https',
|
|
24
|
+
'db',
|
|
25
|
+
'database',
|
|
26
|
+
'query',
|
|
27
|
+
'execute',
|
|
28
|
+
'readFileSync',
|
|
29
|
+
'writeFileSync',
|
|
30
|
+
'existsSync',
|
|
31
|
+
'mkdir',
|
|
32
|
+
'rmdir',
|
|
33
|
+
'unlink',
|
|
34
|
+
'readdir',
|
|
35
|
+
'stat',
|
|
36
|
+
'access',
|
|
37
|
+
'net',
|
|
38
|
+
'spawn',
|
|
39
|
+
'exec',
|
|
40
|
+
'execSync',
|
|
41
|
+
'child_process',
|
|
42
|
+
'WebSocket',
|
|
43
|
+
'XMLHttpRequest',
|
|
44
|
+
'request',
|
|
45
|
+
'got',
|
|
46
|
+
'console',
|
|
47
|
+
];
|
|
48
|
+
export const noPureLogicInShell = {
|
|
49
|
+
meta: {
|
|
50
|
+
type: 'suggestion',
|
|
51
|
+
docs: {
|
|
52
|
+
description: 'Warn when Shell functions contain pure logic that should be in Core',
|
|
53
|
+
recommended: false,
|
|
54
|
+
},
|
|
55
|
+
schema: [],
|
|
56
|
+
messages: {
|
|
57
|
+
pureLogicInShell: 'Shell function "{{name}}" appears to contain pure logic. Consider moving to Core if it performs no I/O.',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
create(context) {
|
|
61
|
+
const filename = context.filename || context.getFilename();
|
|
62
|
+
// Only check files in shell/ directories
|
|
63
|
+
const isShell = /[/\\]shell[/\\]/.test(filename);
|
|
64
|
+
if (!isShell) {
|
|
65
|
+
return {}; // Skip non-shell files
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if function contains I/O indicators
|
|
69
|
+
*/
|
|
70
|
+
function hasIOIndicators(node) {
|
|
71
|
+
let hasIO = false;
|
|
72
|
+
// Check if function is async
|
|
73
|
+
if (node.async) {
|
|
74
|
+
hasIO = true;
|
|
75
|
+
}
|
|
76
|
+
// Walk the function body looking for I/O-related identifiers
|
|
77
|
+
// Optimization: Limit depth and skip non-identifier containers
|
|
78
|
+
const MAX_DEPTH = 10; // Reduced from 50 for better performance
|
|
79
|
+
function visit(n, depth = 0) {
|
|
80
|
+
if (hasIO)
|
|
81
|
+
return; // Early return if already found
|
|
82
|
+
if (depth > MAX_DEPTH)
|
|
83
|
+
return; // Depth limit
|
|
84
|
+
// Performance: Check node type first
|
|
85
|
+
if (n.type === 'Identifier') {
|
|
86
|
+
if (IO_IDENTIFIERS.includes(n.name)) {
|
|
87
|
+
hasIO = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Performance: Skip node types that cannot contain identifiers
|
|
92
|
+
if (n.type === 'Literal' ||
|
|
93
|
+
n.type === 'TemplateElement' ||
|
|
94
|
+
n.type === 'Super' ||
|
|
95
|
+
n.type === 'ThisExpression') {
|
|
96
|
+
return; // These cannot contain identifier children
|
|
97
|
+
}
|
|
98
|
+
// Recursively visit children with depth tracking
|
|
99
|
+
// Only visit properties that typically contain code
|
|
100
|
+
const relevantKeys = ['body', 'expression', 'callee', 'object', 'property', 'left', 'right', 'test', 'consequent', 'alternate', 'arguments', 'params'];
|
|
101
|
+
for (const key of relevantKeys) {
|
|
102
|
+
const value = n[key];
|
|
103
|
+
if (!value)
|
|
104
|
+
continue;
|
|
105
|
+
if (typeof value === 'object') {
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
for (const item of value) {
|
|
108
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
109
|
+
visit(item, depth + 1);
|
|
110
|
+
if (hasIO)
|
|
111
|
+
return; // Early exit
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if ('type' in value) {
|
|
116
|
+
visit(value, depth + 1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (node.body) {
|
|
122
|
+
visit(node.body);
|
|
123
|
+
}
|
|
124
|
+
return hasIO;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if function body is substantial (more than 3 statements)
|
|
128
|
+
*/
|
|
129
|
+
function hasSubstantialLogic(node) {
|
|
130
|
+
if (!node.body || node.body.type !== 'BlockStatement') {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const blockBody = node.body;
|
|
134
|
+
return blockBody.body.length > 3;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get function name with improved extraction from parent context
|
|
138
|
+
*/
|
|
139
|
+
function getFunctionName(node) {
|
|
140
|
+
// 1. FunctionDeclaration - use direct id
|
|
141
|
+
if (node.type === 'FunctionDeclaration' && node.id) {
|
|
142
|
+
return node.id.name;
|
|
143
|
+
}
|
|
144
|
+
// 2. FunctionExpression - try id first, then parent
|
|
145
|
+
if (node.type === 'FunctionExpression' && node.id) {
|
|
146
|
+
return node.id.name;
|
|
147
|
+
}
|
|
148
|
+
// 3. For unnamed FunctionExpression or ArrowFunctionExpression,
|
|
149
|
+
// try to get name from parent VariableDeclarator
|
|
150
|
+
try {
|
|
151
|
+
const ancestors = context.getAncestors();
|
|
152
|
+
// Look for parent VariableDeclarator
|
|
153
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
154
|
+
const ancestor = ancestors[i];
|
|
155
|
+
if (ancestor.type === 'VariableDeclarator') {
|
|
156
|
+
const varDecl = ancestor;
|
|
157
|
+
if (varDecl.id && varDecl.id.type === 'Identifier' && varDecl.id.name) {
|
|
158
|
+
return varDecl.id.name;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// If ancestor lookup fails, fall through to 'anonymous'
|
|
165
|
+
}
|
|
166
|
+
return 'anonymous';
|
|
167
|
+
}
|
|
168
|
+
function checkFunction(node) {
|
|
169
|
+
const functionName = getFunctionName(node);
|
|
170
|
+
// Skip if function name is anonymous or very short (likely helper)
|
|
171
|
+
if (functionName === 'anonymous' || functionName.length < 3) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Skip if function has I/O indicators
|
|
175
|
+
if (hasIOIndicators(node)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// Warn if function has substantial logic but no I/O
|
|
179
|
+
if (hasSubstantialLogic(node)) {
|
|
180
|
+
context.report({
|
|
181
|
+
node: node,
|
|
182
|
+
messageId: 'pureLogicInShell',
|
|
183
|
+
data: {
|
|
184
|
+
name: functionName,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
FunctionDeclaration: checkFunction,
|
|
191
|
+
FunctionExpression: checkFunction,
|
|
192
|
+
ArrowFunctionExpression: checkFunction,
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
export default noPureLogicInShell;
|
|
197
|
+
//# sourceMappingURL=no-pure-logic-in-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-pure-logic-in-shell.js","sourceRoot":"","sources":["../../src/rules/no-pure-logic-in-shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-redundant-type-schema
|
|
3
|
+
*
|
|
4
|
+
* Detect Zod schemas that only repeat TypeScript types without adding semantic constraints.
|
|
5
|
+
*
|
|
6
|
+
* Detects:
|
|
7
|
+
* - z.string() without .min/.max/.regex/.email/etc
|
|
8
|
+
* - z.number() without .int/.min/.max/.positive/etc
|
|
9
|
+
* - z.boolean() (almost always redundant)
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Check if a schema call chain has any refinements
|
|
13
|
+
*/
|
|
14
|
+
function hasRefinements(node, baseType) {
|
|
15
|
+
const refinementMethods = {
|
|
16
|
+
string: ['min', 'max', 'length', 'email', 'url', 'emoji', 'uuid', 'cuid', 'regex', 'startsWith', 'endsWith', 'trim', 'toLowerCase', 'toUpperCase', 'refine', 'transform'],
|
|
17
|
+
number: ['min', 'max', 'int', 'positive', 'negative', 'nonnegative', 'nonpositive', 'multipleOf', 'finite', 'safe', 'refine', 'transform'],
|
|
18
|
+
boolean: [], // boolean is always redundant
|
|
19
|
+
};
|
|
20
|
+
const allowedMethods = refinementMethods[baseType] || [];
|
|
21
|
+
// Walk up the AST to find any method calls on this schema
|
|
22
|
+
let current = node.parent;
|
|
23
|
+
while (current) {
|
|
24
|
+
if (current.type === 'CallExpression') {
|
|
25
|
+
const callee = current.callee;
|
|
26
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
27
|
+
const methodName = callee.property.name;
|
|
28
|
+
if (allowedMethods.includes(methodName)) {
|
|
29
|
+
return true; // Found a refinement
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
current = current.parent;
|
|
33
|
+
}
|
|
34
|
+
else if (current.type === 'VariableDeclarator' || current.type === 'Property') {
|
|
35
|
+
// Reached variable declaration or object property, stop searching
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
current = current.parent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export const noRedundantTypeSchema = {
|
|
45
|
+
meta: {
|
|
46
|
+
type: 'suggestion',
|
|
47
|
+
docs: {
|
|
48
|
+
description: 'Forbid Zod schemas that only repeat TypeScript types without adding constraints',
|
|
49
|
+
recommended: true,
|
|
50
|
+
},
|
|
51
|
+
schema: [],
|
|
52
|
+
messages: {
|
|
53
|
+
redundantString: 'z.string() without constraints is redundant. Add .min(), .max(), .regex(), or use plain TypeScript type.',
|
|
54
|
+
redundantNumber: 'z.number() without constraints is redundant. Add .min(), .max(), .int(), or use plain TypeScript type.',
|
|
55
|
+
redundantBoolean: 'z.boolean() is almost always redundant. Use plain TypeScript boolean type unless validating external input.',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
create(context) {
|
|
59
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
60
|
+
return {
|
|
61
|
+
CallExpression(node) {
|
|
62
|
+
const callee = node.callee;
|
|
63
|
+
// Only check direct z.type() calls, not chained calls
|
|
64
|
+
if (callee.type === 'MemberExpression' &&
|
|
65
|
+
callee.object.type === 'Identifier' &&
|
|
66
|
+
callee.object.name === 'z' &&
|
|
67
|
+
callee.property.type === 'Identifier') {
|
|
68
|
+
const typeName = callee.property.name;
|
|
69
|
+
// Add parent reference for hasRefinements to work
|
|
70
|
+
if (!node.parent) {
|
|
71
|
+
const parents = sourceCode.getAncestors(node);
|
|
72
|
+
node.parent = parents[parents.length - 1];
|
|
73
|
+
}
|
|
74
|
+
if (typeName === 'string' && !hasRefinements(node, 'string')) {
|
|
75
|
+
context.report({
|
|
76
|
+
node: node,
|
|
77
|
+
messageId: 'redundantString',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (typeName === 'number' && !hasRefinements(node, 'number')) {
|
|
81
|
+
context.report({
|
|
82
|
+
node: node,
|
|
83
|
+
messageId: 'redundantNumber',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (typeName === 'boolean') {
|
|
87
|
+
// boolean is always redundant since TypeScript already enforces it
|
|
88
|
+
context.report({
|
|
89
|
+
node: node,
|
|
90
|
+
messageId: 'redundantBoolean',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export default noRedundantTypeSchema;
|
|
99
|
+
//# sourceMappingURL=no-redundant-type-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-redundant-type-schema.js","sourceRoot":"","sources":["../../src/rules/no-redundant-type-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: no-runtime-imports
|
|
3
|
+
*
|
|
4
|
+
* Forbid imports inside functions (runtime imports).
|
|
5
|
+
* All imports should be at module top-level for predictability and performance.
|
|
6
|
+
*
|
|
7
|
+
* Detects:
|
|
8
|
+
* - require() calls inside functions
|
|
9
|
+
* - dynamic import() calls inside functions
|
|
10
|
+
*/
|
|
11
|
+
export const noRuntimeImports = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Forbid imports inside functions (require runtime imports at module top-level)',
|
|
16
|
+
recommended: true,
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
messages: {
|
|
20
|
+
runtimeRequire: 'Runtime require() detected. Move imports to module top-level for predictability.',
|
|
21
|
+
runtimeImport: 'Dynamic import() detected. Move imports to module top-level for predictability.',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
create(context) {
|
|
25
|
+
/**
|
|
26
|
+
* Check if node is inside a function
|
|
27
|
+
*/
|
|
28
|
+
function isInsideFunction(node) {
|
|
29
|
+
const ancestors = context.sourceCode?.getAncestors?.(node) || context.getAncestors();
|
|
30
|
+
for (const ancestor of ancestors) {
|
|
31
|
+
if (ancestor.type === 'FunctionDeclaration' ||
|
|
32
|
+
ancestor.type === 'FunctionExpression' ||
|
|
33
|
+
ancestor.type === 'ArrowFunctionExpression') {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
CallExpression(node) {
|
|
41
|
+
const callNode = node;
|
|
42
|
+
// Check for require() calls
|
|
43
|
+
if (callNode.callee.type === 'Identifier' &&
|
|
44
|
+
callNode.callee.name === 'require') {
|
|
45
|
+
if (isInsideFunction(node)) {
|
|
46
|
+
context.report({
|
|
47
|
+
node: node,
|
|
48
|
+
messageId: 'runtimeRequire',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
ImportExpression(node) {
|
|
54
|
+
// Check for dynamic import()
|
|
55
|
+
if (isInsideFunction(node)) {
|
|
56
|
+
context.report({
|
|
57
|
+
node: node,
|
|
58
|
+
messageId: 'runtimeImport',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export default noRuntimeImports;
|
|
66
|
+
//# sourceMappingURL=no-runtime-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-runtime-imports.js","sourceRoot":"","sources":["../../src/rules/no-runtime-imports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,+EAA+E;YAC5F,WAAW,EAAE,IAAI;SAClB;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,cAAc,EACZ,kFAAkF;YACpF,aAAa,EACX,iFAAiF;SACpF;KACF;IAED,MAAM,CAAC,OAAO;QACZ;;WAEG;QACH,SAAS,gBAAgB,CAAC,IAAe;YACvC,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAErF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,IACE,QAAQ,CAAC,IAAI,KAAK,qBAAqB;oBACvC,QAAQ,CAAC,IAAI,KAAK,oBAAoB;oBACtC,QAAQ,CAAC,IAAI,KAAK,yBAAyB,EAC3C,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,QAAQ,GAAG,IAAiC,CAAC;gBAEnD,4BAA4B;gBAC5B,IACE,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACrC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAClC,CAAC;oBACD,IAAI,gBAAgB,CAAC,IAA4B,CAAC,EAAE,CAAC;wBACnD,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,IAA4B;4BAClC,SAAS,EAAE,gBAAgB;yBAC5B,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAED,gBAAgB,CAAC,IAAI;gBACnB,6BAA6B;gBAC7B,IAAI,gBAAgB,CAAC,IAA4B,CAAC,EAAE,CAAC;oBACnD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAA4B;wBAClC,SAAS,EAAE,eAAe;qBAC3B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|