@webpieces/code-rules 0.3.196 → 0.3.197

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +2 -2
  2. package/src/diff-utils.d.ts +3 -22
  3. package/src/diff-utils.js +9 -116
  4. package/src/diff-utils.js.map +1 -1
  5. package/src/validate-catch-error-pattern.d.ts +1 -1
  6. package/src/validate-catch-error-pattern.js +7 -152
  7. package/src/validate-catch-error-pattern.js.map +1 -1
  8. package/src/validate-dtos.d.ts +1 -1
  9. package/src/validate-dtos.js +6 -141
  10. package/src/validate-dtos.js.map +1 -1
  11. package/src/validate-modified-files.js +3 -87
  12. package/src/validate-modified-files.js.map +1 -1
  13. package/src/validate-modified-methods.d.ts +1 -1
  14. package/src/validate-modified-methods.js +7 -197
  15. package/src/validate-modified-methods.js.map +1 -1
  16. package/src/validate-new-methods.js +4 -164
  17. package/src/validate-new-methods.js.map +1 -1
  18. package/src/validate-no-any-unknown.d.ts +1 -1
  19. package/src/validate-no-any-unknown.js +7 -143
  20. package/src/validate-no-any-unknown.js.map +1 -1
  21. package/src/validate-no-destructure.d.ts +1 -1
  22. package/src/validate-no-destructure.js +7 -143
  23. package/src/validate-no-destructure.js.map +1 -1
  24. package/src/validate-no-direct-api-resolver.d.ts +1 -1
  25. package/src/validate-no-direct-api-resolver.js +10 -89
  26. package/src/validate-no-direct-api-resolver.js.map +1 -1
  27. package/src/validate-no-implicit-any.d.ts +1 -1
  28. package/src/validate-no-implicit-any.js +6 -94
  29. package/src/validate-no-implicit-any.js.map +1 -1
  30. package/src/validate-no-inline-types.d.ts +1 -1
  31. package/src/validate-no-inline-types.js +10 -174
  32. package/src/validate-no-inline-types.js.map +1 -1
  33. package/src/validate-no-symbol-di-tokens.d.ts +1 -1
  34. package/src/validate-no-symbol-di-tokens.js +6 -130
  35. package/src/validate-no-symbol-di-tokens.js.map +1 -1
  36. package/src/validate-no-unmanaged-exceptions.d.ts +1 -1
  37. package/src/validate-no-unmanaged-exceptions.js +7 -152
  38. package/src/validate-no-unmanaged-exceptions.js.map +1 -1
  39. package/src/validate-prisma-converters.d.ts +1 -1
  40. package/src/validate-prisma-converters.js +10 -89
  41. package/src/validate-prisma-converters.js.map +1 -1
  42. package/src/validate-return-types.d.ts +1 -1
  43. package/src/validate-return-types.js +10 -174
  44. package/src/validate-return-types.js.map +1 -1
@@ -26,7 +26,7 @@
26
26
  * ============================================================================
27
27
  * - OFF: Skip validation entirely
28
28
  * - MODIFIED_CODE: Flag Symbol DI tokens on changed lines (lines in diff hunks)
29
- * - MODIFIED_FILES: Flag ALL Symbol DI tokens in files that were modified
29
+ * - NEW_AND_MODIFIED_FILES: Flag ALL Symbol DI tokens in files that were modified
30
30
  *
31
31
  * ============================================================================
32
32
  * ESCAPE HATCH
@@ -39,7 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.NoSymbolDiTokensValidator = void 0;
40
40
  exports.findSymbolViolationsInFile = findSymbolViolationsInFile;
41
41
  const tslib_1 = require("tslib");
42
- const child_process_1 = require("child_process");
43
42
  const fs = tslib_1.__importStar(require("fs"));
44
43
  const path = tslib_1.__importStar(require("path"));
45
44
  const rules_config_1 = require("@webpieces/rules-config");
@@ -144,102 +143,11 @@ function findSymbolViolationsInFile(filePath, workspaceRoot, disableAllowed, all
144
143
  }
145
144
  return violations;
146
145
  }
147
- // webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths
148
- function getChangedTypeScriptFiles(workspaceRoot, base, head) {
149
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
150
- try {
151
- const diffTarget = head ? `${base} ${head}` : base;
152
- const output = (0, child_process_1.execSync)(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
153
- cwd: workspaceRoot,
154
- encoding: 'utf-8',
155
- });
156
- const changedFiles = output
157
- .trim()
158
- .split('\n')
159
- .filter((f) => f && !isTestFile(f));
160
- if (!head) {
161
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
162
- try {
163
- const untrackedOutput = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
164
- cwd: workspaceRoot,
165
- encoding: 'utf-8',
166
- });
167
- const untrackedFiles = untrackedOutput
168
- .trim()
169
- .split('\n')
170
- .filter((f) => f && !isTestFile(f));
171
- const allFiles = new Set([...changedFiles, ...untrackedFiles]);
172
- return Array.from(allFiles);
173
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
174
- }
175
- catch {
176
- return changedFiles;
177
- }
178
- }
179
- return changedFiles;
180
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
181
- }
182
- catch {
183
- return [];
184
- }
185
- }
186
- function getFileDiff(workspaceRoot, file, base, head) {
187
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
188
- try {
189
- const diffTarget = head ? `${base} ${head}` : base;
190
- const diff = (0, child_process_1.execSync)(`git diff ${diffTarget} -- "${file}"`, {
191
- cwd: workspaceRoot,
192
- encoding: 'utf-8',
193
- });
194
- if (!diff && !head) {
195
- const fullPath = path.join(workspaceRoot, file);
196
- if (fs.existsSync(fullPath)) {
197
- const isUntracked = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard "${file}"`, {
198
- cwd: workspaceRoot,
199
- encoding: 'utf-8',
200
- }).trim();
201
- if (isUntracked) {
202
- const content = fs.readFileSync(fullPath, 'utf-8');
203
- const fileLines = content.split('\n');
204
- return fileLines.map((line) => `+${line}`).join('\n');
205
- }
206
- }
207
- }
208
- return diff;
209
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
210
- }
211
- catch {
212
- return '';
213
- }
214
- }
215
- function getChangedLineNumbers(diffContent) {
216
- const changedLines = new Set();
217
- const lines = diffContent.split('\n');
218
- let currentLine = 0;
219
- for (const line of lines) {
220
- const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
221
- if (hunkMatch) {
222
- currentLine = parseInt(hunkMatch[1], 10);
223
- continue;
224
- }
225
- if (line.startsWith('+') && !line.startsWith('+++')) {
226
- changedLines.add(currentLine);
227
- currentLine++;
228
- }
229
- else if (line.startsWith('-') && !line.startsWith('---')) {
230
- // Deletions don't increment line number
231
- }
232
- else {
233
- currentLine++;
234
- }
235
- }
236
- return changedLines;
237
- }
238
146
  function findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths) {
239
147
  const violations = [];
240
148
  for (const file of changedFiles) {
241
- const diff = getFileDiff(workspaceRoot, file, base, head);
242
- const changedLines = getChangedLineNumbers(diff);
149
+ const diff = (0, rules_config_1.getFileDiff)(workspaceRoot, file, base, head);
150
+ const changedLines = (0, rules_config_1.getChangedLineNumbers)(diff);
243
151
  if (changedLines.size === 0)
244
152
  continue;
245
153
  const allViolations = findSymbolViolationsInFile(file, workspaceRoot, disableAllowed, allowedPaths);
@@ -265,38 +173,6 @@ function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllo
265
173
  }
266
174
  return violations;
267
175
  }
268
- function detectBase(workspaceRoot) {
269
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
270
- try {
271
- const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD origin/main', {
272
- cwd: workspaceRoot,
273
- encoding: 'utf-8',
274
- stdio: ['pipe', 'pipe', 'pipe'],
275
- }).trim();
276
- if (mergeBase) {
277
- return mergeBase;
278
- }
279
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
280
- }
281
- catch {
282
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
283
- try {
284
- const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD main', {
285
- cwd: workspaceRoot,
286
- encoding: 'utf-8',
287
- stdio: ['pipe', 'pipe', 'pipe'],
288
- }).trim();
289
- if (mergeBase) {
290
- return mergeBase;
291
- }
292
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
293
- }
294
- catch {
295
- // Ignore
296
- }
297
- }
298
- return null;
299
- }
300
176
  // webpieces-disable max-lines-new-methods -- Console output with guidance message and violation list
301
177
  function reportViolations(violations, mode, disableAllowed) {
302
178
  console.error('');
@@ -346,7 +222,7 @@ async function runValidatorImpl(options, workspaceRoot) {
346
222
  let base = process.env['NX_BASE'];
347
223
  const head = process.env['NX_HEAD'];
348
224
  if (!base) {
349
- base = detectBase(workspaceRoot) ?? undefined;
225
+ base = (0, rules_config_1.detectBase)(workspaceRoot) ?? undefined;
350
226
  if (!base) {
351
227
  console.log('\n⏭️ Skipping no-symbol-di-tokens validation (could not detect base branch)');
352
228
  console.log('');
@@ -356,7 +232,7 @@ async function runValidatorImpl(options, workspaceRoot) {
356
232
  console.log(` Base: ${base}`);
357
233
  console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
358
234
  console.log('');
359
- const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
235
+ const changedFiles = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, head);
360
236
  if (changedFiles.length === 0) {
361
237
  console.log('✅ No TypeScript files changed');
362
238
  return { success: true };
@@ -366,7 +242,7 @@ async function runValidatorImpl(options, workspaceRoot) {
366
242
  if (mode === 'MODIFIED_CODE') {
367
243
  violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths);
368
244
  }
369
- else if (mode === 'MODIFIED_FILES') {
245
+ else if (mode === 'NEW_AND_MODIFIED_FILES') {
370
246
  violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths);
371
247
  }
372
248
  if (violations.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-no-symbol-di-tokens.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-no-symbol-di-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Symbol DI Tokens Executor\n *\n * Validates that Symbol() / Symbol.for() are not used to create DI tokens\n * outside of the designated API-binding packages.\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - export const MY_TOKEN = Symbol('MY_TOKEN')\n * - export const MY_TOKEN = Symbol.for('MY_TOKEN')\n * - const TOKEN: symbol = Symbol('TOKEN')\n *\n * ============================================================================\n * ALLOWED\n * ============================================================================\n *\n * - Files under allowedPaths (configured in webpieces.config.json, defaults to [])\n * - Test files (*.test.ts, *.spec.ts, __tests__/**)\n * - Lines with // webpieces-disable no-symbol-di-tokens -- <reason> (when disableAllowed: true)\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CODE: Flag Symbol DI tokens on changed lines (lines in diff hunks)\n * - MODIFIED_FILES: Flag ALL Symbol DI tokens in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment on the same line or line above the violation:\n * // webpieces-disable no-symbol-di-tokens -- [your justification]\n * export const MY_TOKEN = Symbol('MY_TOKEN');\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { hasDisable, RULE_NAMES, NoSymbolDiTokensConfig, ModifiedCodeMode } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\nconst SYMBOL_DI_REGEX = /=\\s*Symbol(?:\\.for)?\\(/;\n\nconst SHARED_MESSAGE = `Do not create a dependency-injection token with Symbol(). Choose the right pattern:\nOption 1: OWN class — annotate it with @provideSingleton() and inject by concrete class TYPE — no Symbol, no @inject.\n constructor(private readonly myService: MyService) {}\nOption 2: API IMPLEMENTATION — import the Symbol from the API definition file and use:\n @provideSingletonAs(SOME_API_TOKEN)\n export class SomeApiImpl implements SomeApi { ... }\nOption 3: External lib class you cannot decorate (DataSource, Anthropic, etc.):\n bind in a ContainerModule using the class itself as token — no Symbol needed:\n bind<Anthropic>(Anthropic).toDynamicValue(() => new Anthropic({ apiKey: ... })).inSingletonScope()\n Then inject by type — no Symbol, no @inject.\nOption 4 (last resort): append // webpieces-disable no-symbol-di-tokens -- <reason> to justify this specific line.`;\n\ninterface SymbolViolation {\n file: string;\n line: number;\n context: string;\n}\n\ninterface SymbolViolationInfo {\n line: number;\n context: string;\n hasDisableComment: boolean;\n}\n\nfunction isTestFile(filePath: string): boolean {\n return filePath.includes('.spec.ts') ||\n filePath.includes('.test.ts') ||\n filePath.includes('__tests__/');\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction isAllowedPath(filePath: string, allowedPaths: string[]): boolean {\n for (const pattern of allowedPaths) {\n if (globToRegex(pattern).test(filePath)) {\n return true;\n }\n }\n return false;\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\nfunction hasDisableOnLine(line: string): boolean {\n return hasDisable(line, RULE_NAMES.NO_SYMBOL_DI_TOKENS);\n}\n\nfunction resolveDisable(disabled: boolean, disableAllowed: boolean): boolean {\n if (!disableAllowed && disabled) {\n return false;\n }\n return disabled;\n}\n\nexport function findSymbolViolationsInFile(\n filePath: string,\n workspaceRoot: string,\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolationInfo[] {\n if (isTestFile(filePath)) return [];\n if (isAllowedPath(filePath, allowedPaths)) return [];\n\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 violations: SymbolViolationInfo[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n const stripped = stripLineComments(line);\n\n if (!SYMBOL_DI_REGEX.test(stripped)) continue;\n\n const lineNum = i + 1;\n const prevLine = i > 0 ? (lines[i - 1] ?? '') : '';\n const disabled = hasDisableOnLine(line) || hasDisableOnLine(prevLine);\n\n violations.push({\n line: lineNum,\n context: line.trim(),\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n return violations;\n}\n\n// webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const changedFiles = output\n .trim()\n .split('\\n')\n .filter((f: string) => f && !isTestFile(f));\n\n if (!head) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const untrackedFiles = untrackedOutput\n .trim()\n .split('\\n')\n .filter((f: string) => f && !isTestFile(f));\n const allFiles = new Set([...changedFiles, ...untrackedFiles]);\n return Array.from(allFiles);\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return changedFiles;\n }\n }\n\n return changedFiles;\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return [];\n }\n}\n\nfunction getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const diff = execSync(`git diff ${diffTarget} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n\n if (!diff && !head) {\n const fullPath = path.join(workspaceRoot, file);\n if (fs.existsSync(fullPath)) {\n const isUntracked = execSync(`git ls-files --others --exclude-standard \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n }).trim();\n\n if (isUntracked) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n const fileLines = content.split('\\n');\n return fileLines.map((line: string) => `+${line}`).join('\\n');\n }\n }\n }\n\n return diff;\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return '';\n }\n}\n\nfunction getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n let currentLine = 0;\n\n for (const line of lines) {\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Deletions don't increment line number\n } else {\n currentLine++;\n }\n }\n\n return changedLines;\n}\n\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolation[] {\n const violations: SymbolViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n\n if (changedLines.size === 0) continue;\n\n const allViolations = findSymbolViolationsInFile(file, workspaceRoot, disableAllowed, allowedPaths);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolation[] {\n const violations: SymbolViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findSymbolViolationsInFile(file, workspaceRoot, disableAllowed, allowedPaths);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\nfunction detectBase(workspaceRoot: string): string | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD origin/main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n // Ignore\n }\n }\n return null;\n}\n\n// webpieces-disable max-lines-new-methods -- Console output with guidance message and violation list\nfunction reportViolations(violations: SymbolViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('❌ Symbol() DI tokens are not allowed outside api(-external) packages!');\n console.error('');\n console.error(SHARED_MESSAGE);\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n\n if (disableAllowed) {\n console.error(' Escape hatch (use sparingly):');\n console.error(' // webpieces-disable no-symbol-di-tokens -- <reason>');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping no-symbol-di-tokens validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: NoSymbolDiTokensConfig,\n workspaceRoot: string,\n): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n const disableAllowed = options.disableAllowed ?? true;\n const allowedPaths = options.allowedPaths ?? [];\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping no-symbol-di-tokens validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating No Symbol DI Tokens\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping no-symbol-di-tokens validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\n\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);\n\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: SymbolViolation[] = [];\n\n if (mode === 'MODIFIED_CODE') {\n violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths);\n } else if (mode === 'MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths);\n }\n\n if (violations.length === 0) {\n console.log('✅ No Symbol DI token violations found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\nexport class NoSymbolDiTokensValidator extends CodeValidator<NoSymbolDiTokensConfig> {\n constructor(config: NoSymbolDiTokensConfig) {\n super(config, 'no-symbol-di-tokens');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
1
+ {"version":3,"file":"validate-no-symbol-di-tokens.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-no-symbol-di-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Symbol DI Tokens Executor\n *\n * Validates that Symbol() / Symbol.for() are not used to create DI tokens\n * outside of the designated API-binding packages.\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - export const MY_TOKEN = Symbol('MY_TOKEN')\n * - export const MY_TOKEN = Symbol.for('MY_TOKEN')\n * - const TOKEN: symbol = Symbol('TOKEN')\n *\n * ============================================================================\n * ALLOWED\n * ============================================================================\n *\n * - Files under allowedPaths (configured in webpieces.config.json, defaults to [])\n * - Test files (*.test.ts, *.spec.ts, __tests__/**)\n * - Lines with // webpieces-disable no-symbol-di-tokens -- <reason> (when disableAllowed: true)\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CODE: Flag Symbol DI tokens on changed lines (lines in diff hunks)\n * - NEW_AND_MODIFIED_FILES: Flag ALL Symbol DI tokens in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment on the same line or line above the violation:\n * // webpieces-disable no-symbol-di-tokens -- [your justification]\n * export const MY_TOKEN = Symbol('MY_TOKEN');\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { hasDisable, RULE_NAMES, NoSymbolDiTokensConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\nconst SYMBOL_DI_REGEX = /=\\s*Symbol(?:\\.for)?\\(/;\n\nconst SHARED_MESSAGE = `Do not create a dependency-injection token with Symbol(). Choose the right pattern:\nOption 1: OWN class — annotate it with @provideSingleton() and inject by concrete class TYPE — no Symbol, no @inject.\n constructor(private readonly myService: MyService) {}\nOption 2: API IMPLEMENTATION — import the Symbol from the API definition file and use:\n @provideSingletonAs(SOME_API_TOKEN)\n export class SomeApiImpl implements SomeApi { ... }\nOption 3: External lib class you cannot decorate (DataSource, Anthropic, etc.):\n bind in a ContainerModule using the class itself as token — no Symbol needed:\n bind<Anthropic>(Anthropic).toDynamicValue(() => new Anthropic({ apiKey: ... })).inSingletonScope()\n Then inject by type — no Symbol, no @inject.\nOption 4 (last resort): append // webpieces-disable no-symbol-di-tokens -- <reason> to justify this specific line.`;\n\ninterface SymbolViolation {\n file: string;\n line: number;\n context: string;\n}\n\ninterface SymbolViolationInfo {\n line: number;\n context: string;\n hasDisableComment: boolean;\n}\n\nfunction isTestFile(filePath: string): boolean {\n return filePath.includes('.spec.ts') ||\n filePath.includes('.test.ts') ||\n filePath.includes('__tests__/');\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction isAllowedPath(filePath: string, allowedPaths: string[]): boolean {\n for (const pattern of allowedPaths) {\n if (globToRegex(pattern).test(filePath)) {\n return true;\n }\n }\n return false;\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\nfunction hasDisableOnLine(line: string): boolean {\n return hasDisable(line, RULE_NAMES.NO_SYMBOL_DI_TOKENS);\n}\n\nfunction resolveDisable(disabled: boolean, disableAllowed: boolean): boolean {\n if (!disableAllowed && disabled) {\n return false;\n }\n return disabled;\n}\n\nexport function findSymbolViolationsInFile(\n filePath: string,\n workspaceRoot: string,\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolationInfo[] {\n if (isTestFile(filePath)) return [];\n if (isAllowedPath(filePath, allowedPaths)) return [];\n\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 violations: SymbolViolationInfo[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n const stripped = stripLineComments(line);\n\n if (!SYMBOL_DI_REGEX.test(stripped)) continue;\n\n const lineNum = i + 1;\n const prevLine = i > 0 ? (lines[i - 1] ?? '') : '';\n const disabled = hasDisableOnLine(line) || hasDisableOnLine(prevLine);\n\n violations.push({\n line: lineNum,\n context: line.trim(),\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n return violations;\n}\n\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolation[] {\n const violations: SymbolViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n\n if (changedLines.size === 0) continue;\n\n const allViolations = findSymbolViolationsInFile(file, workspaceRoot, disableAllowed, allowedPaths);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n allowedPaths: string[],\n): SymbolViolation[] {\n const violations: SymbolViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findSymbolViolationsInFile(file, workspaceRoot, disableAllowed, allowedPaths);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\n// webpieces-disable max-lines-new-methods -- Console output with guidance message and violation list\nfunction reportViolations(violations: SymbolViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('❌ Symbol() DI tokens are not allowed outside api(-external) packages!');\n console.error('');\n console.error(SHARED_MESSAGE);\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n\n if (disableAllowed) {\n console.error(' Escape hatch (use sparingly):');\n console.error(' // webpieces-disable no-symbol-di-tokens -- <reason>');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping no-symbol-di-tokens validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: NoSymbolDiTokensConfig,\n workspaceRoot: string,\n): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n const disableAllowed = options.disableAllowed ?? true;\n const allowedPaths = options.allowedPaths ?? [];\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping no-symbol-di-tokens validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating No Symbol DI Tokens\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping no-symbol-di-tokens validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\n\n const changedFiles = getChangedFiles(workspaceRoot, base, head);\n\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: SymbolViolation[] = [];\n\n if (mode === 'MODIFIED_CODE') {\n violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths);\n } else if (mode === 'NEW_AND_MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths);\n }\n\n if (violations.length === 0) {\n console.log('✅ No Symbol DI token violations found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\nexport class NoSymbolDiTokensValidator extends CodeValidator<NoSymbolDiTokensConfig> {\n constructor(config: NoSymbolDiTokensConfig) {\n super(config, 'no-symbol-di-tokens');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
@@ -22,7 +22,7 @@
22
22
  * ============================================================================
23
23
  * - OFF: Skip validation entirely
24
24
  * - MODIFIED_CODE: Flag try/catch on changed lines (lines in diff hunks)
25
- * - MODIFIED_FILES: Flag ALL try/catch in files that were modified
25
+ * - NEW_AND_MODIFIED_FILES: Flag ALL try/catch in files that were modified
26
26
  *
27
27
  * ============================================================================
28
28
  * ESCAPE HATCH
@@ -23,7 +23,7 @@
23
23
  * ============================================================================
24
24
  * - OFF: Skip validation entirely
25
25
  * - MODIFIED_CODE: Flag try/catch on changed lines (lines in diff hunks)
26
- * - MODIFIED_FILES: Flag ALL try/catch in files that were modified
26
+ * - NEW_AND_MODIFIED_FILES: Flag ALL try/catch in files that were modified
27
27
  *
28
28
  * ============================================================================
29
29
  * ESCAPE HATCH
@@ -35,121 +35,11 @@
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.NoUnmanagedExceptionsValidator = void 0;
37
37
  const tslib_1 = require("tslib");
38
- const child_process_1 = require("child_process");
39
38
  const fs = tslib_1.__importStar(require("fs"));
40
39
  const path = tslib_1.__importStar(require("path"));
41
40
  const rules_config_1 = require("@webpieces/rules-config");
42
41
  const code_validator_1 = require("./code-validator");
43
42
  const resolve_mode_1 = require("./resolve-mode");
44
- /**
45
- * Check if a file is a test file that should be skipped.
46
- */
47
- function isTestFile(filePath) {
48
- return filePath.includes('.spec.ts') ||
49
- filePath.includes('.test.ts') ||
50
- filePath.includes('__tests__/');
51
- }
52
- /**
53
- * Get changed TypeScript files between base and head (or working tree if head not specified).
54
- * Excludes test files.
55
- */
56
- // webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths
57
- function getChangedTypeScriptFiles(workspaceRoot, base, head) {
58
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
59
- try {
60
- const diffTarget = head ? `${base} ${head}` : base;
61
- const output = (0, child_process_1.execSync)(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
62
- cwd: workspaceRoot,
63
- encoding: 'utf-8',
64
- });
65
- const changedFiles = output
66
- .trim()
67
- .split('\n')
68
- .filter((f) => f && !isTestFile(f));
69
- if (!head) {
70
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
71
- try {
72
- const untrackedOutput = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
73
- cwd: workspaceRoot,
74
- encoding: 'utf-8',
75
- });
76
- const untrackedFiles = untrackedOutput
77
- .trim()
78
- .split('\n')
79
- .filter((f) => f && !isTestFile(f));
80
- const allFiles = new Set([...changedFiles, ...untrackedFiles]);
81
- return Array.from(allFiles);
82
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
83
- }
84
- catch {
85
- return changedFiles;
86
- }
87
- }
88
- return changedFiles;
89
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
90
- }
91
- catch {
92
- return [];
93
- }
94
- }
95
- /**
96
- * Get the diff content for a specific file.
97
- */
98
- function getFileDiff(workspaceRoot, file, base, head) {
99
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
100
- try {
101
- const diffTarget = head ? `${base} ${head}` : base;
102
- const diff = (0, child_process_1.execSync)(`git diff ${diffTarget} -- "${file}"`, {
103
- cwd: workspaceRoot,
104
- encoding: 'utf-8',
105
- });
106
- if (!diff && !head) {
107
- const fullPath = path.join(workspaceRoot, file);
108
- if (fs.existsSync(fullPath)) {
109
- const isUntracked = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard "${file}"`, {
110
- cwd: workspaceRoot,
111
- encoding: 'utf-8',
112
- }).trim();
113
- if (isUntracked) {
114
- const content = fs.readFileSync(fullPath, 'utf-8');
115
- const lines = content.split('\n');
116
- return lines.map((line) => `+${line}`).join('\n');
117
- }
118
- }
119
- }
120
- return diff;
121
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
122
- }
123
- catch {
124
- return '';
125
- }
126
- }
127
- /**
128
- * Parse diff to extract changed line numbers (additions only - lines starting with +).
129
- */
130
- function getChangedLineNumbers(diffContent) {
131
- const changedLines = new Set();
132
- const lines = diffContent.split('\n');
133
- let currentLine = 0;
134
- for (const line of lines) {
135
- const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
136
- if (hunkMatch) {
137
- currentLine = parseInt(hunkMatch[1], 10);
138
- continue;
139
- }
140
- if (line.startsWith('+') && !line.startsWith('+++')) {
141
- changedLines.add(currentLine);
142
- currentLine++;
143
- }
144
- else if (line.startsWith('-') && !line.startsWith('---')) {
145
- // Deletions don't increment line number
146
- }
147
- else {
148
- currentLine++;
149
- }
150
- }
151
- return changedLines;
152
- }
153
43
  /**
154
44
  * Check if a line contains a disable comment for no-unmanaged-exceptions.
155
45
  * Recognizes both webpieces-disable and eslint-disable-next-line @webpieces/ formats.
@@ -206,8 +96,8 @@ function findTryCatchInFile(filePath, workspaceRoot, disableAllowed) {
206
96
  function findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed) {
207
97
  const violations = [];
208
98
  for (const file of changedFiles) {
209
- const diff = getFileDiff(workspaceRoot, file, base, head);
210
- const changedLines = getChangedLineNumbers(diff);
99
+ const diff = (0, rules_config_1.getFileDiff)(workspaceRoot, file, base, head);
100
+ const changedLines = (0, rules_config_1.getChangedLineNumbers)(diff);
211
101
  if (changedLines.size === 0)
212
102
  continue;
213
103
  const allViolations = findTryCatchInFile(file, workspaceRoot, disableAllowed);
@@ -222,7 +112,7 @@ function findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head,
222
112
  return violations;
223
113
  }
224
114
  /**
225
- * MODIFIED_FILES mode: Flag ALL violations in files that were modified.
115
+ * NEW_AND_MODIFIED_FILES mode: Flag ALL violations in files that were modified.
226
116
  */
227
117
  function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed) {
228
118
  const violations = [];
@@ -236,41 +126,6 @@ function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllo
236
126
  }
237
127
  return violations;
238
128
  }
239
- /**
240
- * Auto-detect the base branch by finding the merge-base with origin/main.
241
- */
242
- function detectBase(workspaceRoot) {
243
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
244
- try {
245
- const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD origin/main', {
246
- cwd: workspaceRoot,
247
- encoding: 'utf-8',
248
- stdio: ['pipe', 'pipe', 'pipe'],
249
- }).trim();
250
- if (mergeBase) {
251
- return mergeBase;
252
- }
253
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
254
- }
255
- catch {
256
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
257
- try {
258
- const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD main', {
259
- cwd: workspaceRoot,
260
- encoding: 'utf-8',
261
- stdio: ['pipe', 'pipe', 'pipe'],
262
- }).trim();
263
- if (mergeBase) {
264
- return mergeBase;
265
- }
266
- // webpieces-disable catch-error-pattern -- intentional swallow of git command failure
267
- }
268
- catch {
269
- // Ignore
270
- }
271
- }
272
- return null;
273
- }
274
129
  /**
275
130
  * Report violations to console.
276
131
  */
@@ -331,7 +186,7 @@ async function runValidatorImpl(options, workspaceRoot) {
331
186
  let base = process.env['NX_BASE'];
332
187
  const head = process.env['NX_HEAD'];
333
188
  if (!base) {
334
- base = detectBase(workspaceRoot) ?? undefined;
189
+ base = (0, rules_config_1.detectBase)(workspaceRoot) ?? undefined;
335
190
  if (!base) {
336
191
  console.log('\n\u23ed\ufe0f Skipping no-unmanaged-exceptions validation (could not detect base branch)');
337
192
  console.log('');
@@ -341,7 +196,7 @@ async function runValidatorImpl(options, workspaceRoot) {
341
196
  console.log(` Base: ${base}`);
342
197
  console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
343
198
  console.log('');
344
- const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
199
+ const changedFiles = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, head);
345
200
  if (changedFiles.length === 0) {
346
201
  console.log('\u2705 No TypeScript files changed');
347
202
  return { success: true };
@@ -351,7 +206,7 @@ async function runValidatorImpl(options, workspaceRoot) {
351
206
  if (mode === 'MODIFIED_CODE') {
352
207
  violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed);
353
208
  }
354
- else if (mode === 'MODIFIED_FILES') {
209
+ else if (mode === 'NEW_AND_MODIFIED_FILES') {
355
210
  violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);
356
211
  }
357
212
  if (violations.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-no-unmanaged-exceptions.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-no-unmanaged-exceptions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Unmanaged Exceptions Executor\n *\n * Validates that try/catch blocks are not used outside chokepoints.\n * Uses LINE-BASED detection (not method-based) for git diff filtering.\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - try { — any try/catch block in non-test code\n *\n * ============================================================================\n * ALLOWED (skip — NOT violations)\n * ============================================================================\n *\n * - Test files (.spec.ts, .test.ts, __tests__/)\n * - Lines with // webpieces-disable no-unmanaged-exceptions -- [reason]\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CODE: Flag try/catch on changed lines (lines in diff hunks)\n * - MODIFIED_FILES: Flag ALL try/catch in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment above the violation:\n * // webpieces-disable no-unmanaged-exceptions -- [your justification]\n * try {\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { hasDisable, RULE_NAMES, NoUnmanagedExceptionsConfig, ModifiedCodeMode } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface TryCatchViolation {\n file: string;\n line: number;\n context: string;\n}\n\n/**\n * Check if a file is a test file that should be skipped.\n */\nfunction isTestFile(filePath: string): boolean {\n return filePath.includes('.spec.ts') ||\n filePath.includes('.test.ts') ||\n filePath.includes('__tests__/');\n}\n\n/**\n * Get changed TypeScript files between base and head (or working tree if head not specified).\n * Excludes test files.\n */\n// webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const changedFiles = output\n .trim()\n .split('\\n')\n .filter((f) => f && !isTestFile(f));\n\n if (!head) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const untrackedFiles = untrackedOutput\n .trim()\n .split('\\n')\n .filter((f) => f && !isTestFile(f));\n const allFiles = new Set([...changedFiles, ...untrackedFiles]);\n return Array.from(allFiles);\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return changedFiles;\n }\n }\n\n return changedFiles;\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return [];\n }\n}\n\n/**\n * Get the diff content for a specific file.\n */\nfunction getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const diff = execSync(`git diff ${diffTarget} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n\n if (!diff && !head) {\n const fullPath = path.join(workspaceRoot, file);\n if (fs.existsSync(fullPath)) {\n const isUntracked = execSync(`git ls-files --others --exclude-standard \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n }).trim();\n\n if (isUntracked) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n const lines = content.split('\\n');\n return lines.map((line) => `+${line}`).join('\\n');\n }\n }\n }\n\n return diff;\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n return '';\n }\n}\n\n/**\n * Parse diff to extract changed line numbers (additions only - lines starting with +).\n */\nfunction getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n let currentLine = 0;\n\n for (const line of lines) {\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Deletions don't increment line number\n } else {\n currentLine++;\n }\n }\n\n return changedLines;\n}\n\n/**\n * Check if a line contains a disable comment for no-unmanaged-exceptions.\n * Recognizes both webpieces-disable and eslint-disable-next-line @webpieces/ formats.\n */\nfunction hasDisableComment(lines: string[], lineNumber: number): boolean {\n const startCheck = Math.max(0, lineNumber - 5);\n for (let i = lineNumber - 2; i >= startCheck; i--) {\n const line = lines[i]?.trim() ?? '';\n if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {\n break;\n }\n if (hasDisable(line, RULE_NAMES.NO_UNMANAGED_EXCEPTIONS)) {\n return true;\n }\n if (line.includes('@webpieces/no-unmanaged-exceptions')) {\n return true;\n }\n }\n return false;\n}\n\nconst TRY_PATTERN = /\\btry\\s*\\{/;\n\ninterface TryCatchInfo {\n line: number;\n context: string;\n hasDisableComment: boolean;\n}\n\n/**\n * Find all try/catch patterns in a file using line-based scanning.\n */\nfunction findTryCatchInFile(filePath: string, workspaceRoot: string, disableAllowed: boolean): TryCatchInfo[] {\n const fullPath = path.join(workspaceRoot, filePath);\n if (!fs.existsSync(fullPath)) return [];\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const fileLines = content.split('\\n');\n const violations: TryCatchInfo[] = [];\n\n for (let i = 0; i < fileLines.length; i++) {\n const line = fileLines[i];\n const trimmed = line.trim();\n // Skip comment lines (JSDoc, block comments, line comments)\n if (trimmed.startsWith('*') || trimmed.startsWith('//') || trimmed.startsWith('/*')) continue;\n if (!TRY_PATTERN.test(line)) continue;\n\n const lineNum = i + 1;\n const disabled = hasDisableComment(fileLines, lineNum);\n\n if (!disableAllowed && disabled) {\n violations.push({ line: lineNum, context: line.trim(), hasDisableComment: false });\n } else {\n violations.push({ line: lineNum, context: line.trim(), hasDisableComment: disabled });\n }\n }\n\n return violations;\n}\n\n/**\n * MODIFIED_CODE mode: Flag violations on changed lines in diff hunks.\n */\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n): TryCatchViolation[] {\n const violations: TryCatchViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n\n if (changedLines.size === 0) continue;\n\n const allViolations = findTryCatchInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * MODIFIED_FILES mode: Flag ALL violations in files that were modified.\n */\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n): TryCatchViolation[] {\n const violations: TryCatchViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findTryCatchInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * Auto-detect the base branch by finding the merge-base with origin/main.\n */\nfunction detectBase(workspaceRoot: string): string | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD origin/main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n // webpieces-disable catch-error-pattern -- intentional swallow of git command failure\n } catch {\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Report violations to console.\n */\nfunction reportViolations(violations: TryCatchViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('\\u274c Unmanaged try/catch blocks found! Exceptions should bubble to chokepoints.');\n console.error('');\n console.error('\\ud83d\\udcda Philosophy: Most code should NOT catch exceptions.');\n console.error(' Exceptions should bubble to chokepoints (filter in Node.js, globalErrorHandler in Angular)');\n console.error(' where they are logged with traceId for debugging.');\n console.error('');\n\n for (const v of violations) {\n console.error(` \\u274c ${v.file}:${v.line}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n\n if (disableAllowed) {\n console.error(' Escape hatch (use sparingly):');\n console.error(' // webpieces-disable no-unmanaged-exceptions -- [your reason]');\n console.error('');\n console.error(' When try/catch IS used, the catch block MUST follow:');\n console.error(' catch (err: unknown) { const error = toError(err); ... }');\n console.error(' or: catch (err: unknown) { //const error = toError(err); }');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(' Disable comments are ignored. Remove the try/catch.');\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\n/**\n * Resolve mode considering ignoreModifiedUntilEpoch override.\n */\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: NoUnmanagedExceptionsConfig,\n workspaceRoot: string\n): 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\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n\\ud83d\\udccf Validating No Unmanaged Exceptions\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\n\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);\n\n if (changedFiles.length === 0) {\n console.log('\\u2705 No TypeScript files changed');\n return { success: true };\n }\n\n console.log(`\\ud83d\\udcc2 Checking ${changedFiles.length} changed file(s)...`);\n\n let violations: TryCatchViolation[] = [];\n\n if (mode === 'MODIFIED_CODE') {\n violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed);\n } else if (mode === 'MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);\n }\n\n if (violations.length === 0) {\n console.log('\\u2705 No unmanaged try/catch blocks found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\nexport class NoUnmanagedExceptionsValidator extends CodeValidator<NoUnmanagedExceptionsConfig> {\n constructor(config: NoUnmanagedExceptionsConfig) {\n super(config, 'no-unmanaged-exceptions');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
1
+ {"version":3,"file":"validate-no-unmanaged-exceptions.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-no-unmanaged-exceptions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Unmanaged Exceptions Executor\n *\n * Validates that try/catch blocks are not used outside chokepoints.\n * Uses LINE-BASED detection (not method-based) for git diff filtering.\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - try { — any try/catch block in non-test code\n *\n * ============================================================================\n * ALLOWED (skip — NOT violations)\n * ============================================================================\n *\n * - Test files (.spec.ts, .test.ts, __tests__/)\n * - Lines with // webpieces-disable no-unmanaged-exceptions -- [reason]\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CODE: Flag try/catch on changed lines (lines in diff hunks)\n * - NEW_AND_MODIFIED_FILES: Flag ALL try/catch in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment above the violation:\n * // webpieces-disable no-unmanaged-exceptions -- [your justification]\n * try {\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { hasDisable, RULE_NAMES, NoUnmanagedExceptionsConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface TryCatchViolation {\n file: string;\n line: number;\n context: string;\n}\n\n/**\n * Check if a line contains a disable comment for no-unmanaged-exceptions.\n * Recognizes both webpieces-disable and eslint-disable-next-line @webpieces/ formats.\n */\nfunction hasDisableComment(lines: string[], lineNumber: number): boolean {\n const startCheck = Math.max(0, lineNumber - 5);\n for (let i = lineNumber - 2; i >= startCheck; i--) {\n const line = lines[i]?.trim() ?? '';\n if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {\n break;\n }\n if (hasDisable(line, RULE_NAMES.NO_UNMANAGED_EXCEPTIONS)) {\n return true;\n }\n if (line.includes('@webpieces/no-unmanaged-exceptions')) {\n return true;\n }\n }\n return false;\n}\n\nconst TRY_PATTERN = /\\btry\\s*\\{/;\n\ninterface TryCatchInfo {\n line: number;\n context: string;\n hasDisableComment: boolean;\n}\n\n/**\n * Find all try/catch patterns in a file using line-based scanning.\n */\nfunction findTryCatchInFile(filePath: string, workspaceRoot: string, disableAllowed: boolean): TryCatchInfo[] {\n const fullPath = path.join(workspaceRoot, filePath);\n if (!fs.existsSync(fullPath)) return [];\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const fileLines = content.split('\\n');\n const violations: TryCatchInfo[] = [];\n\n for (let i = 0; i < fileLines.length; i++) {\n const line = fileLines[i];\n const trimmed = line.trim();\n // Skip comment lines (JSDoc, block comments, line comments)\n if (trimmed.startsWith('*') || trimmed.startsWith('//') || trimmed.startsWith('/*')) continue;\n if (!TRY_PATTERN.test(line)) continue;\n\n const lineNum = i + 1;\n const disabled = hasDisableComment(fileLines, lineNum);\n\n if (!disableAllowed && disabled) {\n violations.push({ line: lineNum, context: line.trim(), hasDisableComment: false });\n } else {\n violations.push({ line: lineNum, context: line.trim(), hasDisableComment: disabled });\n }\n }\n\n return violations;\n}\n\n/**\n * MODIFIED_CODE mode: Flag violations on changed lines in diff hunks.\n */\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n): TryCatchViolation[] {\n const violations: TryCatchViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n\n if (changedLines.size === 0) continue;\n\n const allViolations = findTryCatchInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * NEW_AND_MODIFIED_FILES mode: Flag ALL violations in files that were modified.\n */\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n): TryCatchViolation[] {\n const violations: TryCatchViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findTryCatchInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * Report violations to console.\n */\nfunction reportViolations(violations: TryCatchViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('\\u274c Unmanaged try/catch blocks found! Exceptions should bubble to chokepoints.');\n console.error('');\n console.error('\\ud83d\\udcda Philosophy: Most code should NOT catch exceptions.');\n console.error(' Exceptions should bubble to chokepoints (filter in Node.js, globalErrorHandler in Angular)');\n console.error(' where they are logged with traceId for debugging.');\n console.error('');\n\n for (const v of violations) {\n console.error(` \\u274c ${v.file}:${v.line}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n\n if (disableAllowed) {\n console.error(' Escape hatch (use sparingly):');\n console.error(' // webpieces-disable no-unmanaged-exceptions -- [your reason]');\n console.error('');\n console.error(' When try/catch IS used, the catch block MUST follow:');\n console.error(' catch (err: unknown) { const error = toError(err); ... }');\n console.error(' or: catch (err: unknown) { //const error = toError(err); }');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(' Disable comments are ignored. Remove the try/catch.');\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\n/**\n * Resolve mode considering ignoreModifiedUntilEpoch override.\n */\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: NoUnmanagedExceptionsConfig,\n workspaceRoot: string\n): 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\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n\\ud83d\\udccf Validating No Unmanaged Exceptions\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n\\u23ed\\ufe0f Skipping no-unmanaged-exceptions validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\n\n const changedFiles = getChangedFiles(workspaceRoot, base, head);\n\n if (changedFiles.length === 0) {\n console.log('\\u2705 No TypeScript files changed');\n return { success: true };\n }\n\n console.log(`\\ud83d\\udcc2 Checking ${changedFiles.length} changed file(s)...`);\n\n let violations: TryCatchViolation[] = [];\n\n if (mode === '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('\\u2705 No unmanaged try/catch blocks found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\nexport class NoUnmanagedExceptionsValidator extends CodeValidator<NoUnmanagedExceptionsConfig> {\n constructor(config: NoUnmanagedExceptionsConfig) {\n super(config, 'no-unmanaged-exceptions');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
@@ -42,7 +42,7 @@
42
42
  * ============================================================================
43
43
  * - OFF: Skip validation entirely
44
44
  * - NEW_AND_MODIFIED_METHODS: Validate new/modified methods in converters + changed lines in non-converters
45
- * - MODIFIED_FILES: Validate all methods in modified files
45
+ * - NEW_AND_MODIFIED_FILES: Validate all methods in modified files
46
46
  */
47
47
  import { PrismaConverterConfig } from '@webpieces/rules-config';
48
48
  import { CodeValidator, ExecutorResult } from './code-validator';