@webpieces/eslint-rules 0.3.285 → 0.3.287

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/eslint-rules",
3
- "version": "0.3.285",
3
+ "version": "0.3.287",
4
4
  "description": "ESLint rules for WebPieces code patterns and architecture enforcement",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,7 +22,7 @@
22
22
  "directory": "packages/tooling/eslint-rules"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/rules-config": "0.3.285"
25
+ "@webpieces/rules-config": "0.3.287"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "eslint": ">=8.0.0"
package/recommended.mjs CHANGED
@@ -58,7 +58,6 @@ export default [
58
58
  '@webpieces/max-file-lines': ['error', { max: 700 }],
59
59
  '@webpieces/enforce-architecture': 'error',
60
60
  '@webpieces/no-json-property-primitive-type': 'error',
61
- '@webpieces/no-process-exit-outside-main': 'error',
62
61
  'no-console': 'off',
63
62
  'no-debugger': 'warn',
64
63
  'no-alert': 'warn',
package/src/index.d.ts CHANGED
@@ -13,7 +13,6 @@
13
13
  * - no-json-property-primitive-type: Ban @JsonProperty({ type: String/Number/Boolean })
14
14
  * - require-typed-template: Require [templateClassType] on ng-template with let- variables (Angular)
15
15
  * - no-mat-cell-def: Ban *matCellDef/*matHeaderCellDef — use div-grid tables (Angular)
16
- * - no-process-exit-outside-main: process.exit only in main()/runMain; never import another module's main
17
16
  */
18
17
  declare const _default: {
19
18
  rules: {
@@ -25,7 +24,6 @@ declare const _default: {
25
24
  'no-json-property-primitive-type': import("eslint").Rule.RuleModule;
26
25
  'require-typed-template': import("eslint").Rule.RuleModule;
27
26
  'no-mat-cell-def': import("eslint").Rule.RuleModule;
28
- 'no-process-exit-outside-main': import("eslint").Rule.RuleModule;
29
27
  };
30
28
  };
31
29
  export = _default;
package/src/index.js CHANGED
@@ -14,7 +14,6 @@
14
14
  * - no-json-property-primitive-type: Ban @JsonProperty({ type: String/Number/Boolean })
15
15
  * - require-typed-template: Require [templateClassType] on ng-template with let- variables (Angular)
16
16
  * - no-mat-cell-def: Ban *matCellDef/*matHeaderCellDef — use div-grid tables (Angular)
17
- * - no-process-exit-outside-main: process.exit only in main()/runMain; never import another module's main
18
17
  */
19
18
  const tslib_1 = require("tslib");
20
19
  const catch_error_pattern_1 = tslib_1.__importDefault(require("./rules/catch-error-pattern"));
@@ -25,7 +24,6 @@ const enforce_architecture_1 = tslib_1.__importDefault(require("./rules/enforce-
25
24
  const no_json_property_primitive_type_1 = tslib_1.__importDefault(require("./rules/no-json-property-primitive-type"));
26
25
  const require_typed_template_1 = tslib_1.__importDefault(require("./rules/require-typed-template"));
27
26
  const no_mat_cell_def_1 = tslib_1.__importDefault(require("./rules/no-mat-cell-def"));
28
- const no_process_exit_outside_main_1 = tslib_1.__importDefault(require("./rules/no-process-exit-outside-main"));
29
27
  module.exports = {
30
28
  rules: {
31
29
  'catch-error-pattern': catch_error_pattern_1.default,
@@ -36,7 +34,6 @@ module.exports = {
36
34
  'no-json-property-primitive-type': no_json_property_primitive_type_1.default,
37
35
  'require-typed-template': require_typed_template_1.default,
38
36
  'no-mat-cell-def': no_mat_cell_def_1.default,
39
- 'no-process-exit-outside-main': no_process_exit_outside_main_1.default,
40
37
  },
41
38
  };
42
39
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/eslint-rules/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AAEH,8FAA4D;AAC5D,sGAAoE;AACpE,wFAAsD;AACtD,oFAAkD;AAClD,gGAA+D;AAC/D,sHAAkF;AAClF,oGAAkE;AAClE,sFAAmD;AACnD,gHAA4E;AAE5E,iBAAS;IACL,KAAK,EAAE;QACH,qBAAqB,EAAE,6BAAiB;QACxC,yBAAyB,EAAE,iCAAqB;QAChD,kBAAkB,EAAE,0BAAc;QAClC,gBAAgB,EAAE,wBAAY;QAC9B,sBAAsB,EAAE,8BAAmB;QAC3C,iCAAiC,EAAE,yCAA2B;QAC9D,wBAAwB,EAAE,gCAAoB;QAC9C,iBAAiB,EAAE,yBAAY;QAC/B,8BAA8B,EAAE,sCAAwB;KAC3D;CACJ,CAAC","sourcesContent":["/**\n * ESLint plugin for WebPieces\n * Provides rules for enforcing WebPieces code patterns\n *\n * This plugin is automatically included in @webpieces/nx-webpieces-rules\n *\n * Available rules:\n * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)\n * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)\n * - max-method-lines: Enforce maximum method length (default: 70 lines)\n * - max-file-lines: Enforce maximum file length (default: 700 lines)\n * - enforce-architecture: Enforce architecture dependency boundaries\n * - no-json-property-primitive-type: Ban @JsonProperty({ type: String/Number/Boolean })\n * - require-typed-template: Require [templateClassType] on ng-template with let- variables (Angular)\n * - no-mat-cell-def: Ban *matCellDef/*matHeaderCellDef — use div-grid tables (Angular)\n * - no-process-exit-outside-main: process.exit only in main()/runMain; never import another module's main\n */\n\nimport catchErrorPattern from './rules/catch-error-pattern';\nimport noUnmanagedExceptions from './rules/no-unmanaged-exceptions';\nimport maxMethodLines from './rules/max-method-lines';\nimport maxFileLines from './rules/max-file-lines';\nimport enforceArchitecture from './rules/enforce-architecture';\nimport noJsonPropertyPrimitiveType from './rules/no-json-property-primitive-type';\nimport requireTypedTemplate from './rules/require-typed-template';\nimport noMatCellDef from './rules/no-mat-cell-def';\nimport noProcessExitOutsideMain from './rules/no-process-exit-outside-main';\n\nexport = {\n rules: {\n 'catch-error-pattern': catchErrorPattern,\n 'no-unmanaged-exceptions': noUnmanagedExceptions,\n 'max-method-lines': maxMethodLines,\n 'max-file-lines': maxFileLines,\n 'enforce-architecture': enforceArchitecture,\n 'no-json-property-primitive-type': noJsonPropertyPrimitiveType,\n 'require-typed-template': requireTypedTemplate,\n 'no-mat-cell-def': noMatCellDef,\n 'no-process-exit-outside-main': noProcessExitOutsideMain,\n },\n};\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/eslint-rules/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;AAEH,8FAA4D;AAC5D,sGAAoE;AACpE,wFAAsD;AACtD,oFAAkD;AAClD,gGAA+D;AAC/D,sHAAkF;AAClF,oGAAkE;AAClE,sFAAmD;AAEnD,iBAAS;IACL,KAAK,EAAE;QACH,qBAAqB,EAAE,6BAAiB;QACxC,yBAAyB,EAAE,iCAAqB;QAChD,kBAAkB,EAAE,0BAAc;QAClC,gBAAgB,EAAE,wBAAY;QAC9B,sBAAsB,EAAE,8BAAmB;QAC3C,iCAAiC,EAAE,yCAA2B;QAC9D,wBAAwB,EAAE,gCAAoB;QAC9C,iBAAiB,EAAE,yBAAY;KAClC;CACJ,CAAC","sourcesContent":["/**\n * ESLint plugin for WebPieces\n * Provides rules for enforcing WebPieces code patterns\n *\n * This plugin is automatically included in @webpieces/nx-webpieces-rules\n *\n * Available rules:\n * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)\n * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)\n * - max-method-lines: Enforce maximum method length (default: 70 lines)\n * - max-file-lines: Enforce maximum file length (default: 700 lines)\n * - enforce-architecture: Enforce architecture dependency boundaries\n * - no-json-property-primitive-type: Ban @JsonProperty({ type: String/Number/Boolean })\n * - require-typed-template: Require [templateClassType] on ng-template with let- variables (Angular)\n * - no-mat-cell-def: Ban *matCellDef/*matHeaderCellDef — use div-grid tables (Angular)\n */\n\nimport catchErrorPattern from './rules/catch-error-pattern';\nimport noUnmanagedExceptions from './rules/no-unmanaged-exceptions';\nimport maxMethodLines from './rules/max-method-lines';\nimport maxFileLines from './rules/max-file-lines';\nimport enforceArchitecture from './rules/enforce-architecture';\nimport noJsonPropertyPrimitiveType from './rules/no-json-property-primitive-type';\nimport requireTypedTemplate from './rules/require-typed-template';\nimport noMatCellDef from './rules/no-mat-cell-def';\n\nexport = {\n rules: {\n 'catch-error-pattern': catchErrorPattern,\n 'no-unmanaged-exceptions': noUnmanagedExceptions,\n 'max-method-lines': maxMethodLines,\n 'max-file-lines': maxFileLines,\n 'enforce-architecture': enforceArchitecture,\n 'no-json-property-primitive-type': noJsonPropertyPrimitiveType,\n 'require-typed-template': requireTypedTemplate,\n 'no-mat-cell-def': noMatCellDef,\n },\n};\n"]}
@@ -1,24 +0,0 @@
1
- /**
2
- * ESLint rule: no-process-exit-outside-main
3
- *
4
- * Enforces the codebase invariant: `process.exit` may appear ONLY inside a function named `main`
5
- * (a bin's terminal boundary) or inside the shared `runMain` wrapper. Everywhere else — especially a
6
- * library function that another module imports and calls — a `process.exit` silently kills the PARENT
7
- * process. That is exactly the bug this rule prevents: `git-gatherInfo`'s `main()` called
8
- * `process.exit(0)` while imported by `merge-start`, so it killed `wp-start-upsert-pr` mid-flow (with
9
- * a SUCCESS code) and push + build were skipped. Library code must instead `throw CliExitError(code,
10
- * msg)` and let `main()` / `runMain` translate it into the single sanctioned exit.
11
- *
12
- * Two checks:
13
- * 1. `process.exit(...)` outside a `main`/`runMain` function → error (throw CliExitError instead).
14
- * 2. `import { main }` / `import { main as X }` from another module → error. `main` is a bin entry
15
- * point, not a library export; importing it is what let a library call another module's exiting
16
- * `main`. Call a named function; each bin owns its own thin `main()` + `runMain` wrapper.
17
- *
18
- * Genuine terminal boundaries that are not named `main` (a server bootstrap, a hook's exit-code
19
- * protocol) use an inline `// eslint-disable-next-line @webpieces/no-process-exit-outside-main --
20
- * <reason>` with a justification.
21
- */
22
- import type { Rule } from 'eslint';
23
- declare const rule: Rule.RuleModule;
24
- export = rule;
@@ -1,99 +0,0 @@
1
- "use strict";
2
- /**
3
- * ESLint rule: no-process-exit-outside-main
4
- *
5
- * Enforces the codebase invariant: `process.exit` may appear ONLY inside a function named `main`
6
- * (a bin's terminal boundary) or inside the shared `runMain` wrapper. Everywhere else — especially a
7
- * library function that another module imports and calls — a `process.exit` silently kills the PARENT
8
- * process. That is exactly the bug this rule prevents: `git-gatherInfo`'s `main()` called
9
- * `process.exit(0)` while imported by `merge-start`, so it killed `wp-start-upsert-pr` mid-flow (with
10
- * a SUCCESS code) and push + build were skipped. Library code must instead `throw CliExitError(code,
11
- * msg)` and let `main()` / `runMain` translate it into the single sanctioned exit.
12
- *
13
- * Two checks:
14
- * 1. `process.exit(...)` outside a `main`/`runMain` function → error (throw CliExitError instead).
15
- * 2. `import { main }` / `import { main as X }` from another module → error. `main` is a bin entry
16
- * point, not a library export; importing it is what let a library call another module's exiting
17
- * `main`. Call a named function; each bin owns its own thin `main()` + `runMain` wrapper.
18
- *
19
- * Genuine terminal boundaries that are not named `main` (a server bootstrap, a hook's exit-code
20
- * protocol) use an inline `// eslint-disable-next-line @webpieces/no-process-exit-outside-main --
21
- * <reason>` with a justification.
22
- */
23
- const ALLOWED_FUNCTION_NAMES = ['main', 'runMain'];
24
- const rule = {
25
- meta: {
26
- type: 'problem',
27
- docs: {
28
- description: 'process.exit() only inside main()/runMain; never import another module\'s main',
29
- },
30
- messages: {
31
- noProcessExit: 'Do not call process.exit() here. Only a function named `main` (or the shared `runMain` ' +
32
- 'wrapper) may exit the process — library code that another module calls would otherwise ' +
33
- 'silently kill the parent process. Throw `CliExitError(code, message)` and let main()/runMain ' +
34
- 'translate it. Genuine terminal boundary? Add `// eslint-disable-next-line ' +
35
- '@webpieces/no-process-exit-outside-main -- <reason>`.',
36
- noImportMain: 'Do not import another module\'s `main`. `main` is a bin entry point, not a library export; ' +
37
- 'importing it is what lets library code call an exiting `main` and kill the parent process. ' +
38
- 'Export and import a named function instead, and give each bin its own thin main()/runMain wrapper.',
39
- },
40
- schema: [],
41
- },
42
- create(context) {
43
- // Listener params are typed with our minimal AstNode; ESLint passes ESTree nodes, so the whole
44
- // listener object is cast to Rule.RuleListener once at the return (rather than per-node).
45
- const listener = {
46
- CallExpression(node) {
47
- // Match `process.exit(...)` exactly (not computed, not some other object).
48
- const callee = node.callee;
49
- if (!callee || callee.type !== 'MemberExpression' || callee.computed)
50
- return;
51
- const obj = callee.object;
52
- const prop = callee.property;
53
- const isExit = obj.type === 'Identifier' && obj.name === 'process' && prop.type === 'Identifier' && prop.name === 'exit';
54
- if (!isExit)
55
- return;
56
- // Allow only when lexically inside a function named `main` or `runMain`.
57
- // webpieces-disable no-any-unknown -- bridge our loose AstNode to ESLint's Rule.Node for the ancestor API.
58
- const ancestors = context.sourceCode.getAncestors(node);
59
- let allowed = false;
60
- for (const anc of ancestors) {
61
- if (anc.type !== 'FunctionDeclaration' && anc.type !== 'FunctionExpression' && anc.type !== 'ArrowFunctionExpression') {
62
- continue;
63
- }
64
- // Name: `function main()` (declaration id) or `const main = () => {}` (declarator).
65
- let name = null;
66
- if (anc.id && typeof anc.id.name === 'string') {
67
- name = anc.id.name;
68
- }
69
- else {
70
- const parent = anc.parent;
71
- if (parent && parent.type === 'VariableDeclarator' && parent.id && typeof parent.id.name === 'string') {
72
- name = parent.id.name;
73
- }
74
- }
75
- if (name !== null && ALLOWED_FUNCTION_NAMES.includes(name)) {
76
- allowed = true;
77
- break;
78
- }
79
- }
80
- if (allowed)
81
- return;
82
- // webpieces-disable no-any-unknown -- ESTree AST cast for ESLint report.
83
- context.report({ node: node, messageId: 'noProcessExit' });
84
- },
85
- ImportDeclaration(node) {
86
- for (const spec of node.specifiers) {
87
- if (spec.type === 'ImportSpecifier' && spec.imported && spec.imported.name === 'main') {
88
- // webpieces-disable no-any-unknown -- ESTree AST cast for ESLint report.
89
- context.report({ node: spec, messageId: 'noImportMain' });
90
- }
91
- }
92
- },
93
- };
94
- // webpieces-disable no-any-unknown -- our AstNode listener params bridge to ESLint's node types.
95
- return listener;
96
- },
97
- };
98
- module.exports = rule;
99
- //# sourceMappingURL=no-process-exit-outside-main.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"no-process-exit-outside-main.js","sourceRoot":"","sources":["../../../../../../packages/tooling/eslint-rules/src/rules/no-process-exit-outside-main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ESLint rule: no-process-exit-outside-main\n *\n * Enforces the codebase invariant: `process.exit` may appear ONLY inside a function named `main`\n * (a bin's terminal boundary) or inside the shared `runMain` wrapper. Everywhere else — especially a\n * library function that another module imports and calls — a `process.exit` silently kills the PARENT\n * process. That is exactly the bug this rule prevents: `git-gatherInfo`'s `main()` called\n * `process.exit(0)` while imported by `merge-start`, so it killed `wp-start-upsert-pr` mid-flow (with\n * a SUCCESS code) and push + build were skipped. Library code must instead `throw CliExitError(code,\n * msg)` and let `main()` / `runMain` translate it into the single sanctioned exit.\n *\n * Two checks:\n * 1. `process.exit(...)` outside a `main`/`runMain` function → error (throw CliExitError instead).\n * 2. `import { main }` / `import { main as X }` from another module → error. `main` is a bin entry\n * point, not a library export; importing it is what let a library call another module's exiting\n * `main`. Call a named function; each bin owns its own thin `main()` + `runMain` wrapper.\n *\n * Genuine terminal boundaries that are not named `main` (a server bootstrap, a hook's exit-code\n * protocol) use an inline `// eslint-disable-next-line @webpieces/no-process-exit-outside-main --\n * <reason>` with a justification.\n */\n\nimport type { Rule } from 'eslint';\n\n// Minimal structural view of the ESTree/ESLint AST nodes this rule inspects — only the fields we\n// read, declared as named properties (no index signature) so access stays type-checked.\ninterface AstNode {\n type: string;\n name?: string;\n computed?: boolean;\n callee?: AstNode;\n object?: AstNode;\n property?: AstNode;\n id?: AstNode;\n parent?: AstNode;\n imported?: AstNode;\n specifiers?: AstNode[];\n}\n\nconst ALLOWED_FUNCTION_NAMES = ['main', 'runMain'];\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'process.exit() only inside main()/runMain; never import another module\\'s main',\n },\n messages: {\n noProcessExit:\n 'Do not call process.exit() here. Only a function named `main` (or the shared `runMain` ' +\n 'wrapper) may exit the process — library code that another module calls would otherwise ' +\n 'silently kill the parent process. Throw `CliExitError(code, message)` and let main()/runMain ' +\n 'translate it. Genuine terminal boundary? Add `// eslint-disable-next-line ' +\n '@webpieces/no-process-exit-outside-main -- <reason>`.',\n noImportMain:\n 'Do not import another module\\'s `main`. `main` is a bin entry point, not a library export; ' +\n 'importing it is what lets library code call an exiting `main` and kill the parent process. ' +\n 'Export and import a named function instead, and give each bin its own thin main()/runMain wrapper.',\n },\n schema: [],\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n // Listener params are typed with our minimal AstNode; ESLint passes ESTree nodes, so the whole\n // listener object is cast to Rule.RuleListener once at the return (rather than per-node).\n const listener = {\n CallExpression(node: AstNode): void {\n // Match `process.exit(...)` exactly (not computed, not some other object).\n const callee: AstNode | undefined = node.callee as AstNode | undefined;\n if (!callee || callee.type !== 'MemberExpression' || callee.computed) return;\n const obj: AstNode = callee.object as AstNode;\n const prop: AstNode = callee.property as AstNode;\n const isExit = obj.type === 'Identifier' && obj.name === 'process' && prop.type === 'Identifier' && prop.name === 'exit';\n if (!isExit) return;\n\n // Allow only when lexically inside a function named `main` or `runMain`.\n // webpieces-disable no-any-unknown -- bridge our loose AstNode to ESLint's Rule.Node for the ancestor API.\n const ancestors = context.sourceCode.getAncestors(node as unknown as Rule.Node) as unknown as AstNode[];\n let allowed = false;\n for (const anc of ancestors) {\n if (anc.type !== 'FunctionDeclaration' && anc.type !== 'FunctionExpression' && anc.type !== 'ArrowFunctionExpression') {\n continue;\n }\n // Name: `function main()` (declaration id) or `const main = () => {}` (declarator).\n let name: string | null = null;\n if (anc.id && typeof anc.id.name === 'string') {\n name = anc.id.name;\n } else {\n const parent: AstNode | undefined = anc.parent as AstNode | undefined;\n if (parent && parent.type === 'VariableDeclarator' && parent.id && typeof parent.id.name === 'string') {\n name = parent.id.name;\n }\n }\n if (name !== null && ALLOWED_FUNCTION_NAMES.includes(name)) {\n allowed = true;\n break;\n }\n }\n if (allowed) return;\n // webpieces-disable no-any-unknown -- ESTree AST cast for ESLint report.\n context.report({ node: node as unknown as Rule.Node, messageId: 'noProcessExit' });\n },\n ImportDeclaration(node: AstNode): void {\n for (const spec of node.specifiers as AstNode[]) {\n if (spec.type === 'ImportSpecifier' && spec.imported && spec.imported.name === 'main') {\n // webpieces-disable no-any-unknown -- ESTree AST cast for ESLint report.\n context.report({ node: spec as unknown as Rule.Node, messageId: 'noImportMain' });\n }\n }\n },\n };\n // webpieces-disable no-any-unknown -- our AstNode listener params bridge to ESLint's node types.\n return listener as unknown as Rule.RuleListener;\n },\n};\n\nexport = rule;\n"]}