@webpieces/code-rules 0.3.285 → 0.3.286
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 +2 -2
- package/src/index.d.ts +1 -0
- package/src/index.js +3 -1
- package/src/index.js.map +1 -1
- package/src/validate-code.js +2 -0
- package/src/validate-code.js.map +1 -1
- package/src/validate-no-process-exit-outside-main.d.ts +33 -0
- package/src/validate-no-process-exit-outside-main.js +199 -0
- package/src/validate-no-process-exit-outside-main.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/code-rules",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.286",
|
|
4
4
|
"description": "Standalone code validation rules extracted from architecture-validators, no Nx dependency required",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"directory": "packages/tooling/code-rules"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@webpieces/rules-config": "0.3.
|
|
23
|
+
"@webpieces/rules-config": "0.3.286"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|
package/src/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptio
|
|
|
11
11
|
export { NoDestructureValidator } from './validate-no-destructure';
|
|
12
12
|
export { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';
|
|
13
13
|
export { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';
|
|
14
|
+
export { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';
|
|
14
15
|
export { FrameworkTagValidator } from './validate-framework-tag';
|
|
15
16
|
export { RoleTagValidator } from './validate-role-tag';
|
|
16
17
|
export { PrismaValidateDtosValidator } from './validate-dtos';
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
|
|
3
|
+
exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.NoProcessExitOutsideMainValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
var code_validator_1 = require("./code-validator");
|
|
6
6
|
Object.defineProperty(exports, "CodeValidator", { enumerable: true, get: function () { return code_validator_1.CodeValidator; } });
|
|
@@ -29,6 +29,8 @@ var validate_no_direct_api_resolver_1 = require("./validate-no-direct-api-resolv
|
|
|
29
29
|
Object.defineProperty(exports, "NoDirectApiResolverValidator", { enumerable: true, get: function () { return validate_no_direct_api_resolver_1.NoDirectApiResolverValidator; } });
|
|
30
30
|
var validate_no_symbol_di_tokens_1 = require("./validate-no-symbol-di-tokens");
|
|
31
31
|
Object.defineProperty(exports, "NoSymbolDiTokensValidator", { enumerable: true, get: function () { return validate_no_symbol_di_tokens_1.NoSymbolDiTokensValidator; } });
|
|
32
|
+
var validate_no_process_exit_outside_main_1 = require("./validate-no-process-exit-outside-main");
|
|
33
|
+
Object.defineProperty(exports, "NoProcessExitOutsideMainValidator", { enumerable: true, get: function () { return validate_no_process_exit_outside_main_1.NoProcessExitOutsideMainValidator; } });
|
|
32
34
|
var validate_framework_tag_1 = require("./validate-framework-tag");
|
|
33
35
|
Object.defineProperty(exports, "FrameworkTagValidator", { enumerable: true, get: function () { return validate_framework_tag_1.FrameworkTagValidator; } });
|
|
34
36
|
var validate_role_tag_1 = require("./validate-role-tag");
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDAA8D;AAArD,4HAAA,2BAA2B,OAAA;AACpC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA;AACjC,iDAA0D;AAAjD,sIAAA,OAAO,OAAgB","sourcesContent":["export { CodeValidator, ExecutorResult } from './code-validator';\n\nexport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nexport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nexport { MaxMethodLinesValidator, runModifiedMethods } from './validate-modified-methods';\nexport { runNewMethods } from './validate-new-methods';\nexport { MaxFileLinesValidator } from './validate-modified-files';\nexport { RequireReturnTypeValidator } from './validate-return-types';\nexport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nexport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nexport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nexport { NoDestructureValidator } from './validate-no-destructure';\nexport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nexport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,iGAA4F;AAAnF,0JAAA,iCAAiC,OAAA;AAC1C,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDAA8D;AAArD,4HAAA,2BAA2B,OAAA;AACpC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA;AACjC,iDAA0D;AAAjD,sIAAA,OAAO,OAAgB","sourcesContent":["export { CodeValidator, ExecutorResult } from './code-validator';\n\nexport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nexport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nexport { MaxMethodLinesValidator, runModifiedMethods } from './validate-modified-methods';\nexport { runNewMethods } from './validate-new-methods';\nexport { MaxFileLinesValidator } from './validate-modified-files';\nexport { RequireReturnTypeValidator } from './validate-return-types';\nexport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nexport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nexport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nexport { NoDestructureValidator } from './validate-no-destructure';\nexport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nexport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nexport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n"]}
|
package/src/validate-code.js
CHANGED
|
@@ -16,6 +16,7 @@ const validate_catch_error_pattern_1 = require("./validate-catch-error-pattern")
|
|
|
16
16
|
const validate_no_unmanaged_exceptions_1 = require("./validate-no-unmanaged-exceptions");
|
|
17
17
|
const validate_no_direct_api_resolver_1 = require("./validate-no-direct-api-resolver");
|
|
18
18
|
const validate_no_symbol_di_tokens_1 = require("./validate-no-symbol-di-tokens");
|
|
19
|
+
const validate_no_process_exit_outside_main_1 = require("./validate-no-process-exit-outside-main");
|
|
19
20
|
const validate_framework_tag_1 = require("./validate-framework-tag");
|
|
20
21
|
const validate_role_tag_1 = require("./validate-role-tag");
|
|
21
22
|
const validate_match_rules_1 = require("./validate-match-rules");
|
|
@@ -41,6 +42,7 @@ function buildValidators(config, matchRules) {
|
|
|
41
42
|
new validate_no_unmanaged_exceptions_1.NoUnmanagedExceptionsValidator(config['no-unmanaged-exceptions'] ?? new rules_config_1.NoUnmanagedExceptionsConfig()),
|
|
42
43
|
new validate_no_direct_api_resolver_1.NoDirectApiResolverValidator(config['angular-no-direct-api-in-resolver'] ?? new rules_config_1.AngularNoDirectApiInResolverConfig()),
|
|
43
44
|
new validate_no_symbol_di_tokens_1.NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new rules_config_1.NoSymbolDiTokensConfig()),
|
|
45
|
+
new validate_no_process_exit_outside_main_1.NoProcessExitOutsideMainValidator(config['no-process-exit-outside-main'] ?? new rules_config_1.NoProcessExitOutsideMainConfig()),
|
|
44
46
|
new validate_framework_tag_1.FrameworkTagValidator(config['framework-tag'] ?? new rules_config_1.FrameworkTagConfig()),
|
|
45
47
|
new validate_role_tag_1.RoleTagValidator(config['role-tag'] ?? new rules_config_1.RoleTagConfig()),
|
|
46
48
|
// One validator per client-authored match-rules entry (each with its own name/mode/epoch).
|
package/src/validate-code.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-code.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-code.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"validate-code.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-code.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n loadAndValidate,\n WebpiecesRulesConfig,\n BaseRuleConfig,\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n CatchErrorPatternConfig,\n NoUnmanagedExceptionsConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoProcessExitOutsideMainConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n} from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { runValidators } from './rule-reporter';\nimport { MaxMethodLinesValidator } from './validate-modified-methods';\nimport { MaxFileLinesValidator } from './validate-modified-files';\nimport { RequireReturnTypeValidator } from './validate-return-types';\nimport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nimport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nimport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nimport { PrismaValidateDtosValidator } from './validate-dtos';\nimport { PrismaConverterValidator } from './validate-prisma-converters';\nimport { NoDestructureValidator } from './validate-no-destructure';\nimport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nimport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nimport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nimport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nimport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nimport { FrameworkTagValidator } from './validate-framework-tag';\nimport { RoleTagValidator } from './validate-role-tag';\nimport { MatchRulesValidator } from './validate-match-rules';\nimport { MatchRuleConfig } from '@webpieces/rules-config';\n\nexport { ExecutorResult } from './code-validator';\n\n/**\n * Build every code validator from the typed WebpiecesRulesConfig. Each validator is\n * constructed with its own `*Config` (the genuinely-consumed config class), so the\n * config types are exercised at compile time. Missing rule keys fall back to an empty\n * config instance — `loadAndValidate` already validates that configured rules\n * carry an explicit `mode`, so in practice every key is present.\n */\nfunction buildValidators(\n config: WebpiecesRulesConfig,\n matchRules: readonly MatchRuleConfig[],\n): CodeValidator<BaseRuleConfig>[] {\n return [\n new MaxMethodLinesValidator(config['max-method-lines'] ?? new MaxMethodLinesConfig()),\n new MaxFileLinesValidator(config['max-file-lines'] ?? new MaxFileLinesConfig()),\n new RequireReturnTypeValidator(config['require-return-type'] ?? new RequireReturnTypeConfig()),\n new NoInlineTypeLiteralsValidator(config['no-inline-type-literals'] ?? new NoInlineTypeLiteralsConfig()),\n new NoAnyUnknownValidator(config['no-any-unknown'] ?? new NoAnyUnknownConfig()),\n new NoImplicitAnyValidator(config['no-implicit-any'] ?? new NoImplicitAnyConfig()),\n new PrismaValidateDtosValidator(config['prisma-validate-dtos'] ?? new PrismaValidateDtosConfig()),\n new PrismaConverterValidator(config['prisma-converter'] ?? new PrismaConverterConfig()),\n new NoDestructureValidator(config['no-destructure'] ?? new NoDestructureConfig()),\n new CatchErrorPatternValidator(config['catch-error-pattern'] ?? new CatchErrorPatternConfig()),\n new NoUnmanagedExceptionsValidator(config['no-unmanaged-exceptions'] ?? new NoUnmanagedExceptionsConfig()),\n new NoDirectApiResolverValidator(config['angular-no-direct-api-in-resolver'] ?? new AngularNoDirectApiInResolverConfig()),\n new NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new NoSymbolDiTokensConfig()),\n new NoProcessExitOutsideMainValidator(config['no-process-exit-outside-main'] ?? new NoProcessExitOutsideMainConfig()),\n new FrameworkTagValidator(config['framework-tag'] ?? new FrameworkTagConfig()),\n new RoleTagValidator(config['role-tag'] ?? new RoleTagConfig()),\n // One validator per client-authored match-rules entry (each with its own name/mode/epoch).\n ...matchRules.map((mr: MatchRuleConfig) => new MatchRulesValidator(mr)),\n ];\n}\n\n/**\n * Run all configured code validators against the workspace.\n *\n * Owns config loading: callers just pass the workspace root. Config comes from\n * webpieces.config.json (loaded via @webpieces/rules-config so ai-hooks and this\n * executor agree on every rule's enabled/mode/options). A validator runs only when\n * `shouldRun()` is true (i.e. its mode is not OFF and no branch/epoch escape hatch matches).\n */\nexport default async function runValidator(workspaceRoot: string): Promise<ExecutorResult> {\n const loaded = loadAndValidate(workspaceRoot);\n if (loaded.configPath === null) {\n console.error('\\n❌ No webpieces.config.json found at workspace root (or any ancestor).\\n');\n return { success: false };\n }\n\n console.log(`\\n📄 Loaded config: ${loaded.configPath}`);\n\n const validators = buildValidators(loaded.rulesConfig, loaded.matchRules);\n const active = validators.filter((v: CodeValidator<BaseRuleConfig>) => v.shouldRun());\n\n if (active.length === 0) {\n console.log('\\n⏭️ Skipping all code validations (all modes: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Active rules: ${active.map((v: CodeValidator<BaseRuleConfig>) => v.name).join(', ')}`);\n console.log('');\n\n // Per-validator isolation lives in runValidators: a bug in one validator no longer aborts the\n // rest, and a thrown RuleFailError is reported like any other failure.\n const result = await runValidators(active, workspaceRoot);\n const allSuccess = result.success;\n\n console.log(allSuccess ? '\\n✅ All code validations passed\\n' : '\\n❌ Some code validations failed\\n');\n return { success: allSuccess };\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate No Process Exit Outside Main
|
|
3
|
+
*
|
|
4
|
+
* Flags `process.exit(...)` outside a `main()`/`runMain` wrapper, and `import { main }` from another
|
|
5
|
+
* module. A `process.exit` deep in a reused call stack crashes a server or command far too early and
|
|
6
|
+
* unexpectedly — and with exit 0 it masquerades as success. Throw a semantic error instead and let
|
|
7
|
+
* the ONE top-level `main()`/`runMain` try-catch pick the exit code.
|
|
8
|
+
*
|
|
9
|
+
* See .webpieces/instruct-ai/webpieces.noexitinmain.md (generated on violation) for the full pattern.
|
|
10
|
+
*
|
|
11
|
+
* ALLOWED
|
|
12
|
+
* - A `process.exit` whose nearest enclosing function is named `main` or `runMain`.
|
|
13
|
+
* - Test files (*.test.ts, *.spec.ts, __tests__/**).
|
|
14
|
+
* - Lines with `// webpieces-disable no-process-exit-outside-main -- <reason>` (when disableAllowed).
|
|
15
|
+
*
|
|
16
|
+
* MODES (LINE-BASED)
|
|
17
|
+
* - OFF: Skip.
|
|
18
|
+
* - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).
|
|
19
|
+
* - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.
|
|
20
|
+
*/
|
|
21
|
+
import { NoProcessExitOutsideMainConfig } from '@webpieces/rules-config';
|
|
22
|
+
import { CodeValidator, ExecutorResult } from './code-validator';
|
|
23
|
+
interface ExitViolationInfo {
|
|
24
|
+
line: number;
|
|
25
|
+
context: string;
|
|
26
|
+
hasDisableComment: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function findExitViolationsInFile(filePath: string, workspaceRoot: string, disableAllowed: boolean): ExitViolationInfo[];
|
|
29
|
+
export declare class NoProcessExitOutsideMainValidator extends CodeValidator<NoProcessExitOutsideMainConfig> {
|
|
30
|
+
constructor(config: NoProcessExitOutsideMainConfig);
|
|
31
|
+
run(workspaceRoot: string): Promise<ExecutorResult>;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validate No Process Exit Outside Main
|
|
4
|
+
*
|
|
5
|
+
* Flags `process.exit(...)` outside a `main()`/`runMain` wrapper, and `import { main }` from another
|
|
6
|
+
* module. A `process.exit` deep in a reused call stack crashes a server or command far too early and
|
|
7
|
+
* unexpectedly — and with exit 0 it masquerades as success. Throw a semantic error instead and let
|
|
8
|
+
* the ONE top-level `main()`/`runMain` try-catch pick the exit code.
|
|
9
|
+
*
|
|
10
|
+
* See .webpieces/instruct-ai/webpieces.noexitinmain.md (generated on violation) for the full pattern.
|
|
11
|
+
*
|
|
12
|
+
* ALLOWED
|
|
13
|
+
* - A `process.exit` whose nearest enclosing function is named `main` or `runMain`.
|
|
14
|
+
* - Test files (*.test.ts, *.spec.ts, __tests__/**).
|
|
15
|
+
* - Lines with `// webpieces-disable no-process-exit-outside-main -- <reason>` (when disableAllowed).
|
|
16
|
+
*
|
|
17
|
+
* MODES (LINE-BASED)
|
|
18
|
+
* - OFF: Skip.
|
|
19
|
+
* - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).
|
|
20
|
+
* - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.NoProcessExitOutsideMainValidator = void 0;
|
|
24
|
+
exports.findExitViolationsInFile = findExitViolationsInFile;
|
|
25
|
+
const tslib_1 = require("tslib");
|
|
26
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
27
|
+
const path = tslib_1.__importStar(require("path"));
|
|
28
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
29
|
+
const code_validator_1 = require("./code-validator");
|
|
30
|
+
const resolve_mode_1 = require("./resolve-mode");
|
|
31
|
+
const INSTRUCT_FILE = 'webpieces.noexitinmain.md';
|
|
32
|
+
const EXIT_REGEX = /\bprocess\.exit\s*\(/;
|
|
33
|
+
const IMPORT_MAIN_REGEX = /^\s*import\b[^;]*\{[^}]*\bmain\b[^}]*\}[^;]*\bfrom\b/;
|
|
34
|
+
// Named function / arrow-const DEFINITION openers (never a call like `main()`), used to find the
|
|
35
|
+
// enclosing function's name via a backward scan.
|
|
36
|
+
const FUNC_DEF_REGEX = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(|(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?(?:\(|function\b)/;
|
|
37
|
+
const ALLOWED_FN = new Set(['main', 'runMain']);
|
|
38
|
+
const SHARED_MESSAGE = `Do not call process.exit() deep in the stack — any code may be reused, and a deep exit crashes a
|
|
39
|
+
server or command far too early (exit 0 even masquerades as success). Throw a semantic error instead:
|
|
40
|
+
- Expected/normal failure → your domain's failure error (in the rules engine: RuleFailError).
|
|
41
|
+
- A bug / broken precondition (should-never-happen, missing required var) → an ordinary Error.
|
|
42
|
+
Let the ONE top-level main()/runMain try-catch pick the exit code (non-zero on failure, 0 on success):
|
|
43
|
+
if (require.main === module) runMain(main)
|
|
44
|
+
Need a specific exit code deep down? throw new CliExitError(code, message) — runMain maps it.
|
|
45
|
+
Also: do NOT import another module's main — call a named function; only a thin wrapper calls main.
|
|
46
|
+
READ .webpieces/instruct-ai/webpieces.noexitinmain.md for the full pattern.
|
|
47
|
+
Last resort: append // webpieces-disable no-process-exit-outside-main -- <reason> at a genuine terminal boundary.`;
|
|
48
|
+
function isTestFile(filePath) {
|
|
49
|
+
return filePath.includes('.spec.ts') || filePath.includes('.test.ts') || filePath.includes('__tests__/');
|
|
50
|
+
}
|
|
51
|
+
function stripLineComments(line) {
|
|
52
|
+
const idx = line.indexOf('//');
|
|
53
|
+
if (idx === -1)
|
|
54
|
+
return line;
|
|
55
|
+
return line.substring(0, idx);
|
|
56
|
+
}
|
|
57
|
+
// Walk backward from `idx` for the nearest NAMED function/arrow definition and return its name.
|
|
58
|
+
// Line-based heuristic (no AST): recognises a `main`/`runMain` wrapper enclosing the exit.
|
|
59
|
+
function enclosingFunctionName(strippedLines, idx) {
|
|
60
|
+
for (let j = idx; j >= 0; j -= 1) {
|
|
61
|
+
const m = FUNC_DEF_REGEX.exec(strippedLines[j] ?? '');
|
|
62
|
+
if (m)
|
|
63
|
+
return m[1] ?? m[2] ?? null;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function resolveDisable(disabled, disableAllowed) {
|
|
68
|
+
if (!disableAllowed && disabled)
|
|
69
|
+
return false;
|
|
70
|
+
return disabled;
|
|
71
|
+
}
|
|
72
|
+
function findExitViolationsInFile(filePath, workspaceRoot, disableAllowed) {
|
|
73
|
+
if (isTestFile(filePath))
|
|
74
|
+
return [];
|
|
75
|
+
const fullPath = path.join(workspaceRoot, filePath);
|
|
76
|
+
if (!fs.existsSync(fullPath))
|
|
77
|
+
return [];
|
|
78
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
79
|
+
const lines = content.split('\n');
|
|
80
|
+
const stripped = lines.map((l) => stripLineComments(l));
|
|
81
|
+
const violations = [];
|
|
82
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
83
|
+
const line = lines[i] ?? '';
|
|
84
|
+
const strippedLine = stripped[i] ?? '';
|
|
85
|
+
const isExit = EXIT_REGEX.test(strippedLine) && !ALLOWED_FN.has(enclosingFunctionName(stripped, i) ?? '');
|
|
86
|
+
const isImportMain = IMPORT_MAIN_REGEX.test(strippedLine);
|
|
87
|
+
if (!isExit && !isImportMain)
|
|
88
|
+
continue;
|
|
89
|
+
const lineNum = i + 1;
|
|
90
|
+
const prevLine = i > 0 ? (lines[i - 1] ?? '') : '';
|
|
91
|
+
const disabled = (0, rules_config_1.hasDisable)(line, rules_config_1.RULE_NAMES.NO_PROCESS_EXIT_OUTSIDE_MAIN) || (0, rules_config_1.hasDisable)(prevLine, rules_config_1.RULE_NAMES.NO_PROCESS_EXIT_OUTSIDE_MAIN);
|
|
92
|
+
violations.push({ line: lineNum, context: line.trim(), hasDisableComment: resolveDisable(disabled, disableAllowed) });
|
|
93
|
+
}
|
|
94
|
+
return violations;
|
|
95
|
+
}
|
|
96
|
+
function findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed) {
|
|
97
|
+
const violations = [];
|
|
98
|
+
for (const file of changedFiles) {
|
|
99
|
+
const changedLines = (0, rules_config_1.getChangedLineNumbers)((0, rules_config_1.getFileDiff)(workspaceRoot, file, base, head));
|
|
100
|
+
if (changedLines.size === 0)
|
|
101
|
+
continue;
|
|
102
|
+
for (const v of findExitViolationsInFile(file, workspaceRoot, disableAllowed)) {
|
|
103
|
+
if (disableAllowed && v.hasDisableComment)
|
|
104
|
+
continue;
|
|
105
|
+
if (!changedLines.has(v.line))
|
|
106
|
+
continue;
|
|
107
|
+
violations.push({ file, line: v.line, context: v.context });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return violations;
|
|
111
|
+
}
|
|
112
|
+
function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed) {
|
|
113
|
+
const violations = [];
|
|
114
|
+
for (const file of changedFiles) {
|
|
115
|
+
for (const v of findExitViolationsInFile(file, workspaceRoot, disableAllowed)) {
|
|
116
|
+
if (disableAllowed && v.hasDisableComment)
|
|
117
|
+
continue;
|
|
118
|
+
violations.push({ file, line: v.line, context: v.context });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return violations;
|
|
122
|
+
}
|
|
123
|
+
function reportViolations(workspaceRoot, violations, mode, disableAllowed) {
|
|
124
|
+
(0, rules_config_1.writeTemplateIfMissing)(workspaceRoot, INSTRUCT_FILE);
|
|
125
|
+
console.error('');
|
|
126
|
+
console.error('❌ process.exit() outside main()/runMain (or an import of another module\'s main)!');
|
|
127
|
+
console.error('');
|
|
128
|
+
console.error(SHARED_MESSAGE);
|
|
129
|
+
console.error('');
|
|
130
|
+
for (const v of violations) {
|
|
131
|
+
console.error(` ❌ ${v.file}:${v.line}`);
|
|
132
|
+
console.error(` ${v.context}`);
|
|
133
|
+
}
|
|
134
|
+
console.error('');
|
|
135
|
+
console.error(disableAllowed
|
|
136
|
+
? ' Escape hatch (genuine terminal boundary only): // webpieces-disable no-process-exit-outside-main -- <reason>'
|
|
137
|
+
: ' Escape hatch: DISABLED (disableAllowed: false)');
|
|
138
|
+
console.error(`\n Current mode: ${mode}\n`);
|
|
139
|
+
}
|
|
140
|
+
function resolveMode(normalMode, epoch, branchPattern) {
|
|
141
|
+
if (normalMode === 'OFF')
|
|
142
|
+
return normalMode;
|
|
143
|
+
const skip = (0, resolve_mode_1.shouldSkipRule)(epoch, branchPattern);
|
|
144
|
+
if (skip.skip) {
|
|
145
|
+
console.log(`\n⏭️ Skipping no-process-exit-outside-main validation (${skip.reason})\n`);
|
|
146
|
+
return 'OFF';
|
|
147
|
+
}
|
|
148
|
+
return normalMode;
|
|
149
|
+
}
|
|
150
|
+
async function runValidatorImpl(options, workspaceRoot) {
|
|
151
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);
|
|
152
|
+
const disableAllowed = options.disableAllowed ?? true;
|
|
153
|
+
if (mode === 'OFF') {
|
|
154
|
+
console.log('\n⏭️ Skipping no-process-exit-outside-main validation (mode: OFF)\n');
|
|
155
|
+
return { success: true };
|
|
156
|
+
}
|
|
157
|
+
console.log('\n📏 Validating No Process Exit Outside Main\n');
|
|
158
|
+
console.log(` Mode: ${mode}`);
|
|
159
|
+
let base = process.env['NX_BASE'];
|
|
160
|
+
const head = process.env['NX_HEAD'];
|
|
161
|
+
if (!base) {
|
|
162
|
+
base = (0, rules_config_1.detectBase)(workspaceRoot) ?? undefined;
|
|
163
|
+
if (!base) {
|
|
164
|
+
console.log('\n⏭️ Skipping no-process-exit-outside-main validation (could not detect base branch)\n');
|
|
165
|
+
return { success: true };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log(` Base: ${base}`);
|
|
169
|
+
console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}\n`);
|
|
170
|
+
const changedFiles = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, head);
|
|
171
|
+
if (changedFiles.length === 0) {
|
|
172
|
+
console.log('✅ No TypeScript files changed');
|
|
173
|
+
return { success: true };
|
|
174
|
+
}
|
|
175
|
+
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
176
|
+
let violations = [];
|
|
177
|
+
if (mode === 'NEW_AND_MODIFIED_CODE') {
|
|
178
|
+
violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed);
|
|
179
|
+
}
|
|
180
|
+
else if (mode === 'NEW_AND_MODIFIED_FILES') {
|
|
181
|
+
violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);
|
|
182
|
+
}
|
|
183
|
+
if (violations.length === 0) {
|
|
184
|
+
console.log('✅ No process.exit-outside-main violations found');
|
|
185
|
+
return { success: true };
|
|
186
|
+
}
|
|
187
|
+
reportViolations(workspaceRoot, violations, mode, disableAllowed);
|
|
188
|
+
return { success: false };
|
|
189
|
+
}
|
|
190
|
+
class NoProcessExitOutsideMainValidator extends code_validator_1.CodeValidator {
|
|
191
|
+
constructor(config) {
|
|
192
|
+
super(config, 'no-process-exit-outside-main');
|
|
193
|
+
}
|
|
194
|
+
async run(workspaceRoot) {
|
|
195
|
+
return runValidatorImpl(this.config, workspaceRoot);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
exports.NoProcessExitOutsideMainValidator = NoProcessExitOutsideMainValidator;
|
|
199
|
+
//# sourceMappingURL=validate-no-process-exit-outside-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-no-process-exit-outside-main.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-no-process-exit-outside-main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Process Exit Outside Main\n *\n * Flags `process.exit(...)` outside a `main()`/`runMain` wrapper, and `import { main }` from another\n * module. A `process.exit` deep in a reused call stack crashes a server or command far too early and\n * unexpectedly — and with exit 0 it masquerades as success. Throw a semantic error instead and let\n * the ONE top-level `main()`/`runMain` try-catch pick the exit code.\n *\n * See .webpieces/instruct-ai/webpieces.noexitinmain.md (generated on violation) for the full pattern.\n *\n * ALLOWED\n * - A `process.exit` whose nearest enclosing function is named `main` or `runMain`.\n * - Test files (*.test.ts, *.spec.ts, __tests__/**).\n * - Lines with `// webpieces-disable no-process-exit-outside-main -- <reason>` (when disableAllowed).\n *\n * MODES (LINE-BASED)\n * - OFF: Skip.\n * - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).\n * - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { hasDisable, RULE_NAMES, NoProcessExitOutsideMainConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers, writeTemplateIfMissing } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\nconst INSTRUCT_FILE = 'webpieces.noexitinmain.md';\nconst EXIT_REGEX = /\\bprocess\\.exit\\s*\\(/;\nconst IMPORT_MAIN_REGEX = /^\\s*import\\b[^;]*\\{[^}]*\\bmain\\b[^}]*\\}[^;]*\\bfrom\\b/;\n// Named function / arrow-const DEFINITION openers (never a call like `main()`), used to find the\n// enclosing function's name via a backward scan.\nconst FUNC_DEF_REGEX = /(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(|(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?(?:\\(|function\\b)/;\nconst ALLOWED_FN = new Set(['main', 'runMain']);\n\nconst SHARED_MESSAGE = `Do not call process.exit() deep in the stack — any code may be reused, and a deep exit crashes a\nserver or command far too early (exit 0 even masquerades as success). Throw a semantic error instead:\n - Expected/normal failure → your domain's failure error (in the rules engine: RuleFailError).\n - A bug / broken precondition (should-never-happen, missing required var) → an ordinary Error.\nLet the ONE top-level main()/runMain try-catch pick the exit code (non-zero on failure, 0 on success):\n if (require.main === module) runMain(main)\nNeed a specific exit code deep down? throw new CliExitError(code, message) — runMain maps it.\nAlso: do NOT import another module's main — call a named function; only a thin wrapper calls main.\nREAD .webpieces/instruct-ai/webpieces.noexitinmain.md for the full pattern.\nLast resort: append // webpieces-disable no-process-exit-outside-main -- <reason> at a genuine terminal boundary.`;\n\ninterface ExitViolation {\n file: string;\n line: number;\n context: string;\n}\n\ninterface ExitViolationInfo {\n line: number;\n context: string;\n hasDisableComment: boolean;\n}\n\nfunction isTestFile(filePath: string): boolean {\n return filePath.includes('.spec.ts') || filePath.includes('.test.ts') || filePath.includes('__tests__/');\n}\n\nfunction stripLineComments(line: string): string {\n const idx = line.indexOf('//');\n if (idx === -1) return line;\n return line.substring(0, idx);\n}\n\n// Walk backward from `idx` for the nearest NAMED function/arrow definition and return its name.\n// Line-based heuristic (no AST): recognises a `main`/`runMain` wrapper enclosing the exit.\nfunction enclosingFunctionName(strippedLines: string[], idx: number): string | null {\n for (let j = idx; j >= 0; j -= 1) {\n const m = FUNC_DEF_REGEX.exec(strippedLines[j] ?? '');\n if (m) return m[1] ?? m[2] ?? null;\n }\n return null;\n}\n\nfunction resolveDisable(disabled: boolean, disableAllowed: boolean): boolean {\n if (!disableAllowed && disabled) return false;\n return disabled;\n}\n\nexport function findExitViolationsInFile(filePath: string, workspaceRoot: string, disableAllowed: boolean): ExitViolationInfo[] {\n if (isTestFile(filePath)) return [];\n const fullPath = path.join(workspaceRoot, filePath);\n if (!fs.existsSync(fullPath)) return [];\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const lines = content.split('\\n');\n const stripped = lines.map((l: string): string => stripLineComments(l));\n const violations: ExitViolationInfo[] = [];\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i] ?? '';\n const strippedLine = stripped[i] ?? '';\n const isExit = EXIT_REGEX.test(strippedLine) && !ALLOWED_FN.has(enclosingFunctionName(stripped, i) ?? '');\n const isImportMain = IMPORT_MAIN_REGEX.test(strippedLine);\n if (!isExit && !isImportMain) continue;\n\n const lineNum = i + 1;\n const prevLine = i > 0 ? (lines[i - 1] ?? '') : '';\n const disabled = hasDisable(line, RULE_NAMES.NO_PROCESS_EXIT_OUTSIDE_MAIN) || hasDisable(prevLine, RULE_NAMES.NO_PROCESS_EXIT_OUTSIDE_MAIN);\n violations.push({ line: lineNum, context: line.trim(), hasDisableComment: resolveDisable(disabled, disableAllowed) });\n }\n return violations;\n}\n\nfunction findViolationsForModifiedCode(workspaceRoot: string, changedFiles: string[], base: string, head: string | undefined, disableAllowed: boolean): ExitViolation[] {\n const violations: ExitViolation[] = [];\n for (const file of changedFiles) {\n const changedLines = getChangedLineNumbers(getFileDiff(workspaceRoot, file, base, head));\n if (changedLines.size === 0) continue;\n for (const v of findExitViolationsInFile(file, workspaceRoot, disableAllowed)) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n return violations;\n}\n\nfunction findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: string[], disableAllowed: boolean): ExitViolation[] {\n const violations: ExitViolation[] = [];\n for (const file of changedFiles) {\n for (const v of findExitViolationsInFile(file, workspaceRoot, disableAllowed)) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n return violations;\n}\n\nfunction reportViolations(workspaceRoot: string, violations: ExitViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n writeTemplateIfMissing(workspaceRoot, INSTRUCT_FILE);\n console.error('');\n console.error('❌ process.exit() outside main()/runMain (or an import of another module\\'s main)!');\n console.error('');\n console.error(SHARED_MESSAGE);\n console.error('');\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n console.error(disableAllowed\n ? ' Escape hatch (genuine terminal boundary only): // webpieces-disable no-process-exit-outside-main -- <reason>'\n : ' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(`\\n Current mode: ${mode}\\n`);\n}\n\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') return normalMode;\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping no-process-exit-outside-main validation (${skip.reason})\\n`);\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(options: NoProcessExitOutsideMainConfig, workspaceRoot: string): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n const disableAllowed = options.disableAllowed ?? true;\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping no-process-exit-outside-main validation (mode: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Validating No Process Exit Outside Main\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n if (!base) {\n console.log('\\n⏭️ Skipping no-process-exit-outside-main validation (could not detect base branch)\\n');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}\\n`);\n\n const changedFiles = getChangedFiles(workspaceRoot, base, head);\n if (changedFiles.length === 0) {\n console.log('✅ No TypeScript files changed');\n return { success: true };\n }\n\n console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);\n\n let violations: ExitViolation[] = [];\n if (mode === 'NEW_AND_MODIFIED_CODE') {\n violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed);\n } else if (mode === 'NEW_AND_MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);\n }\n\n if (violations.length === 0) {\n console.log('✅ No process.exit-outside-main violations found');\n return { success: true };\n }\n\n reportViolations(workspaceRoot, violations, mode, disableAllowed);\n return { success: false };\n}\n\nexport class NoProcessExitOutsideMainValidator extends CodeValidator<NoProcessExitOutsideMainConfig> {\n constructor(config: NoProcessExitOutsideMainConfig) {\n super(config, 'no-process-exit-outside-main');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
|