@webpieces/code-rules 0.4.490 → 0.4.492
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/tag-rule.d.ts +2 -2
- package/src/tag-rule.js +1 -1
- package/src/tag-rule.js.map +1 -1
- package/src/validate-catch-error-pattern.js +2 -2
- package/src/validate-catch-error-pattern.js.map +1 -1
- package/src/validate-dtos.js +2 -2
- package/src/validate-dtos.js.map +1 -1
- package/src/validate-inject-annotation-not-needed-for-concrete-class.js +1 -1
- package/src/validate-inject-annotation-not-needed-for-concrete-class.js.map +1 -1
- package/src/validate-match-rules.js +2 -2
- package/src/validate-match-rules.js.map +1 -1
- package/src/validate-modified-files.js +3 -3
- package/src/validate-modified-files.js.map +1 -1
- package/src/validate-modified-methods.js +3 -3
- package/src/validate-modified-methods.js.map +1 -1
- package/src/validate-new-methods.js +1 -1
- package/src/validate-new-methods.js.map +1 -1
- package/src/validate-no-any-unknown.js +2 -2
- package/src/validate-no-any-unknown.js.map +1 -1
- package/src/validate-no-client-creation-outside-server-or-client.js +1 -1
- package/src/validate-no-client-creation-outside-server-or-client.js.map +1 -1
- package/src/validate-no-custom-css.js +1 -1
- package/src/validate-no-custom-css.js.map +1 -1
- package/src/validate-no-destructure.js +2 -2
- package/src/validate-no-destructure.js.map +1 -1
- package/src/validate-no-direct-api-resolver.js +2 -2
- package/src/validate-no-direct-api-resolver.js.map +1 -1
- package/src/validate-no-function-outside-class.js +1 -1
- package/src/validate-no-function-outside-class.js.map +1 -1
- package/src/validate-no-implicit-any.js +1 -1
- package/src/validate-no-implicit-any.js.map +1 -1
- package/src/validate-no-inline-types.js +2 -2
- package/src/validate-no-inline-types.js.map +1 -1
- package/src/validate-no-process-exit-outside-main.js +1 -1
- package/src/validate-no-process-exit-outside-main.js.map +1 -1
- package/src/validate-no-symbol-di-tokens.js +1 -1
- package/src/validate-no-symbol-di-tokens.js.map +1 -1
- package/src/validate-no-unmanaged-exceptions.js +2 -2
- package/src/validate-no-unmanaged-exceptions.js.map +1 -1
- package/src/validate-prisma-converters.js +2 -2
- package/src/validate-prisma-converters.js.map +1 -1
- package/src/validate-return-types.js +2 -2
- package/src/validate-return-types.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/code-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.492",
|
|
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.4.
|
|
23
|
+
"@webpieces/rules-config": "0.4.492",
|
|
24
24
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
25
25
|
"inversify": "7.10.4",
|
|
26
26
|
"reflect-metadata": "0.2.2"
|
package/src/tag-rule.d.ts
CHANGED
|
@@ -71,8 +71,8 @@ export declare class TagRuleSpec {
|
|
|
71
71
|
export interface TagRuleConfig {
|
|
72
72
|
mode?: ProjectMode;
|
|
73
73
|
knownTypes?: string[];
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
turnOffRuleUntilEpoch?: number;
|
|
75
|
+
turnOffRuleWhileOnBranch?: string | null;
|
|
76
76
|
}
|
|
77
77
|
/** Every owning project of a changed file that is missing a `<tagPrefix>` tag. */
|
|
78
78
|
export declare function findProjectsMissingTag(workspaceRoot: string, changedFiles: string[], tagPrefix: string): MissingTagProject[];
|
package/src/tag-rule.js
CHANGED
|
@@ -195,7 +195,7 @@ function resolveMode(normalMode, epoch, branchPattern, ruleName) {
|
|
|
195
195
|
}
|
|
196
196
|
/** Run a concrete tag rule (framework-tag / role-tag) against the git diff. */
|
|
197
197
|
async function runTagValidator(options, workspaceRoot, spec) {
|
|
198
|
-
const mode = resolveMode(options.mode ?? 'OFF', options.
|
|
198
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined), spec.ruleName);
|
|
199
199
|
const knownTypes = options.knownTypes && options.knownTypes.length > 0 ? options.knownTypes : spec.defaultKnownTypes;
|
|
200
200
|
if (mode === 'OFF') {
|
|
201
201
|
console.log(`\n⏭️ Skipping ${spec.ruleName} validation (mode: OFF)`);
|
package/src/tag-rule.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag-rule.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/tag-rule.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared \"every modified project must carry a `<prefix>` nx tag\" rule engine.\n *\n * Both `framework-tag` (prefix `framework:`) and `role-tag` (prefix `role:`) are\n * the same rule with different prefix/known-values/messages, so the\n * project-walking + diff-scoping + mode logic lives here once and each concrete\n * rule supplies only a {@link TagRuleSpec}.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ProjectMode, detectBase, getChangedFiles, toError } from '@webpieces/rules-config';\nimport { ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\n/** A project (identified by its project.json) missing the required tag. */\nexport class MissingTagProject {\n constructor(\n public readonly name: string,\n public readonly projectJsonPath: string\n ) {}\n}\n\n/**\n * A project carrying one or more `<prefix>` tag VALUES outside the known set\n * (e.g. `framework:all`). `role-tag` never uses this — only rules that opt into\n * value validation via {@link TagRuleSpec.validateValues}.\n */\nexport class InvalidTagProject {\n constructor(\n public readonly name: string,\n public readonly projectJsonPath: string,\n public readonly badValues: string[]\n ) {}\n}\n\n/** Print the invalid-value report for projects carrying out-of-set tag values. */\nexport type ReportInvalidValues = (\n invalid: InvalidTagProject[],\n knownTypes: string[],\n mode: ProjectMode\n) => void;\n\n/** The knobs and messages that make a concrete tag rule out of the shared engine. */\nexport class TagRuleSpec {\n constructor(\n /** Tag prefix, e.g. `framework:` or `role:`. */\n public readonly tagPrefix: string,\n /** Rule id, e.g. `framework-tag` / `role-tag` (used in skip/validating logs). */\n public readonly ruleName: string,\n /** Human label for the \"Validating X\" banner, e.g. `Framework Tag`. */\n public readonly ruleLabel: string,\n /** Default allowed values when config.knownTypes is empty. */\n public readonly defaultKnownTypes: string[],\n /** Print the violation report for the missing-tag projects. */\n public readonly report: (untagged: MissingTagProject[], knownTypes: string[], mode: ProjectMode) => void,\n /**\n * When true, ALSO validate that every `<prefix>` tag value is in the\n * known set (framework-tag opts in to reject `framework:all` and typos;\n * role-tag leaves this false so its single-value behavior is unchanged).\n */\n public readonly validateValues: boolean = false,\n /** Report for out-of-set values; required when {@link validateValues} is true. */\n public readonly reportInvalidValues: ReportInvalidValues | null = null\n ) {}\n}\n\n/** The config fields the shared engine reads (shared by every tag rule config). */\nexport interface TagRuleConfig {\n mode?: ProjectMode;\n knownTypes?: string[];\n ignoreModifiedUntilEpoch?: number;\n ignoreRuleWhileOnBranch?: string;\n}\n\ntype RawProjectJson = { name?: string; tags?: string[] };\n\nclass ProjectJson {\n constructor(\n public readonly name: string | null,\n public readonly tags: string[]\n ) {}\n}\n\n/**\n * Parse `name` + `tags` from a project.json. Returns an empty ProjectJson when\n * unreadable/unparseable — config validation owns malformed-json reporting; this\n * rule just nudges for a tag and must never crash the build.\n */\nfunction readProjectJson(projectJsonPath: string, workspaceRoot: string): ProjectJson {\n const fullPath = path.join(workspaceRoot, projectJsonPath);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const parsed: RawProjectJson = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const name = typeof parsed.name === 'string' && parsed.name.length > 0 ? parsed.name : null;\n const tags = Array.isArray(parsed.tags) ? parsed.tags.filter((tag: string) => typeof tag === 'string') : [];\n return new ProjectJson(name, tags);\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — malformed project.json is not this rule's concern\n return new ProjectJson(null, []);\n }\n}\n\n/**\n * Walk up from a changed file to the nearest ancestor directory containing a\n * project.json (the file's owning nx project). Returns the repo-relative\n * project.json path, or null when the file belongs to no project.\n */\nfunction findOwningProjectJson(changedFile: string, workspaceRoot: string): string | null {\n let dir = path.dirname(changedFile);\n while (true) {\n const candidate = path.join(dir, 'project.json');\n if (fs.existsSync(path.join(workspaceRoot, candidate))) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir || dir === '.' || dir === '') {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction hasTag(tags: string[], tagPrefix: string): boolean {\n return tags.some((tag: string) => tag.startsWith(tagPrefix) && tag.slice(tagPrefix.length).trim().length > 0);\n}\n\n/** The non-empty values of every `<tagPrefix>` tag on a project (the env/role set). */\nfunction tagValues(tags: string[], tagPrefix: string): string[] {\n return tags\n .filter((tag: string) => tag.startsWith(tagPrefix))\n .map((tag: string) => tag.slice(tagPrefix.length).trim())\n .filter((value: string) => value.length > 0);\n}\n\n/** The distinct owning project.json paths of a set of changed files. */\nfunction collectOwningProjectJsons(workspaceRoot: string, changedFiles: string[]): string[] {\n const projectJsonPaths = new Set<string>();\n for (const file of changedFiles) {\n const owning = findOwningProjectJson(file, workspaceRoot);\n if (owning !== null) {\n projectJsonPaths.add(owning);\n }\n }\n return Array.from(projectJsonPaths).sort();\n}\n\n/** Every owning project of a changed file that is missing a `<tagPrefix>` tag. */\nexport function findProjectsMissingTag(\n workspaceRoot: string,\n changedFiles: string[],\n tagPrefix: string\n): MissingTagProject[] {\n const missing: MissingTagProject[] = [];\n for (const projectJsonPath of collectOwningProjectJsons(workspaceRoot, changedFiles)) {\n const projectJson = readProjectJson(projectJsonPath, workspaceRoot);\n if (!hasTag(projectJson.tags, tagPrefix)) {\n const name = projectJson.name ?? path.dirname(projectJsonPath);\n missing.push(new MissingTagProject(name, projectJsonPath));\n }\n }\n return missing;\n}\n\n/**\n * Every owning project of a changed file that carries one or more `<tagPrefix>`\n * tag VALUES outside `knownTypes`. Multiple values per project are allowed (an\n * env set) — this only flags values not in the known set (e.g. `framework:all`\n * or a typo). Projects with no `<tagPrefix>` tag are skipped here (the\n * missing-tag scan owns that case).\n */\nexport function findProjectsWithInvalidTagValues(\n workspaceRoot: string,\n changedFiles: string[],\n tagPrefix: string,\n knownTypes: string[]\n): InvalidTagProject[] {\n const known = new Set(knownTypes);\n const invalid: InvalidTagProject[] = [];\n for (const projectJsonPath of collectOwningProjectJsons(workspaceRoot, changedFiles)) {\n const projectJson = readProjectJson(projectJsonPath, workspaceRoot);\n const badValues = tagValues(projectJson.tags, tagPrefix).filter((value: string) => !known.has(value));\n if (badValues.length > 0) {\n const name = projectJson.name ?? path.dirname(projectJsonPath);\n invalid.push(new InvalidTagProject(name, projectJsonPath, badValues));\n }\n }\n return invalid;\n}\n\nfunction resolveMode(\n normalMode: ProjectMode,\n epoch: number | undefined,\n branchPattern: string | undefined,\n ruleName: string\n): ProjectMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping ${ruleName} validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\n/** Run a concrete tag rule (framework-tag / role-tag) against the git diff. */\nexport async function runTagValidator(\n options: TagRuleConfig,\n workspaceRoot: string,\n spec: TagRuleSpec\n): Promise<ExecutorResult> {\n const mode: ProjectMode = resolveMode(\n options.mode ?? 'OFF',\n options.ignoreModifiedUntilEpoch,\n options.ignoreRuleWhileOnBranch,\n spec.ruleName\n );\n const knownTypes = options.knownTypes && options.knownTypes.length > 0 ? options.knownTypes : spec.defaultKnownTypes;\n\n if (mode === 'OFF') {\n console.log(`\\n⏭️ Skipping ${spec.ruleName} validation (mode: OFF)`);\n console.log('');\n return { success: true };\n }\n\n console.log(`\\n📏 Validating ${spec.ruleLabel}\\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 if (!base) {\n console.log(`\\n⏭️ Skipping ${spec.ruleName} 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 // Project-level rule: ANY changed file (not just .ts) makes its owning\n // project require a tag, so include non-code files too.\n const changedFiles = getChangedFiles(workspaceRoot, base, head, { tsOnly: false });\n if (changedFiles.length === 0) {\n console.log('✅ No changed files in any project');\n return { success: true };\n }\n\n const missing = findProjectsMissingTag(workspaceRoot, changedFiles, spec.tagPrefix);\n const invalid =\n spec.validateValues && spec.reportInvalidValues !== null\n ? findProjectsWithInvalidTagValues(workspaceRoot, changedFiles, spec.tagPrefix, knownTypes)\n : [];\n\n if (missing.length === 0 && invalid.length === 0) {\n console.log(`✅ Every modified project declares a valid ${spec.tagPrefix.replace(/:$/, '')} tag`);\n return { success: true };\n }\n\n if (missing.length > 0) {\n spec.report(missing, knownTypes, mode);\n }\n if (invalid.length > 0 && spec.reportInvalidValues !== null) {\n spec.reportInvalidValues(invalid, knownTypes, mode);\n }\n return { success: false };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tag-rule.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/tag-rule.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared \"every modified project must carry a `<prefix>` nx tag\" rule engine.\n *\n * Both `framework-tag` (prefix `framework:`) and `role-tag` (prefix `role:`) are\n * the same rule with different prefix/known-values/messages, so the\n * project-walking + diff-scoping + mode logic lives here once and each concrete\n * rule supplies only a {@link TagRuleSpec}.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ProjectMode, detectBase, getChangedFiles, toError } from '@webpieces/rules-config';\nimport { ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\n/** A project (identified by its project.json) missing the required tag. */\nexport class MissingTagProject {\n constructor(\n public readonly name: string,\n public readonly projectJsonPath: string\n ) {}\n}\n\n/**\n * A project carrying one or more `<prefix>` tag VALUES outside the known set\n * (e.g. `framework:all`). `role-tag` never uses this — only rules that opt into\n * value validation via {@link TagRuleSpec.validateValues}.\n */\nexport class InvalidTagProject {\n constructor(\n public readonly name: string,\n public readonly projectJsonPath: string,\n public readonly badValues: string[]\n ) {}\n}\n\n/** Print the invalid-value report for projects carrying out-of-set tag values. */\nexport type ReportInvalidValues = (\n invalid: InvalidTagProject[],\n knownTypes: string[],\n mode: ProjectMode\n) => void;\n\n/** The knobs and messages that make a concrete tag rule out of the shared engine. */\nexport class TagRuleSpec {\n constructor(\n /** Tag prefix, e.g. `framework:` or `role:`. */\n public readonly tagPrefix: string,\n /** Rule id, e.g. `framework-tag` / `role-tag` (used in skip/validating logs). */\n public readonly ruleName: string,\n /** Human label for the \"Validating X\" banner, e.g. `Framework Tag`. */\n public readonly ruleLabel: string,\n /** Default allowed values when config.knownTypes is empty. */\n public readonly defaultKnownTypes: string[],\n /** Print the violation report for the missing-tag projects. */\n public readonly report: (untagged: MissingTagProject[], knownTypes: string[], mode: ProjectMode) => void,\n /**\n * When true, ALSO validate that every `<prefix>` tag value is in the\n * known set (framework-tag opts in to reject `framework:all` and typos;\n * role-tag leaves this false so its single-value behavior is unchanged).\n */\n public readonly validateValues: boolean = false,\n /** Report for out-of-set values; required when {@link validateValues} is true. */\n public readonly reportInvalidValues: ReportInvalidValues | null = null\n ) {}\n}\n\n/** The config fields the shared engine reads (shared by every tag rule config). */\nexport interface TagRuleConfig {\n mode?: ProjectMode;\n knownTypes?: string[];\n turnOffRuleUntilEpoch?: number;\n turnOffRuleWhileOnBranch?: string | null;\n}\n\ntype RawProjectJson = { name?: string; tags?: string[] };\n\nclass ProjectJson {\n constructor(\n public readonly name: string | null,\n public readonly tags: string[]\n ) {}\n}\n\n/**\n * Parse `name` + `tags` from a project.json. Returns an empty ProjectJson when\n * unreadable/unparseable — config validation owns malformed-json reporting; this\n * rule just nudges for a tag and must never crash the build.\n */\nfunction readProjectJson(projectJsonPath: string, workspaceRoot: string): ProjectJson {\n const fullPath = path.join(workspaceRoot, projectJsonPath);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const parsed: RawProjectJson = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const name = typeof parsed.name === 'string' && parsed.name.length > 0 ? parsed.name : null;\n const tags = Array.isArray(parsed.tags) ? parsed.tags.filter((tag: string) => typeof tag === 'string') : [];\n return new ProjectJson(name, tags);\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — malformed project.json is not this rule's concern\n return new ProjectJson(null, []);\n }\n}\n\n/**\n * Walk up from a changed file to the nearest ancestor directory containing a\n * project.json (the file's owning nx project). Returns the repo-relative\n * project.json path, or null when the file belongs to no project.\n */\nfunction findOwningProjectJson(changedFile: string, workspaceRoot: string): string | null {\n let dir = path.dirname(changedFile);\n while (true) {\n const candidate = path.join(dir, 'project.json');\n if (fs.existsSync(path.join(workspaceRoot, candidate))) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir || dir === '.' || dir === '') {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction hasTag(tags: string[], tagPrefix: string): boolean {\n return tags.some((tag: string) => tag.startsWith(tagPrefix) && tag.slice(tagPrefix.length).trim().length > 0);\n}\n\n/** The non-empty values of every `<tagPrefix>` tag on a project (the env/role set). */\nfunction tagValues(tags: string[], tagPrefix: string): string[] {\n return tags\n .filter((tag: string) => tag.startsWith(tagPrefix))\n .map((tag: string) => tag.slice(tagPrefix.length).trim())\n .filter((value: string) => value.length > 0);\n}\n\n/** The distinct owning project.json paths of a set of changed files. */\nfunction collectOwningProjectJsons(workspaceRoot: string, changedFiles: string[]): string[] {\n const projectJsonPaths = new Set<string>();\n for (const file of changedFiles) {\n const owning = findOwningProjectJson(file, workspaceRoot);\n if (owning !== null) {\n projectJsonPaths.add(owning);\n }\n }\n return Array.from(projectJsonPaths).sort();\n}\n\n/** Every owning project of a changed file that is missing a `<tagPrefix>` tag. */\nexport function findProjectsMissingTag(\n workspaceRoot: string,\n changedFiles: string[],\n tagPrefix: string\n): MissingTagProject[] {\n const missing: MissingTagProject[] = [];\n for (const projectJsonPath of collectOwningProjectJsons(workspaceRoot, changedFiles)) {\n const projectJson = readProjectJson(projectJsonPath, workspaceRoot);\n if (!hasTag(projectJson.tags, tagPrefix)) {\n const name = projectJson.name ?? path.dirname(projectJsonPath);\n missing.push(new MissingTagProject(name, projectJsonPath));\n }\n }\n return missing;\n}\n\n/**\n * Every owning project of a changed file that carries one or more `<tagPrefix>`\n * tag VALUES outside `knownTypes`. Multiple values per project are allowed (an\n * env set) — this only flags values not in the known set (e.g. `framework:all`\n * or a typo). Projects with no `<tagPrefix>` tag are skipped here (the\n * missing-tag scan owns that case).\n */\nexport function findProjectsWithInvalidTagValues(\n workspaceRoot: string,\n changedFiles: string[],\n tagPrefix: string,\n knownTypes: string[]\n): InvalidTagProject[] {\n const known = new Set(knownTypes);\n const invalid: InvalidTagProject[] = [];\n for (const projectJsonPath of collectOwningProjectJsons(workspaceRoot, changedFiles)) {\n const projectJson = readProjectJson(projectJsonPath, workspaceRoot);\n const badValues = tagValues(projectJson.tags, tagPrefix).filter((value: string) => !known.has(value));\n if (badValues.length > 0) {\n const name = projectJson.name ?? path.dirname(projectJsonPath);\n invalid.push(new InvalidTagProject(name, projectJsonPath, badValues));\n }\n }\n return invalid;\n}\n\nfunction resolveMode(\n normalMode: ProjectMode,\n epoch: number | undefined,\n branchPattern: string | undefined,\n ruleName: string\n): ProjectMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping ${ruleName} validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\n/** Run a concrete tag rule (framework-tag / role-tag) against the git diff. */\nexport async function runTagValidator(\n options: TagRuleConfig,\n workspaceRoot: string,\n spec: TagRuleSpec\n): Promise<ExecutorResult> {\n const mode: ProjectMode = resolveMode(\n options.mode ?? 'OFF',\n options.turnOffRuleUntilEpoch,\n (options.turnOffRuleWhileOnBranch ?? undefined),\n spec.ruleName\n );\n const knownTypes = options.knownTypes && options.knownTypes.length > 0 ? options.knownTypes : spec.defaultKnownTypes;\n\n if (mode === 'OFF') {\n console.log(`\\n⏭️ Skipping ${spec.ruleName} validation (mode: OFF)`);\n console.log('');\n return { success: true };\n }\n\n console.log(`\\n📏 Validating ${spec.ruleLabel}\\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 if (!base) {\n console.log(`\\n⏭️ Skipping ${spec.ruleName} 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 // Project-level rule: ANY changed file (not just .ts) makes its owning\n // project require a tag, so include non-code files too.\n const changedFiles = getChangedFiles(workspaceRoot, base, head, { tsOnly: false });\n if (changedFiles.length === 0) {\n console.log('✅ No changed files in any project');\n return { success: true };\n }\n\n const missing = findProjectsMissingTag(workspaceRoot, changedFiles, spec.tagPrefix);\n const invalid =\n spec.validateValues && spec.reportInvalidValues !== null\n ? findProjectsWithInvalidTagValues(workspaceRoot, changedFiles, spec.tagPrefix, knownTypes)\n : [];\n\n if (missing.length === 0 && invalid.length === 0) {\n console.log(`✅ Every modified project declares a valid ${spec.tagPrefix.replace(/:$/, '')} tag`);\n return { success: true };\n }\n\n if (missing.length > 0) {\n spec.report(missing, knownTypes, mode);\n }\n if (invalid.length > 0 && spec.reportInvalidValues !== null) {\n spec.reportInvalidValues(invalid, knownTypes, mode);\n }\n return { success: false };\n}\n"]}
|
|
@@ -316,7 +316,7 @@ function reportViolations(violations, mode, disableAllowed) {
|
|
|
316
316
|
console.error('');
|
|
317
317
|
}
|
|
318
318
|
/**
|
|
319
|
-
* Resolve mode considering
|
|
319
|
+
* Resolve mode considering turnOffRuleUntilEpoch override.
|
|
320
320
|
*/
|
|
321
321
|
function resolveMode(normalMode, epoch, branchPattern) {
|
|
322
322
|
if (normalMode === 'OFF') {
|
|
@@ -331,7 +331,7 @@ function resolveMode(normalMode, epoch, branchPattern) {
|
|
|
331
331
|
return normalMode;
|
|
332
332
|
}
|
|
333
333
|
async function runValidatorImpl(options, workspaceRoot) {
|
|
334
|
-
const mode = resolveMode(options.mode ?? 'OFF', options.
|
|
334
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined));
|
|
335
335
|
const disableAllowed = options.disableAllowed ?? true;
|
|
336
336
|
if (mode === 'OFF') {
|
|
337
337
|
console.log('\n\u23ed\ufe0f Skipping catch-error-pattern validation (mode: OFF)');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-catch-error-pattern.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-catch-error-pattern.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Catch Error Pattern Executor\n *\n * Validates that catch blocks follow the standardized error handling pattern.\n * Uses TypeScript AST for detection and LINE-BASED git diff filtering.\n *\n * ============================================================================\n * REQUIRED PATTERN\n * ============================================================================\n *\n * Standard: catch (err: unknown) { const error = toError(err); ... }\n * Ignored: catch (err: unknown) { //const error = toError(err); ... }\n * Nested: catch (err2: unknown) { const error2 = toError(err2); ... }\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - catch (e) { ... } — wrong parameter name\n * - catch (err) { ... } — missing : unknown type annotation\n * - catch (err: unknown) { ... } — missing toError() as first statement\n * - catch (err: unknown) { const x = toError(err); } — wrong variable name\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - NEW_AND_MODIFIED_CODE: Flag catch violations on changed lines (lines in diff hunks)\n * - NEW_AND_MODIFIED_FILES: Flag ALL catch violations in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment above the violation:\n * // webpieces-disable catch-error-pattern -- [your justification]\n * } catch (err: unknown) {\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport { hasDisable, RULE_NAMES, CatchErrorPatternConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface CatchViolation {\n file: string;\n line: number;\n message: string;\n context: string;\n}\n\ninterface CatchViolationInfo {\n line: number;\n message: string;\n context: string;\n hasDisableComment: boolean;\n}\n\n/**\n * Check if a line contains a disable comment for catch-error-pattern.\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.CATCH_ERROR_PATTERN)) {\n return true;\n }\n if (line.includes('@webpieces/catch-error-pattern')) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if the catch block contains a disable comment for catch-error-pattern.\n */\nfunction hasBlockLevelDisable(sourceText: string, blockStart: number, blockEnd: number): boolean {\n const blockText = sourceText.substring(blockStart, blockEnd);\n return hasDisable(blockText, RULE_NAMES.CATCH_ERROR_PATTERN) ||\n blockText.includes('@webpieces/catch-error-pattern');\n}\n\n/**\n * Check if the catch block body text contains the commented-out ignore pattern.\n */\nfunction hasIgnoreComment(\n sourceText: string,\n blockStart: number,\n blockEnd: number,\n expectedVarName: string,\n actualParamName: string,\n): boolean {\n const blockText = sourceText.substring(blockStart, blockEnd);\n const ignorePattern = new RegExp(\n `//\\\\s*const\\\\s+${expectedVarName}\\\\s*=\\\\s*toError\\\\(${actualParamName}\\\\)`,\n );\n return ignorePattern.test(blockText);\n}\n\n/**\n * Validate a single CatchClause node.\n */\n// webpieces-disable max-lines-new-methods -- AST validation with multiple check paths for param name, type, and toError\nfunction validateCatchClause(\n node: ts.CatchClause,\n sourceFile: ts.SourceFile,\n fileLines: string[],\n depth: number,\n disableAllowed: boolean,\n): CatchViolationInfo[] {\n const violations: CatchViolationInfo[] = [];\n const suffix = depth === 1 ? '' : String(depth);\n const expectedParam = 'err' + suffix;\n const expectedVar = 'error' + suffix;\n\n const catchLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;\n const catchContext = fileLines[catchLine - 1]?.trim() ?? '';\n const blockStart = node.block.getStart(sourceFile);\n const blockEnd = node.block.getEnd();\n const disabled = hasDisableComment(fileLines, catchLine) ||\n hasBlockLevelDisable(sourceFile.text, blockStart, blockEnd);\n\n const varDecl = node.variableDeclaration;\n if (!varDecl) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch clause must declare a parameter: catch (${expectedParam}: unknown)`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n return violations;\n }\n\n const actualParam = ts.isIdentifier(varDecl.name) ? varDecl.name.text : expectedParam;\n\n // Check parameter name\n if (ts.isIdentifier(varDecl.name) && varDecl.name.text !== expectedParam) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch parameter must be named \"${expectedParam}\" (or \"err2\", \"err3\" for nested), got \"${varDecl.name.text}\"`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n // Check type annotation is : unknown\n if (!varDecl.type || varDecl.type.kind !== ts.SyntaxKind.UnknownKeyword) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch parameter must be typed as \"unknown\": catch (${actualParam}: unknown)`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n // Check for commented-out ignore pattern\n if (hasIgnoreComment(sourceFile.text, blockStart, blockEnd, expectedVar, actualParam)) {\n return violations;\n }\n\n // Check first statement is const error = toError(err)\n if (node.block.statements.length === 0) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch block must call toError(${actualParam}) as first statement: const ${expectedVar} = toError(${actualParam});`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n return violations;\n }\n\n const firstStmt = node.block.statements[0];\n const toErrorViolation = validateToErrorStatement(firstStmt, sourceFile, fileLines, expectedParam, expectedVar, actualParam, disabled, disableAllowed);\n if (toErrorViolation) {\n violations.push(toErrorViolation);\n }\n\n return violations;\n}\n\nfunction resolveDisable(disabled: boolean, disableAllowed: boolean): boolean {\n if (!disableAllowed && disabled) {\n return false;\n }\n return disabled;\n}\n\n/**\n * Validate that the first statement is `const error = toError(err);`\n */\n// webpieces-disable max-lines-new-methods -- Deep AST check for variable declaration with toError call expression\nfunction validateToErrorStatement(\n stmt: ts.Statement,\n sourceFile: ts.SourceFile,\n fileLines: string[],\n expectedParam: string,\n expectedVar: string,\n actualParam: string,\n disabled: boolean,\n disableAllowed: boolean,\n): CatchViolationInfo | null {\n const stmtLine = sourceFile.getLineAndCharacterOfPosition(stmt.getStart(sourceFile)).line + 1;\n const stmtContext = fileLines[stmtLine - 1]?.trim() ?? '';\n\n if (!ts.isVariableStatement(stmt)) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n const declarations = stmt.declarationList.declarations;\n if (declarations.length === 0) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n const decl = declarations[0];\n\n // Check variable name\n if (ts.isIdentifier(decl.name) && decl.name.text !== expectedVar) {\n return {\n line: stmtLine,\n message: `Error variable must be named \"${expectedVar}\", got \"${decl.name.text}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n // Check initializer is toError(actualParam)\n const init = decl.initializer;\n if (!init || !ts.isCallExpression(init)) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n if (!ts.isIdentifier(init.expression) || init.expression.text !== 'toError') {\n return {\n line: stmtLine,\n message: `First statement in catch must call toError(), not \"${init.expression.getText(sourceFile)}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n // Check argument\n const args = init.arguments;\n if (args.length !== 1 || !ts.isIdentifier(args[0]) || args[0].text !== actualParam) {\n return {\n line: stmtLine,\n message: `toError() must be called with \"${actualParam}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n return null;\n}\n\n/**\n * Find all catch pattern violations in a file using AST.\n */\nfunction findCatchViolationsInFile(\n filePath: string,\n workspaceRoot: string,\n disableAllowed: boolean,\n): CatchViolationInfo[] {\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 sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n const violations: CatchViolationInfo[] = [];\n let catchDepth = 0;\n\n function visit(node: ts.Node): void {\n if (ts.isCatchClause(node)) {\n catchDepth++;\n const clauseViolations = validateCatchClause(node, sourceFile, fileLines, catchDepth, disableAllowed);\n violations.push(...clauseViolations);\n ts.forEachChild(node, visit);\n catchDepth--;\n return;\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return violations;\n}\n\n/**\n * NEW_AND_MODIFIED_CODE mode: Flag violations on changed lines.\n */\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n): CatchViolation[] {\n const violations: CatchViolation[] = [];\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 = findCatchViolationsInFile(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, message: v.message, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * NEW_AND_MODIFIED_FILES mode: Flag ALL violations in modified files.\n */\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n): CatchViolation[] {\n const violations: CatchViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findCatchViolationsInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, message: v.message, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * Report violations to console.\n */\n// webpieces-disable max-lines-new-methods -- Console output with pattern examples and escape hatch\nfunction reportViolations(violations: CatchViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('\\u274c Catch blocks must follow the standardized error handling pattern!');\n console.error('');\n console.error('\\ud83d\\udcda Required pattern:');\n console.error('');\n console.error(' catch (err: unknown) {');\n console.error(' const error = toError(err);');\n console.error(' // ... use error ...');\n console.error(' }');\n console.error('');\n console.error(' Or to explicitly ignore:');\n console.error(' catch (err: unknown) {');\n console.error(' //const error = toError(err);');\n console.error(' }');\n console.error('');\n console.error(' For nested catches: err2/error2, err3/error3, etc.');\n console.error('');\n\n for (const v of violations) {\n console.error(` \\u274c ${v.file}:${v.line}`);\n console.error(` ${v.message}`);\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 catch-error-pattern -- [your reason]');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(' Disable comments are ignored. Fix the catch block directly.');\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 catch-error-pattern validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: CatchErrorPatternConfig,\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 catch-error-pattern validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n\\ud83d\\udccf Validating Catch Error Pattern\\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 catch-error-pattern 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: CatchViolation[] = [];\n\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('\\u2705 No catch error pattern violations found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class CatchErrorPatternValidator extends CodeValidator<CatchErrorPatternConfig> {\n constructor(config: CatchErrorPatternConfig) {\n super(config, 'catch-error-pattern');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"validate-catch-error-pattern.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-catch-error-pattern.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Catch Error Pattern Executor\n *\n * Validates that catch blocks follow the standardized error handling pattern.\n * Uses TypeScript AST for detection and LINE-BASED git diff filtering.\n *\n * ============================================================================\n * REQUIRED PATTERN\n * ============================================================================\n *\n * Standard: catch (err: unknown) { const error = toError(err); ... }\n * Ignored: catch (err: unknown) { //const error = toError(err); ... }\n * Nested: catch (err2: unknown) { const error2 = toError(err2); ... }\n *\n * ============================================================================\n * VIOLATIONS (BAD) - These patterns are flagged:\n * ============================================================================\n *\n * - catch (e) { ... } — wrong parameter name\n * - catch (err) { ... } — missing : unknown type annotation\n * - catch (err: unknown) { ... } — missing toError() as first statement\n * - catch (err: unknown) { const x = toError(err); } — wrong variable name\n *\n * ============================================================================\n * MODES (LINE-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - NEW_AND_MODIFIED_CODE: Flag catch violations on changed lines (lines in diff hunks)\n * - NEW_AND_MODIFIED_FILES: Flag ALL catch violations in files that were modified\n *\n * ============================================================================\n * ESCAPE HATCH\n * ============================================================================\n * Add comment above the violation:\n * // webpieces-disable catch-error-pattern -- [your justification]\n * } catch (err: unknown) {\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport { hasDisable, RULE_NAMES, CatchErrorPatternConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface CatchViolation {\n file: string;\n line: number;\n message: string;\n context: string;\n}\n\ninterface CatchViolationInfo {\n line: number;\n message: string;\n context: string;\n hasDisableComment: boolean;\n}\n\n/**\n * Check if a line contains a disable comment for catch-error-pattern.\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.CATCH_ERROR_PATTERN)) {\n return true;\n }\n if (line.includes('@webpieces/catch-error-pattern')) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if the catch block contains a disable comment for catch-error-pattern.\n */\nfunction hasBlockLevelDisable(sourceText: string, blockStart: number, blockEnd: number): boolean {\n const blockText = sourceText.substring(blockStart, blockEnd);\n return hasDisable(blockText, RULE_NAMES.CATCH_ERROR_PATTERN) ||\n blockText.includes('@webpieces/catch-error-pattern');\n}\n\n/**\n * Check if the catch block body text contains the commented-out ignore pattern.\n */\nfunction hasIgnoreComment(\n sourceText: string,\n blockStart: number,\n blockEnd: number,\n expectedVarName: string,\n actualParamName: string,\n): boolean {\n const blockText = sourceText.substring(blockStart, blockEnd);\n const ignorePattern = new RegExp(\n `//\\\\s*const\\\\s+${expectedVarName}\\\\s*=\\\\s*toError\\\\(${actualParamName}\\\\)`,\n );\n return ignorePattern.test(blockText);\n}\n\n/**\n * Validate a single CatchClause node.\n */\n// webpieces-disable max-lines-new-methods -- AST validation with multiple check paths for param name, type, and toError\nfunction validateCatchClause(\n node: ts.CatchClause,\n sourceFile: ts.SourceFile,\n fileLines: string[],\n depth: number,\n disableAllowed: boolean,\n): CatchViolationInfo[] {\n const violations: CatchViolationInfo[] = [];\n const suffix = depth === 1 ? '' : String(depth);\n const expectedParam = 'err' + suffix;\n const expectedVar = 'error' + suffix;\n\n const catchLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;\n const catchContext = fileLines[catchLine - 1]?.trim() ?? '';\n const blockStart = node.block.getStart(sourceFile);\n const blockEnd = node.block.getEnd();\n const disabled = hasDisableComment(fileLines, catchLine) ||\n hasBlockLevelDisable(sourceFile.text, blockStart, blockEnd);\n\n const varDecl = node.variableDeclaration;\n if (!varDecl) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch clause must declare a parameter: catch (${expectedParam}: unknown)`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n return violations;\n }\n\n const actualParam = ts.isIdentifier(varDecl.name) ? varDecl.name.text : expectedParam;\n\n // Check parameter name\n if (ts.isIdentifier(varDecl.name) && varDecl.name.text !== expectedParam) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch parameter must be named \"${expectedParam}\" (or \"err2\", \"err3\" for nested), got \"${varDecl.name.text}\"`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n // Check type annotation is : unknown\n if (!varDecl.type || varDecl.type.kind !== ts.SyntaxKind.UnknownKeyword) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch parameter must be typed as \"unknown\": catch (${actualParam}: unknown)`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n }\n\n // Check for commented-out ignore pattern\n if (hasIgnoreComment(sourceFile.text, blockStart, blockEnd, expectedVar, actualParam)) {\n return violations;\n }\n\n // Check first statement is const error = toError(err)\n if (node.block.statements.length === 0) {\n violations.push({\n line: catchLine, context: catchContext,\n message: `Catch block must call toError(${actualParam}) as first statement: const ${expectedVar} = toError(${actualParam});`,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n });\n return violations;\n }\n\n const firstStmt = node.block.statements[0];\n const toErrorViolation = validateToErrorStatement(firstStmt, sourceFile, fileLines, expectedParam, expectedVar, actualParam, disabled, disableAllowed);\n if (toErrorViolation) {\n violations.push(toErrorViolation);\n }\n\n return violations;\n}\n\nfunction resolveDisable(disabled: boolean, disableAllowed: boolean): boolean {\n if (!disableAllowed && disabled) {\n return false;\n }\n return disabled;\n}\n\n/**\n * Validate that the first statement is `const error = toError(err);`\n */\n// webpieces-disable max-lines-new-methods -- Deep AST check for variable declaration with toError call expression\nfunction validateToErrorStatement(\n stmt: ts.Statement,\n sourceFile: ts.SourceFile,\n fileLines: string[],\n expectedParam: string,\n expectedVar: string,\n actualParam: string,\n disabled: boolean,\n disableAllowed: boolean,\n): CatchViolationInfo | null {\n const stmtLine = sourceFile.getLineAndCharacterOfPosition(stmt.getStart(sourceFile)).line + 1;\n const stmtContext = fileLines[stmtLine - 1]?.trim() ?? '';\n\n if (!ts.isVariableStatement(stmt)) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n const declarations = stmt.declarationList.declarations;\n if (declarations.length === 0) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n const decl = declarations[0];\n\n // Check variable name\n if (ts.isIdentifier(decl.name) && decl.name.text !== expectedVar) {\n return {\n line: stmtLine,\n message: `Error variable must be named \"${expectedVar}\", got \"${decl.name.text}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n // Check initializer is toError(actualParam)\n const init = decl.initializer;\n if (!init || !ts.isCallExpression(init)) {\n return {\n line: stmtLine,\n message: `First statement in catch must be: const ${expectedVar} = toError(${actualParam});`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n if (!ts.isIdentifier(init.expression) || init.expression.text !== 'toError') {\n return {\n line: stmtLine,\n message: `First statement in catch must call toError(), not \"${init.expression.getText(sourceFile)}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n // Check argument\n const args = init.arguments;\n if (args.length !== 1 || !ts.isIdentifier(args[0]) || args[0].text !== actualParam) {\n return {\n line: stmtLine,\n message: `toError() must be called with \"${actualParam}\"`,\n context: stmtContext,\n hasDisableComment: resolveDisable(disabled, disableAllowed),\n };\n }\n\n return null;\n}\n\n/**\n * Find all catch pattern violations in a file using AST.\n */\nfunction findCatchViolationsInFile(\n filePath: string,\n workspaceRoot: string,\n disableAllowed: boolean,\n): CatchViolationInfo[] {\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 sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n const violations: CatchViolationInfo[] = [];\n let catchDepth = 0;\n\n function visit(node: ts.Node): void {\n if (ts.isCatchClause(node)) {\n catchDepth++;\n const clauseViolations = validateCatchClause(node, sourceFile, fileLines, catchDepth, disableAllowed);\n violations.push(...clauseViolations);\n ts.forEachChild(node, visit);\n catchDepth--;\n return;\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return violations;\n}\n\n/**\n * NEW_AND_MODIFIED_CODE mode: Flag violations on changed lines.\n */\nfunction findViolationsForModifiedCode(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean,\n): CatchViolation[] {\n const violations: CatchViolation[] = [];\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 = findCatchViolationsInFile(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, message: v.message, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * NEW_AND_MODIFIED_FILES mode: Flag ALL violations in modified files.\n */\nfunction findViolationsForModifiedFiles(\n workspaceRoot: string,\n changedFiles: string[],\n disableAllowed: boolean,\n): CatchViolation[] {\n const violations: CatchViolation[] = [];\n\n for (const file of changedFiles) {\n const allViolations = findCatchViolationsInFile(file, workspaceRoot, disableAllowed);\n\n for (const v of allViolations) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, message: v.message, context: v.context });\n }\n }\n\n return violations;\n}\n\n/**\n * Report violations to console.\n */\n// webpieces-disable max-lines-new-methods -- Console output with pattern examples and escape hatch\nfunction reportViolations(violations: CatchViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('\\u274c Catch blocks must follow the standardized error handling pattern!');\n console.error('');\n console.error('\\ud83d\\udcda Required pattern:');\n console.error('');\n console.error(' catch (err: unknown) {');\n console.error(' const error = toError(err);');\n console.error(' // ... use error ...');\n console.error(' }');\n console.error('');\n console.error(' Or to explicitly ignore:');\n console.error(' catch (err: unknown) {');\n console.error(' //const error = toError(err);');\n console.error(' }');\n console.error('');\n console.error(' For nested catches: err2/error2, err3/error3, etc.');\n console.error('');\n\n for (const v of violations) {\n console.error(` \\u274c ${v.file}:${v.line}`);\n console.error(` ${v.message}`);\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 catch-error-pattern -- [your reason]');\n } else {\n console.error(' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(' Disable comments are ignored. Fix the catch block directly.');\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\n/**\n * Resolve mode considering turnOffRuleUntilEpoch 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 catch-error-pattern validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: CatchErrorPatternConfig,\n workspaceRoot: string\n): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined));\n const disableAllowed = options.disableAllowed ?? true;\n\n if (mode === 'OFF') {\n console.log('\\n\\u23ed\\ufe0f Skipping catch-error-pattern validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n\\ud83d\\udccf Validating Catch Error Pattern\\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 catch-error-pattern 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: CatchViolation[] = [];\n\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('\\u2705 No catch error pattern violations found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n\n return { success: false };\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class CatchErrorPatternValidator extends CodeValidator<CatchErrorPatternConfig> {\n constructor(config: CatchErrorPatternConfig) {\n super(config, 'catch-error-pattern');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
|
package/src/validate-dtos.js
CHANGED
|
@@ -387,7 +387,7 @@ function validateDtoFiles(workspaceRoot, prismaSchemaPath, changedFiles, dtoSour
|
|
|
387
387
|
return { success: false };
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
|
-
* Resolve mode considering
|
|
390
|
+
* Resolve mode considering turnOffRuleUntilEpoch override.
|
|
391
391
|
* When active, downgrades to OFF. When expired, logs a warning.
|
|
392
392
|
*/
|
|
393
393
|
function resolveMode(normalMode, epoch, branchPattern) {
|
|
@@ -403,7 +403,7 @@ function resolveMode(normalMode, epoch, branchPattern) {
|
|
|
403
403
|
return normalMode;
|
|
404
404
|
}
|
|
405
405
|
async function runValidatorImpl(options, workspaceRoot) {
|
|
406
|
-
const mode = resolveMode(options.mode ?? 'OFF', options.
|
|
406
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined));
|
|
407
407
|
if (mode === 'OFF') {
|
|
408
408
|
console.log('\n⏭️ Skipping validate-dtos (mode: OFF)');
|
|
409
409
|
console.log('');
|
package/src/validate-dtos.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-dtos.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-dtos.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate DTOs Executor\n *\n * Validates that every non-deprecated field in a XxxDto class/interface exists\n * in the corresponding XxxDbo Prisma model. This catches AI agents inventing\n * field names that don't match the database schema.\n *\n * ============================================================================\n * MODES\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CLASS: Only validate Dto classes that have changed lines in the diff\n * - NEW_AND_MODIFIED_FILES: Validate ALL Dto classes in files that were modified\n *\n * ============================================================================\n * SKIP CONDITIONS\n * ============================================================================\n * - If schema.prisma itself is modified, validation is skipped (schema in flux)\n * - Dto classes ending with \"JoinDto\" are skipped (they compose other Dtos)\n * - Fields marked @deprecated in a comment are exempt\n *\n * ============================================================================\n * MATCHING\n * ============================================================================\n * - UserDto matches UserDbo by case-insensitive prefix (\"user\")\n * - Dbo field names are converted from snake_case to camelCase for comparison\n * - Dto fields must be a subset of Dbo fields\n * - Extra Dbo fields are allowed (e.g., password)\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport { PrismaValidateDtosConfig, PrismaValidateDtosMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface DtoFieldInfo {\n name: string;\n line: number;\n deprecated: boolean;\n}\n\ninterface DtoInfo {\n name: string;\n file: string;\n startLine: number;\n endLine: number;\n fields: DtoFieldInfo[];\n}\n\ninterface DtoViolation {\n file: string;\n line: number;\n dtoName: string;\n fieldName: string;\n dboName: string;\n availableFields: string[];\n}\n\ninterface DboEntry {\n name: string;\n fields: Set<string>;\n}\n\n/**\n * Convert a snake_case string to camelCase.\n * e.g., \"version_number\" -> \"versionNumber\", \"id\" -> \"id\"\n */\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase());\n}\n\n/**\n * Parse schema.prisma to build a map of Dbo model name -> set of field names (camelCase).\n * Only models whose name ends with \"Dbo\" are included.\n * Field names are converted from snake_case to camelCase since Dto fields use camelCase.\n */\nfunction parsePrismaSchema(schemaPath: string): Map<string, Set<string>> {\n const models = new Map<string, Set<string>>();\n\n if (!fs.existsSync(schemaPath)) {\n return models;\n }\n\n const content = fs.readFileSync(schemaPath, 'utf-8');\n const lines = content.split('\\n');\n\n let currentModel: string | null = null;\n let currentFields: Set<string> | null = null;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Match model declaration: model XxxDbo {\n const modelMatch = trimmed.match(/^model\\s+(\\w+Dbo)\\s*\\{/);\n if (modelMatch) {\n currentModel = modelMatch[1];\n currentFields = new Set<string>();\n continue;\n }\n\n // End of model block\n if (currentModel && trimmed === '}') {\n models.set(currentModel, currentFields!);\n currentModel = null;\n currentFields = null;\n continue;\n }\n\n // Inside a model block - extract field names\n if (currentModel && currentFields) {\n // Skip empty lines, comments, and model-level attributes (@@)\n if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('@@')) {\n continue;\n }\n\n // Field name is the first word on the line, converted to camelCase\n const fieldMatch = trimmed.match(/^(\\w+)\\s/);\n if (fieldMatch) {\n currentFields.add(snakeToCamel(fieldMatch[1]));\n }\n }\n }\n\n return models;\n}\n\n/**\n * Check if a field has @deprecated in a comment above it (within 3 lines).\n */\nfunction isFieldDeprecated(fileLines: string[], fieldLine: number): boolean {\n const start = Math.max(0, fieldLine - 4);\n for (let i = start; i <= fieldLine - 1; i++) {\n const line = fileLines[i]?.trim() ?? '';\n if (line.includes('@deprecated')) return true;\n }\n return false;\n}\n\n/**\n * Parse a TypeScript file to find Dto class/interface declarations and their fields.\n * Skips classes ending with \"JoinDto\" since they compose other Dtos.\n */\n// webpieces-disable max-lines-new-methods -- AST traversal for both class and interface Dto detection with field extraction\nfunction findDtosInFile(filePath: string, workspaceRoot: string): DtoInfo[] {\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 sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n const dtos: DtoInfo[] = [];\n\n function visit(node: ts.Node): void {\n const isClass = ts.isClassDeclaration(node);\n const isInterface = ts.isInterfaceDeclaration(node);\n\n if ((isClass || isInterface) && node.name) {\n const name = node.name.text;\n\n // Must end with Dto but NOT with JoinDto\n if (name.endsWith('Dto') && !name.endsWith('JoinDto')) {\n const fields: DtoFieldInfo[] = [];\n const nodeStart = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n const nodeEnd = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n\n for (const member of node.members) {\n if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {\n if (member.name && ts.isIdentifier(member.name)) {\n const fieldName = member.name.text;\n const startPos = member.getStart(sourceFile);\n const pos = sourceFile.getLineAndCharacterOfPosition(startPos);\n const line = pos.line + 1;\n const deprecated = isFieldDeprecated(fileLines, line);\n\n fields.push({ name: fieldName, line, deprecated });\n }\n }\n }\n\n dtos.push({\n name,\n file: filePath,\n startLine: nodeStart.line + 1,\n endLine: nodeEnd.line + 1,\n fields,\n });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return dtos;\n}\n\n/**\n * Extract the prefix from a Dto/Dbo name by removing the suffix.\n * e.g., \"UserDto\" -> \"user\", \"UserDbo\" -> \"user\"\n */\nfunction extractPrefix(name: string, suffix: string): string {\n return name.slice(0, -suffix.length).toLowerCase();\n}\n\n/**\n * Find violations: Dto fields that don't exist in the corresponding Dbo.\n */\nfunction findViolations(\n dtos: DtoInfo[],\n dboModels: Map<string, Set<string>>\n): DtoViolation[] {\n const violations: DtoViolation[] = [];\n\n // Build a lowercase prefix -> Dbo info map\n const dboByPrefix = new Map<string, DboEntry>();\n dboModels.forEach((fields, dboName) => {\n const prefix = extractPrefix(dboName, 'Dbo');\n dboByPrefix.set(prefix, { name: dboName, fields });\n });\n\n for (const dto of dtos) {\n const prefix = extractPrefix(dto.name, 'Dto');\n const dbo = dboByPrefix.get(prefix);\n\n if (!dbo) {\n // No matching Dbo found - skip (might be a Dto without a DB table)\n continue;\n }\n\n for (const field of dto.fields) {\n if (field.deprecated) continue;\n\n if (!dbo.fields.has(field.name)) {\n violations.push({\n file: dto.file,\n line: field.line,\n dtoName: dto.name,\n fieldName: field.name,\n dboName: dbo.name,\n availableFields: Array.from(dbo.fields).sort(),\n });\n }\n }\n }\n\n return violations;\n}\n\n/**\n * Compute similarity between two strings using longest common subsequence ratio.\n * Returns a value between 0 and 1, where 1 is an exact match.\n */\nfunction similarity(a: string, b: string): number {\n const al = a.toLowerCase();\n const bl = b.toLowerCase();\n if (al === bl) return 1;\n\n const m = al.length;\n const n = bl.length;\n const prev = new Array<number>(n + 1).fill(0);\n const curr = new Array<number>(n + 1).fill(0);\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (al[i - 1] === bl[j - 1]) {\n curr[j] = prev[j - 1] + 1;\n } else {\n curr[j] = Math.max(prev[j], curr[j - 1]);\n }\n }\n for (let j = 0; j <= n; j++) {\n prev[j] = curr[j];\n curr[j] = 0;\n }\n }\n\n const lcsLen = prev[n];\n return (2 * lcsLen) / (m + n);\n}\n\n/**\n * Find the closest matching Dbo field for a given Dto field name.\n * Returns the best match or null if nothing scores above the threshold.\n */\nfunction suggestClosestField(fieldName: string, availableFields: string[]): string | null {\n let bestMatch: string | null = null;\n let bestScore = 0.4; // minimum threshold\n\n for (const candidate of availableFields) {\n const score = similarity(fieldName, candidate);\n if (score > bestScore) {\n bestScore = score;\n bestMatch = candidate;\n }\n }\n\n return bestMatch;\n}\n\n/**\n * Report violations to console with prominent AI warning and rename suggestions.\n */\nfunction reportViolations(violations: DtoViolation[]): void {\n console.error('');\n console.error('❌❌❌ DTO FIELD MISMATCH - AI AGENT: READ THIS CAREFULLY ❌❌❌');\n console.error('');\n console.error('🛑 DO NOT attempt find/replace or manual renaming. Instead:');\n console.error(' 1. IDE rename is 10x faster than AI renaming PLUS 100% correct vs. AI\\'s 80%');\n console.error(' 2. Based on errors below, suggest renames to the user');\n console.error(' 3. Ask the human to use IDE rename-refactoring (F2 / right-click → Rename Symbol)');\n console.error('');\n console.error('📚 Every non-deprecated field in a Dto must exist in the corresponding Dbo.');\n console.error(' Dbo can have extra fields (e.g., password) — Dto fields cannot be invented.');\n console.error('');\n console.error(' When needing fields from multiple tables (e.g., a join), use a XxxJoinDto that');\n console.error(' contains YYDto and ZZDto fields from the other tables instead of flattening.');\n console.error(' Fields marked @deprecated in the Dto are exempt from this check.');\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` ${v.dtoName}.${v.fieldName} does not exist in ${v.dboName}`);\n\n const suggestion = suggestClosestField(v.fieldName, v.availableFields);\n if (suggestion) {\n console.error(` 💡 Suggested rename: ${v.fieldName} → ${suggestion} (Use IDE rename on ${v.dtoName}.${v.fieldName})`);\n } else {\n const preview = v.availableFields.slice(0, 8).join(', ');\n const ellipsis = v.availableFields.length > 8 ? ', ...' : '';\n console.error(` 💡 No close match found. Available: ${preview}${ellipsis}`);\n }\n }\n console.error('');\n\n console.error('❌❌❌ AI AGENT: Suggest to the human which are the most likely renames needed and list them ❌❌❌');\n console.error('');\n}\n\n/**\n * Filter changed files to only TypeScript Dto source files within configured paths.\n */\nfunction filterDtoFiles(changedFiles: string[], dtoSourcePaths: string[]): string[] {\n return changedFiles.filter((f) => {\n if (!f.endsWith('.ts') && !f.endsWith('.tsx')) return false;\n if (f.includes('.spec.ts') || f.includes('.test.ts')) return false;\n return dtoSourcePaths.some((srcPath) => f.startsWith(srcPath));\n });\n}\n\n/**\n * Collect all Dto definitions from the given files.\n */\nfunction collectDtos(dtoFiles: string[], workspaceRoot: string): DtoInfo[] {\n const allDtos: DtoInfo[] = [];\n for (const file of dtoFiles) {\n const dtos = findDtosInFile(file, workspaceRoot);\n allDtos.push(...dtos);\n }\n return allDtos;\n}\n\n/**\n * Check if a Dto class overlaps with any changed lines in the diff.\n */\nfunction isDtoTouched(dto: DtoInfo, changedLines: Set<number>): boolean {\n for (let line = dto.startLine; line <= dto.endLine; line++) {\n if (changedLines.has(line)) return true;\n }\n return false;\n}\n\n/**\n * Filter Dtos to only those that have changed lines in the diff (MODIFIED_CLASS mode).\n */\nfunction filterTouchedDtos(\n dtos: DtoInfo[],\n workspaceRoot: string,\n base: string,\n head?: string\n): DtoInfo[] {\n // Group dtos by file to avoid re-fetching diffs\n const byFile = new Map<string, DtoInfo[]>();\n for (const dto of dtos) {\n const list = byFile.get(dto.file) ?? [];\n list.push(dto);\n byFile.set(dto.file, list);\n }\n\n const touched: DtoInfo[] = [];\n byFile.forEach((fileDtos, file) => {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n for (const dto of fileDtos) {\n if (isDtoTouched(dto, changedLines)) {\n touched.push(dto);\n }\n }\n });\n return touched;\n}\n\n/**\n * Resolve git base ref from env vars or auto-detection.\n */\nfunction resolveBase(workspaceRoot: string): string | undefined {\n const envBase = process.env['NX_BASE'];\n if (envBase) return envBase;\n return detectBase(workspaceRoot) ?? undefined;\n}\n\n/**\n * Run the core validation after early-exit checks have passed.\n */\n// webpieces-disable max-lines-new-methods -- Core validation orchestration with multiple early-exit checks\nfunction validateDtoFiles(\n workspaceRoot: string,\n prismaSchemaPath: string,\n changedFiles: string[],\n dtoSourcePaths: string[],\n mode: PrismaValidateDtosMode,\n base: string,\n head?: string\n): ExecutorResult {\n if (changedFiles.some((f) => f.endsWith(prismaSchemaPath))) {\n console.log('⏭️ Skipping validate-dtos (schema.prisma is modified - schema in flux)');\n console.log('');\n return { success: true };\n }\n\n const dtoFiles = filterDtoFiles(changedFiles, dtoSourcePaths);\n\n if (dtoFiles.length === 0) {\n console.log('✅ No Dto files changed');\n return { success: true };\n }\n\n console.log(`📂 Checking ${dtoFiles.length} changed file(s) for Dto definitions...`);\n\n const fullSchemaPath = path.join(workspaceRoot, prismaSchemaPath);\n const dboModels = parsePrismaSchema(fullSchemaPath);\n\n if (dboModels.size === 0) {\n console.log('⏭️ No Dbo models found in schema.prisma');\n console.log('');\n return { success: true };\n }\n\n console.log(` Found ${dboModels.size} Dbo model(s) in schema.prisma`);\n\n let allDtos = collectDtos(dtoFiles, workspaceRoot);\n\n if (allDtos.length === 0) {\n console.log('✅ No Dto definitions found in changed files');\n return { success: true };\n }\n\n // In MODIFIED_CLASS mode, narrow to only Dtos with changed lines\n if (mode === 'MODIFIED_CLASS') {\n allDtos = filterTouchedDtos(allDtos, workspaceRoot, base, head);\n if (allDtos.length === 0) {\n console.log('✅ No Dto classes were modified');\n return { success: true };\n }\n }\n\n console.log(` Validating ${allDtos.length} Dto definition(s)`);\n\n const violations = findViolations(allDtos, dboModels);\n\n if (violations.length === 0) {\n console.log('✅ All Dto fields match their Dbo models');\n return { success: true };\n }\n\n reportViolations(violations);\n return { success: false };\n}\n\n/**\n * Resolve mode considering ignoreModifiedUntilEpoch override.\n * When active, downgrades to OFF. When expired, logs a warning.\n */\nfunction resolveMode(normalMode: PrismaValidateDtosMode, epoch: number | undefined, branchPattern: string | undefined): PrismaValidateDtosMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping validate-dtos (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: PrismaValidateDtosConfig,\n workspaceRoot: string\n): Promise<ExecutorResult> {\n const mode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping validate-dtos (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n const prismaSchemaPath = options.prismaSchemaPath;\n const dtoSourcePaths = options.dtoSourcePaths ?? [];\n\n if (!prismaSchemaPath || dtoSourcePaths.length === 0) {\n const reason = !prismaSchemaPath ? 'no prismaSchemaPath configured' : 'no dtoSourcePaths configured';\n console.log(`\\n⏭️ Skipping validate-dtos (${reason})`);\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating DTOs match Prisma Dbo models\\n');\n console.log(` Mode: ${mode}`);\n console.log(` Schema: ${prismaSchemaPath}`);\n console.log(` Dto paths: ${dtoSourcePaths.join(', ')}`);\n\n const base = resolveBase(workspaceRoot);\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n console.log('\\n⏭️ Skipping validate-dtos (could not detect base branch)');\n console.log('');\n return { success: true };\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, { tsOnly: false });\n\n return validateDtoFiles(workspaceRoot, prismaSchemaPath, changedFiles, dtoSourcePaths, mode, base, head);\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class PrismaValidateDtosValidator extends CodeValidator<PrismaValidateDtosConfig> {\n constructor(config: PrismaValidateDtosConfig) {\n super(config, 'prisma-validate-dtos');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"validate-dtos.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-dtos.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate DTOs Executor\n *\n * Validates that every non-deprecated field in a XxxDto class/interface exists\n * in the corresponding XxxDbo Prisma model. This catches AI agents inventing\n * field names that don't match the database schema.\n *\n * ============================================================================\n * MODES\n * ============================================================================\n * - OFF: Skip validation entirely\n * - MODIFIED_CLASS: Only validate Dto classes that have changed lines in the diff\n * - NEW_AND_MODIFIED_FILES: Validate ALL Dto classes in files that were modified\n *\n * ============================================================================\n * SKIP CONDITIONS\n * ============================================================================\n * - If schema.prisma itself is modified, validation is skipped (schema in flux)\n * - Dto classes ending with \"JoinDto\" are skipped (they compose other Dtos)\n * - Fields marked @deprecated in a comment are exempt\n *\n * ============================================================================\n * MATCHING\n * ============================================================================\n * - UserDto matches UserDbo by case-insensitive prefix (\"user\")\n * - Dbo field names are converted from snake_case to camelCase for comparison\n * - Dto fields must be a subset of Dbo fields\n * - Extra Dbo fields are allowed (e.g., password)\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport { PrismaValidateDtosConfig, PrismaValidateDtosMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { shouldSkipRule } from './resolve-mode';\n\ninterface DtoFieldInfo {\n name: string;\n line: number;\n deprecated: boolean;\n}\n\ninterface DtoInfo {\n name: string;\n file: string;\n startLine: number;\n endLine: number;\n fields: DtoFieldInfo[];\n}\n\ninterface DtoViolation {\n file: string;\n line: number;\n dtoName: string;\n fieldName: string;\n dboName: string;\n availableFields: string[];\n}\n\ninterface DboEntry {\n name: string;\n fields: Set<string>;\n}\n\n/**\n * Convert a snake_case string to camelCase.\n * e.g., \"version_number\" -> \"versionNumber\", \"id\" -> \"id\"\n */\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase());\n}\n\n/**\n * Parse schema.prisma to build a map of Dbo model name -> set of field names (camelCase).\n * Only models whose name ends with \"Dbo\" are included.\n * Field names are converted from snake_case to camelCase since Dto fields use camelCase.\n */\nfunction parsePrismaSchema(schemaPath: string): Map<string, Set<string>> {\n const models = new Map<string, Set<string>>();\n\n if (!fs.existsSync(schemaPath)) {\n return models;\n }\n\n const content = fs.readFileSync(schemaPath, 'utf-8');\n const lines = content.split('\\n');\n\n let currentModel: string | null = null;\n let currentFields: Set<string> | null = null;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Match model declaration: model XxxDbo {\n const modelMatch = trimmed.match(/^model\\s+(\\w+Dbo)\\s*\\{/);\n if (modelMatch) {\n currentModel = modelMatch[1];\n currentFields = new Set<string>();\n continue;\n }\n\n // End of model block\n if (currentModel && trimmed === '}') {\n models.set(currentModel, currentFields!);\n currentModel = null;\n currentFields = null;\n continue;\n }\n\n // Inside a model block - extract field names\n if (currentModel && currentFields) {\n // Skip empty lines, comments, and model-level attributes (@@)\n if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('@@')) {\n continue;\n }\n\n // Field name is the first word on the line, converted to camelCase\n const fieldMatch = trimmed.match(/^(\\w+)\\s/);\n if (fieldMatch) {\n currentFields.add(snakeToCamel(fieldMatch[1]));\n }\n }\n }\n\n return models;\n}\n\n/**\n * Check if a field has @deprecated in a comment above it (within 3 lines).\n */\nfunction isFieldDeprecated(fileLines: string[], fieldLine: number): boolean {\n const start = Math.max(0, fieldLine - 4);\n for (let i = start; i <= fieldLine - 1; i++) {\n const line = fileLines[i]?.trim() ?? '';\n if (line.includes('@deprecated')) return true;\n }\n return false;\n}\n\n/**\n * Parse a TypeScript file to find Dto class/interface declarations and their fields.\n * Skips classes ending with \"JoinDto\" since they compose other Dtos.\n */\n// webpieces-disable max-lines-new-methods -- AST traversal for both class and interface Dto detection with field extraction\nfunction findDtosInFile(filePath: string, workspaceRoot: string): DtoInfo[] {\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 sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n const dtos: DtoInfo[] = [];\n\n function visit(node: ts.Node): void {\n const isClass = ts.isClassDeclaration(node);\n const isInterface = ts.isInterfaceDeclaration(node);\n\n if ((isClass || isInterface) && node.name) {\n const name = node.name.text;\n\n // Must end with Dto but NOT with JoinDto\n if (name.endsWith('Dto') && !name.endsWith('JoinDto')) {\n const fields: DtoFieldInfo[] = [];\n const nodeStart = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n const nodeEnd = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n\n for (const member of node.members) {\n if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {\n if (member.name && ts.isIdentifier(member.name)) {\n const fieldName = member.name.text;\n const startPos = member.getStart(sourceFile);\n const pos = sourceFile.getLineAndCharacterOfPosition(startPos);\n const line = pos.line + 1;\n const deprecated = isFieldDeprecated(fileLines, line);\n\n fields.push({ name: fieldName, line, deprecated });\n }\n }\n }\n\n dtos.push({\n name,\n file: filePath,\n startLine: nodeStart.line + 1,\n endLine: nodeEnd.line + 1,\n fields,\n });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return dtos;\n}\n\n/**\n * Extract the prefix from a Dto/Dbo name by removing the suffix.\n * e.g., \"UserDto\" -> \"user\", \"UserDbo\" -> \"user\"\n */\nfunction extractPrefix(name: string, suffix: string): string {\n return name.slice(0, -suffix.length).toLowerCase();\n}\n\n/**\n * Find violations: Dto fields that don't exist in the corresponding Dbo.\n */\nfunction findViolations(\n dtos: DtoInfo[],\n dboModels: Map<string, Set<string>>\n): DtoViolation[] {\n const violations: DtoViolation[] = [];\n\n // Build a lowercase prefix -> Dbo info map\n const dboByPrefix = new Map<string, DboEntry>();\n dboModels.forEach((fields, dboName) => {\n const prefix = extractPrefix(dboName, 'Dbo');\n dboByPrefix.set(prefix, { name: dboName, fields });\n });\n\n for (const dto of dtos) {\n const prefix = extractPrefix(dto.name, 'Dto');\n const dbo = dboByPrefix.get(prefix);\n\n if (!dbo) {\n // No matching Dbo found - skip (might be a Dto without a DB table)\n continue;\n }\n\n for (const field of dto.fields) {\n if (field.deprecated) continue;\n\n if (!dbo.fields.has(field.name)) {\n violations.push({\n file: dto.file,\n line: field.line,\n dtoName: dto.name,\n fieldName: field.name,\n dboName: dbo.name,\n availableFields: Array.from(dbo.fields).sort(),\n });\n }\n }\n }\n\n return violations;\n}\n\n/**\n * Compute similarity between two strings using longest common subsequence ratio.\n * Returns a value between 0 and 1, where 1 is an exact match.\n */\nfunction similarity(a: string, b: string): number {\n const al = a.toLowerCase();\n const bl = b.toLowerCase();\n if (al === bl) return 1;\n\n const m = al.length;\n const n = bl.length;\n const prev = new Array<number>(n + 1).fill(0);\n const curr = new Array<number>(n + 1).fill(0);\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (al[i - 1] === bl[j - 1]) {\n curr[j] = prev[j - 1] + 1;\n } else {\n curr[j] = Math.max(prev[j], curr[j - 1]);\n }\n }\n for (let j = 0; j <= n; j++) {\n prev[j] = curr[j];\n curr[j] = 0;\n }\n }\n\n const lcsLen = prev[n];\n return (2 * lcsLen) / (m + n);\n}\n\n/**\n * Find the closest matching Dbo field for a given Dto field name.\n * Returns the best match or null if nothing scores above the threshold.\n */\nfunction suggestClosestField(fieldName: string, availableFields: string[]): string | null {\n let bestMatch: string | null = null;\n let bestScore = 0.4; // minimum threshold\n\n for (const candidate of availableFields) {\n const score = similarity(fieldName, candidate);\n if (score > bestScore) {\n bestScore = score;\n bestMatch = candidate;\n }\n }\n\n return bestMatch;\n}\n\n/**\n * Report violations to console with prominent AI warning and rename suggestions.\n */\nfunction reportViolations(violations: DtoViolation[]): void {\n console.error('');\n console.error('❌❌❌ DTO FIELD MISMATCH - AI AGENT: READ THIS CAREFULLY ❌❌❌');\n console.error('');\n console.error('🛑 DO NOT attempt find/replace or manual renaming. Instead:');\n console.error(' 1. IDE rename is 10x faster than AI renaming PLUS 100% correct vs. AI\\'s 80%');\n console.error(' 2. Based on errors below, suggest renames to the user');\n console.error(' 3. Ask the human to use IDE rename-refactoring (F2 / right-click → Rename Symbol)');\n console.error('');\n console.error('📚 Every non-deprecated field in a Dto must exist in the corresponding Dbo.');\n console.error(' Dbo can have extra fields (e.g., password) — Dto fields cannot be invented.');\n console.error('');\n console.error(' When needing fields from multiple tables (e.g., a join), use a XxxJoinDto that');\n console.error(' contains YYDto and ZZDto fields from the other tables instead of flattening.');\n console.error(' Fields marked @deprecated in the Dto are exempt from this check.');\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` ${v.dtoName}.${v.fieldName} does not exist in ${v.dboName}`);\n\n const suggestion = suggestClosestField(v.fieldName, v.availableFields);\n if (suggestion) {\n console.error(` 💡 Suggested rename: ${v.fieldName} → ${suggestion} (Use IDE rename on ${v.dtoName}.${v.fieldName})`);\n } else {\n const preview = v.availableFields.slice(0, 8).join(', ');\n const ellipsis = v.availableFields.length > 8 ? ', ...' : '';\n console.error(` 💡 No close match found. Available: ${preview}${ellipsis}`);\n }\n }\n console.error('');\n\n console.error('❌❌❌ AI AGENT: Suggest to the human which are the most likely renames needed and list them ❌❌❌');\n console.error('');\n}\n\n/**\n * Filter changed files to only TypeScript Dto source files within configured paths.\n */\nfunction filterDtoFiles(changedFiles: string[], dtoSourcePaths: string[]): string[] {\n return changedFiles.filter((f) => {\n if (!f.endsWith('.ts') && !f.endsWith('.tsx')) return false;\n if (f.includes('.spec.ts') || f.includes('.test.ts')) return false;\n return dtoSourcePaths.some((srcPath) => f.startsWith(srcPath));\n });\n}\n\n/**\n * Collect all Dto definitions from the given files.\n */\nfunction collectDtos(dtoFiles: string[], workspaceRoot: string): DtoInfo[] {\n const allDtos: DtoInfo[] = [];\n for (const file of dtoFiles) {\n const dtos = findDtosInFile(file, workspaceRoot);\n allDtos.push(...dtos);\n }\n return allDtos;\n}\n\n/**\n * Check if a Dto class overlaps with any changed lines in the diff.\n */\nfunction isDtoTouched(dto: DtoInfo, changedLines: Set<number>): boolean {\n for (let line = dto.startLine; line <= dto.endLine; line++) {\n if (changedLines.has(line)) return true;\n }\n return false;\n}\n\n/**\n * Filter Dtos to only those that have changed lines in the diff (MODIFIED_CLASS mode).\n */\nfunction filterTouchedDtos(\n dtos: DtoInfo[],\n workspaceRoot: string,\n base: string,\n head?: string\n): DtoInfo[] {\n // Group dtos by file to avoid re-fetching diffs\n const byFile = new Map<string, DtoInfo[]>();\n for (const dto of dtos) {\n const list = byFile.get(dto.file) ?? [];\n list.push(dto);\n byFile.set(dto.file, list);\n }\n\n const touched: DtoInfo[] = [];\n byFile.forEach((fileDtos, file) => {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const changedLines = getChangedLineNumbers(diff);\n for (const dto of fileDtos) {\n if (isDtoTouched(dto, changedLines)) {\n touched.push(dto);\n }\n }\n });\n return touched;\n}\n\n/**\n * Resolve git base ref from env vars or auto-detection.\n */\nfunction resolveBase(workspaceRoot: string): string | undefined {\n const envBase = process.env['NX_BASE'];\n if (envBase) return envBase;\n return detectBase(workspaceRoot) ?? undefined;\n}\n\n/**\n * Run the core validation after early-exit checks have passed.\n */\n// webpieces-disable max-lines-new-methods -- Core validation orchestration with multiple early-exit checks\nfunction validateDtoFiles(\n workspaceRoot: string,\n prismaSchemaPath: string,\n changedFiles: string[],\n dtoSourcePaths: string[],\n mode: PrismaValidateDtosMode,\n base: string,\n head?: string\n): ExecutorResult {\n if (changedFiles.some((f) => f.endsWith(prismaSchemaPath))) {\n console.log('⏭️ Skipping validate-dtos (schema.prisma is modified - schema in flux)');\n console.log('');\n return { success: true };\n }\n\n const dtoFiles = filterDtoFiles(changedFiles, dtoSourcePaths);\n\n if (dtoFiles.length === 0) {\n console.log('✅ No Dto files changed');\n return { success: true };\n }\n\n console.log(`📂 Checking ${dtoFiles.length} changed file(s) for Dto definitions...`);\n\n const fullSchemaPath = path.join(workspaceRoot, prismaSchemaPath);\n const dboModels = parsePrismaSchema(fullSchemaPath);\n\n if (dboModels.size === 0) {\n console.log('⏭️ No Dbo models found in schema.prisma');\n console.log('');\n return { success: true };\n }\n\n console.log(` Found ${dboModels.size} Dbo model(s) in schema.prisma`);\n\n let allDtos = collectDtos(dtoFiles, workspaceRoot);\n\n if (allDtos.length === 0) {\n console.log('✅ No Dto definitions found in changed files');\n return { success: true };\n }\n\n // In MODIFIED_CLASS mode, narrow to only Dtos with changed lines\n if (mode === 'MODIFIED_CLASS') {\n allDtos = filterTouchedDtos(allDtos, workspaceRoot, base, head);\n if (allDtos.length === 0) {\n console.log('✅ No Dto classes were modified');\n return { success: true };\n }\n }\n\n console.log(` Validating ${allDtos.length} Dto definition(s)`);\n\n const violations = findViolations(allDtos, dboModels);\n\n if (violations.length === 0) {\n console.log('✅ All Dto fields match their Dbo models');\n return { success: true };\n }\n\n reportViolations(violations);\n return { success: false };\n}\n\n/**\n * Resolve mode considering turnOffRuleUntilEpoch override.\n * When active, downgrades to OFF. When expired, logs a warning.\n */\nfunction resolveMode(normalMode: PrismaValidateDtosMode, epoch: number | undefined, branchPattern: string | undefined): PrismaValidateDtosMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping validate-dtos (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(\n options: PrismaValidateDtosConfig,\n workspaceRoot: string\n): Promise<ExecutorResult> {\n const mode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined));\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping validate-dtos (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n const prismaSchemaPath = options.prismaSchemaPath;\n const dtoSourcePaths = options.dtoSourcePaths ?? [];\n\n if (!prismaSchemaPath || dtoSourcePaths.length === 0) {\n const reason = !prismaSchemaPath ? 'no prismaSchemaPath configured' : 'no dtoSourcePaths configured';\n console.log(`\\n⏭️ Skipping validate-dtos (${reason})`);\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating DTOs match Prisma Dbo models\\n');\n console.log(` Mode: ${mode}`);\n console.log(` Schema: ${prismaSchemaPath}`);\n console.log(` Dto paths: ${dtoSourcePaths.join(', ')}`);\n\n const base = resolveBase(workspaceRoot);\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n console.log('\\n⏭️ Skipping validate-dtos (could not detect base branch)');\n console.log('');\n return { success: true };\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, { tsOnly: false });\n\n return validateDtoFiles(workspaceRoot, prismaSchemaPath, changedFiles, dtoSourcePaths, mode, base, head);\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class PrismaValidateDtosValidator extends CodeValidator<PrismaValidateDtosConfig> {\n constructor(config: PrismaValidateDtosConfig) {\n super(config, 'prisma-validate-dtos');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}
|
|
@@ -218,7 +218,7 @@ function resolveMode(normalMode, epoch, branchPattern) {
|
|
|
218
218
|
}
|
|
219
219
|
// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
|
|
220
220
|
async function runValidatorImpl(options, workspaceRoot) {
|
|
221
|
-
const mode = resolveMode(options.mode ?? 'OFF', options.
|
|
221
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.turnOffRuleUntilEpoch, (options.turnOffRuleWhileOnBranch ?? undefined));
|
|
222
222
|
const disableAllowed = options.disableAllowed ?? true;
|
|
223
223
|
const allowedPaths = options.allowedPaths ?? [];
|
|
224
224
|
if (mode === 'OFF') {
|