@webpieces/code-rules 0.0.1 → 0.2.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/package.json +4 -3
  2. package/src/cli.d.ts +1 -0
  3. package/src/cli.js +19 -0
  4. package/src/cli.js.map +1 -0
  5. package/src/diff-utils.d.ts +24 -0
  6. package/src/{diff-utils.ts → diff-utils.js} +30 -38
  7. package/src/diff-utils.js.map +1 -0
  8. package/src/from-shared-config.d.ts +28 -0
  9. package/src/from-shared-config.js +119 -0
  10. package/src/from-shared-config.js.map +1 -0
  11. package/src/index.js +33 -0
  12. package/src/index.js.map +1 -0
  13. package/src/validate-catch-error-pattern.d.ts +47 -0
  14. package/src/{validate-catch-error-pattern.ts → validate-catch-error-pattern.js} +74 -195
  15. package/src/validate-catch-error-pattern.js.map +1 -0
  16. package/src/validate-code.d.ts +98 -0
  17. package/src/{validate-code.ts → validate-code.js} +65 -259
  18. package/src/validate-code.js.map +1 -0
  19. package/src/validate-dtos.d.ts +41 -0
  20. package/src/{validate-dtos.ts → validate-dtos.js} +88 -215
  21. package/src/validate-dtos.js.map +1 -0
  22. package/src/validate-modified-files.d.ts +24 -0
  23. package/src/{validate-modified-files.ts → validate-modified-files.js} +46 -115
  24. package/src/validate-modified-files.js.map +1 -0
  25. package/src/validate-modified-methods.d.ts +30 -0
  26. package/src/{validate-modified-methods.ts → validate-modified-methods.js} +94 -196
  27. package/src/validate-modified-methods.js.map +1 -0
  28. package/src/validate-new-methods.d.ts +27 -0
  29. package/src/{validate-new-methods.ts → validate-new-methods.js} +63 -133
  30. package/src/validate-new-methods.js.map +1 -0
  31. package/src/validate-no-any-unknown.d.ts +41 -0
  32. package/src/{validate-no-any-unknown.ts → validate-no-any-unknown.js} +69 -146
  33. package/src/validate-no-any-unknown.js.map +1 -0
  34. package/src/validate-no-destructure.d.ts +51 -0
  35. package/src/{validate-no-destructure.ts → validate-no-destructure.js} +80 -166
  36. package/src/validate-no-destructure.js.map +1 -0
  37. package/src/validate-no-direct-api-resolver.d.ts +46 -0
  38. package/src/{validate-no-direct-api-resolver.ts → validate-no-direct-api-resolver.js} +112 -211
  39. package/src/validate-no-direct-api-resolver.js.map +1 -0
  40. package/src/validate-no-implicit-any.d.ts +36 -0
  41. package/src/{validate-no-implicit-any.ts → validate-no-implicit-any.js} +94 -141
  42. package/src/validate-no-implicit-any.js.map +1 -0
  43. package/src/validate-no-inline-types.d.ts +90 -0
  44. package/src/{validate-no-inline-types.ts → validate-no-inline-types.js} +93 -198
  45. package/src/validate-no-inline-types.js.map +1 -0
  46. package/src/validate-no-unmanaged-exceptions.d.ts +43 -0
  47. package/src/{validate-no-unmanaged-exceptions.ts → validate-no-unmanaged-exceptions.js} +71 -140
  48. package/src/validate-no-unmanaged-exceptions.js.map +1 -0
  49. package/src/validate-prisma-converters.d.ts +59 -0
  50. package/src/{validate-prisma-converters.ts → validate-prisma-converters.js} +120 -307
  51. package/src/validate-prisma-converters.js.map +1 -0
  52. package/src/validate-return-types.d.ts +28 -0
  53. package/src/{validate-return-types.ts → validate-return-types.js} +84 -168
  54. package/src/validate-return-types.js.map +1 -0
  55. package/LICENSE +0 -373
  56. package/jest.config.ts +0 -20
  57. package/project.json +0 -22
  58. package/src/cli.ts +0 -17
  59. package/src/from-shared-config.ts +0 -118
  60. package/tsconfig.json +0 -22
  61. package/tsconfig.lib.json +0 -10
  62. package/tsconfig.spec.json +0 -14
  63. /package/src/{index.ts → index.d.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * Validate Prisma Converters Executor
3
4
  *
@@ -44,92 +45,58 @@
44
45
  * - NEW_AND_MODIFIED_METHODS: Validate new/modified methods in converters + changed lines in non-converters
45
46
  * - MODIFIED_FILES: Validate all methods in modified files
46
47
  */
47
-
48
- import { execSync } from 'child_process';
49
- import * as fs from 'fs';
50
- import * as path from 'path';
51
- import * as ts from 'typescript';
52
- import { getFileDiff, getChangedLineNumbers, findNewMethodSignaturesInDiff, isNewOrModified } from './diff-utils';
53
-
54
- export type PrismaConverterMode = 'OFF' | 'NEW_AND_MODIFIED_METHODS' | 'MODIFIED_FILES';
55
-
56
- export interface ValidatePrismaConvertersOptions {
57
- mode?: PrismaConverterMode;
58
- disableAllowed?: boolean;
59
- schemaPath?: string;
60
- convertersPaths?: string[];
61
- enforcePaths?: string[];
62
- ignoreModifiedUntilEpoch?: number;
63
- }
64
-
65
- export interface ExecutorResult {
66
- success: boolean;
67
- }
68
-
69
- interface PrismaConverterViolation {
70
- file: string;
71
- line: number;
72
- message: string;
73
- }
74
-
75
- interface UnwrapResult {
76
- inner: string;
77
- isAsync: boolean;
78
- }
79
-
80
- interface FileContext {
81
- filePath: string;
82
- fileLines: string[];
83
- sourceFile: ts.SourceFile;
84
- prismaModels: Set<string>;
85
- disableAllowed: boolean;
86
- }
87
-
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.default = runValidator;
50
+ const tslib_1 = require("tslib");
51
+ const child_process_1 = require("child_process");
52
+ const fs = tslib_1.__importStar(require("fs"));
53
+ const path = tslib_1.__importStar(require("path"));
54
+ const ts = tslib_1.__importStar(require("typescript"));
55
+ const diff_utils_1 = require("./diff-utils");
88
56
  /**
89
57
  * Auto-detect the base branch by finding the merge-base with origin/main.
90
58
  */
91
- function detectBase(workspaceRoot: string): string | null {
59
+ function detectBase(workspaceRoot) {
92
60
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
93
61
  try {
94
- const mergeBase = execSync('git merge-base HEAD origin/main', {
62
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD origin/main', {
95
63
  cwd: workspaceRoot,
96
64
  encoding: 'utf-8',
97
65
  stdio: ['pipe', 'pipe', 'pipe'],
98
66
  }).trim();
99
-
100
67
  if (mergeBase) {
101
68
  return mergeBase;
102
69
  }
103
- } catch (err: unknown) {
70
+ }
71
+ catch (err) {
104
72
  //const error = toError(err);
105
73
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
106
74
  try {
107
- const mergeBase = execSync('git merge-base HEAD main', {
75
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD main', {
108
76
  cwd: workspaceRoot,
109
77
  encoding: 'utf-8',
110
78
  stdio: ['pipe', 'pipe', 'pipe'],
111
79
  }).trim();
112
-
113
80
  if (mergeBase) {
114
81
  return mergeBase;
115
82
  }
116
- } catch (err2: unknown) {
83
+ }
84
+ catch (err2) {
117
85
  //const error2 = toError(err2);
118
86
  // Ignore
119
87
  }
120
88
  }
121
89
  return null;
122
90
  }
123
-
124
91
  /**
125
92
  * Get changed TypeScript files between base and head (or working tree if head not specified).
126
93
  */
127
94
  // webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths
128
- function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {
95
+ function getChangedTypeScriptFiles(workspaceRoot, base, head) {
129
96
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
130
97
  try {
131
98
  const diffTarget = head ? `${base} ${head}` : base;
132
- const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
99
+ const output = (0, child_process_1.execSync)(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
133
100
  cwd: workspaceRoot,
134
101
  encoding: 'utf-8',
135
102
  });
@@ -137,11 +104,10 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
137
104
  .trim()
138
105
  .split('\n')
139
106
  .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
140
-
141
107
  if (!head) {
142
108
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
143
109
  try {
144
- const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
110
+ const untrackedOutput = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
145
111
  cwd: workspaceRoot,
146
112
  encoding: 'utf-8',
147
113
  });
@@ -151,53 +117,48 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
151
117
  .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
152
118
  const allFiles = new Set([...changedFiles, ...untrackedFiles]);
153
119
  return Array.from(allFiles);
154
- } catch (err: unknown) {
120
+ }
121
+ catch (err) {
155
122
  //const error = toError(err);
156
123
  return changedFiles;
157
124
  }
158
125
  }
159
-
160
126
  return changedFiles;
161
- } catch (err: unknown) {
127
+ }
128
+ catch (err) {
162
129
  //const error = toError(err);
163
130
  return [];
164
131
  }
165
132
  }
166
-
167
133
  /**
168
134
  * Parse schema.prisma to extract all model names into a Set.
169
135
  */
170
- function parsePrismaModels(schemaPath: string): Set<string> {
171
- const models = new Set<string>();
172
-
136
+ function parsePrismaModels(schemaPath) {
137
+ const models = new Set();
173
138
  if (!fs.existsSync(schemaPath)) {
174
139
  return models;
175
140
  }
176
-
177
141
  const content = fs.readFileSync(schemaPath, 'utf-8');
178
142
  const regex = /^model\s+(\w+)\s*\{/gm;
179
- let match: RegExpExecArray | null;
180
-
143
+ let match;
181
144
  while ((match = regex.exec(content)) !== null) {
182
145
  models.add(match[1]);
183
146
  }
184
-
185
147
  return models;
186
148
  }
187
-
188
149
  /**
189
150
  * Derive the expected Dbo name from a return type ending in Dto.
190
151
  * "XxxDto" -> "XxxDbo". Returns null if name doesn't end with Dto.
191
152
  */
192
- function deriveExpectedDboName(returnType: string): string | null {
193
- if (!returnType.endsWith('Dto')) return null;
153
+ function deriveExpectedDboName(returnType) {
154
+ if (!returnType.endsWith('Dto'))
155
+ return null;
194
156
  return returnType.slice(0, -3) + 'Dbo';
195
157
  }
196
-
197
158
  /**
198
159
  * Check if a line has a webpieces-disable comment for prisma-converter.
199
160
  */
200
- function hasDisableComment(lines: string[], lineNumber: number): boolean {
161
+ function hasDisableComment(lines, lineNumber) {
201
162
  const startCheck = Math.max(0, lineNumber - 5);
202
163
  for (let i = lineNumber - 2; i >= startCheck; i--) {
203
164
  const line = lines[i]?.trim() ?? '';
@@ -210,76 +171,68 @@ function hasDisableComment(lines: string[], lineNumber: number): boolean {
210
171
  }
211
172
  return false;
212
173
  }
213
-
214
174
  /**
215
175
  * Check if a method/function node has a @deprecated decorator.
216
176
  */
217
- function hasDeprecatedDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration): boolean {
177
+ function hasDeprecatedDecorator(node) {
218
178
  const modifiers = ts.canHaveDecorators(node) ? ts.getDecorators(node) : undefined;
219
- if (!modifiers) return false;
220
-
179
+ if (!modifiers)
180
+ return false;
221
181
  for (const decorator of modifiers) {
222
182
  const expr = decorator.expression;
223
183
  // @deprecated or @deprecated()
224
- if (ts.isIdentifier(expr) && expr.text === 'deprecated') return true;
184
+ if (ts.isIdentifier(expr) && expr.text === 'deprecated')
185
+ return true;
225
186
  if (ts.isCallExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === 'deprecated') {
226
187
  return true;
227
188
  }
228
189
  }
229
190
  return false;
230
191
  }
231
-
232
192
  /**
233
193
  * Check if a node has @deprecated in its JSDoc comments.
234
194
  */
235
- function hasDeprecatedJsDoc(node: ts.Node): boolean {
195
+ function hasDeprecatedJsDoc(node) {
236
196
  const jsDocs = ts.getJSDocTags(node);
237
197
  for (const tag of jsDocs) {
238
- if (tag.tagName.text === 'deprecated') return true;
198
+ if (tag.tagName.text === 'deprecated')
199
+ return true;
239
200
  }
240
201
  return false;
241
202
  }
242
-
243
203
  /**
244
204
  * Check if a method is deprecated via decorator or JSDoc.
245
205
  */
246
- function isDeprecated(node: ts.MethodDeclaration | ts.FunctionDeclaration): boolean {
206
+ function isDeprecated(node) {
247
207
  return hasDeprecatedDecorator(node) || hasDeprecatedJsDoc(node);
248
208
  }
249
-
250
209
  /**
251
210
  * Extract the text of a type node, stripping whitespace.
252
211
  */
253
- function getTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): string {
212
+ function getTypeText(typeNode, sourceFile) {
254
213
  return typeNode.getText(sourceFile).trim();
255
214
  }
256
-
257
215
  /**
258
216
  * Unwrap Promise<T> to get T. Returns the inner type text if wrapped, otherwise returns as-is.
259
217
  */
260
- function unwrapPromise(typeText: string): UnwrapResult {
218
+ function unwrapPromise(typeText) {
261
219
  const promiseMatch = typeText.match(/^Promise\s*<\s*(.+)\s*>$/);
262
220
  if (promiseMatch) {
263
221
  return { inner: promiseMatch[1].trim(), isAsync: true };
264
222
  }
265
223
  return { inner: typeText, isAsync: false };
266
224
  }
267
-
268
225
  /**
269
226
  * Check a standalone function declaration in a converter file and return a violation if applicable.
270
227
  */
271
- function checkStandaloneFunction(
272
- node: ts.FunctionDeclaration,
273
- ctx: FileContext
274
- ): PrismaConverterViolation | null {
275
- if (!node.name) return null;
276
-
228
+ function checkStandaloneFunction(node, ctx) {
229
+ if (!node.name)
230
+ return null;
277
231
  const startPos = node.getStart(ctx.sourceFile);
278
232
  const pos = ctx.sourceFile.getLineAndCharacterOfPosition(startPos);
279
233
  const line = pos.line + 1;
280
-
281
- if ((ctx.disableAllowed && hasDisableComment(ctx.fileLines, line)) || isDeprecated(node)) return null;
282
-
234
+ if ((ctx.disableAllowed && hasDisableComment(ctx.fileLines, line)) || isDeprecated(node))
235
+ return null;
283
236
  return {
284
237
  file: ctx.filePath,
285
238
  line,
@@ -287,20 +240,12 @@ function checkStandaloneFunction(
287
240
  'Move to a converter class so it can be injected via DI.',
288
241
  };
289
242
  }
290
-
291
243
  /**
292
244
  * Validate the parameters of a converter method that returns a Dto with a matching Dbo.
293
245
  */
294
- function checkMethodParams(
295
- node: ts.MethodDeclaration,
296
- innerType: string,
297
- expectedDbo: string,
298
- ctx: FileContext,
299
- line: number
300
- ): PrismaConverterViolation[] {
301
- const violations: PrismaConverterViolation[] = [];
246
+ function checkMethodParams(node, innerType, expectedDbo, ctx, line) {
247
+ const violations = [];
302
248
  const params = node.parameters;
303
-
304
249
  if (params.length === 0) {
305
250
  violations.push({
306
251
  file: ctx.filePath,
@@ -310,7 +255,6 @@ function checkMethodParams(
310
255
  });
311
256
  return violations;
312
257
  }
313
-
314
258
  const firstParam = params[0];
315
259
  if (firstParam.type) {
316
260
  const firstParamType = getTypeText(firstParam.type, ctx.sourceFile);
@@ -323,7 +267,6 @@ function checkMethodParams(
323
267
  });
324
268
  }
325
269
  }
326
-
327
270
  for (let i = 1; i < params.length; i++) {
328
271
  const param = params[i];
329
272
  if (param.type) {
@@ -339,118 +282,89 @@ function checkMethodParams(
339
282
  }
340
283
  }
341
284
  }
342
-
343
285
  return violations;
344
286
  }
345
-
346
287
  /**
347
288
  * Check a class method declaration for converter pattern violations.
348
289
  */
349
- function checkConverterMethod(
350
- node: ts.MethodDeclaration,
351
- ctx: FileContext
352
- ): PrismaConverterViolation[] {
353
- if (!node.name || !node.type) return [];
354
-
290
+ function checkConverterMethod(node, ctx) {
291
+ if (!node.name || !node.type)
292
+ return [];
355
293
  const startPos = node.getStart(ctx.sourceFile);
356
294
  const pos = ctx.sourceFile.getLineAndCharacterOfPosition(startPos);
357
295
  const line = pos.line + 1;
358
-
359
- if ((ctx.disableAllowed && hasDisableComment(ctx.fileLines, line)) || isDeprecated(node)) return [];
360
-
296
+ if ((ctx.disableAllowed && hasDisableComment(ctx.fileLines, line)) || isDeprecated(node))
297
+ return [];
361
298
  const returnTypeText = getTypeText(node.type, ctx.sourceFile);
362
299
  const unwrapResult = unwrapPromise(returnTypeText);
363
300
  const innerType = unwrapResult.inner;
364
301
  const isAsync = unwrapResult.isAsync;
365
302
  const expectedDbo = deriveExpectedDboName(innerType);
366
-
367
- if (!expectedDbo || !ctx.prismaModels.has(expectedDbo)) return [];
368
-
303
+ if (!expectedDbo || !ctx.prismaModels.has(expectedDbo))
304
+ return [];
369
305
  if (isAsync) {
370
306
  return [{
371
- file: ctx.filePath,
372
- line,
373
- message: `Async converter method returning "Promise<${innerType}>" found. ` +
374
- 'Converters should be pure data mapping with no async work. Remove async/Promise.',
375
- }];
307
+ file: ctx.filePath,
308
+ line,
309
+ message: `Async converter method returning "Promise<${innerType}>" found. ` +
310
+ 'Converters should be pure data mapping with no async work. Remove async/Promise.',
311
+ }];
376
312
  }
377
-
378
313
  return checkMethodParams(node, innerType, expectedDbo, ctx, line);
379
314
  }
380
-
381
315
  /**
382
316
  * Find converter method violations in a single file.
383
317
  * Checks class methods for proper Dbo parameter patterns and flags standalone functions.
384
318
  */
385
- function findConverterViolationsInFile(
386
- filePath: string,
387
- workspaceRoot: string,
388
- prismaModels: Set<string>,
389
- disableAllowed: boolean
390
- ): PrismaConverterViolation[] {
319
+ function findConverterViolationsInFile(filePath, workspaceRoot, prismaModels, disableAllowed) {
391
320
  const fullPath = path.join(workspaceRoot, filePath);
392
- if (!fs.existsSync(fullPath)) return [];
393
-
321
+ if (!fs.existsSync(fullPath))
322
+ return [];
394
323
  const content = fs.readFileSync(fullPath, 'utf-8');
395
- const ctx: FileContext = {
324
+ const ctx = {
396
325
  filePath,
397
326
  fileLines: content.split('\n'),
398
327
  sourceFile: ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true),
399
328
  prismaModels,
400
329
  disableAllowed,
401
330
  };
402
-
403
- const violations: PrismaConverterViolation[] = [];
404
-
405
- function visitNode(node: ts.Node): void {
331
+ const violations = [];
332
+ function visitNode(node) {
406
333
  if (ts.isFunctionDeclaration(node)) {
407
334
  const violation = checkStandaloneFunction(node, ctx);
408
- if (violation) violations.push(violation);
335
+ if (violation)
336
+ violations.push(violation);
409
337
  }
410
-
411
338
  if (ts.isMethodDeclaration(node)) {
412
339
  violations.push(...checkConverterMethod(node, ctx));
413
340
  }
414
-
415
341
  ts.forEachChild(node, visitNode);
416
342
  }
417
-
418
343
  visitNode(ctx.sourceFile);
419
344
  return violations;
420
345
  }
421
-
422
346
  /**
423
347
  * Find violations in non-converter files: creating `new XxxDto(...)` where XxxDbo exists in prisma.
424
348
  * These Dto instances must only be created inside converter classes.
425
349
  */
426
350
  // webpieces-disable max-lines-new-methods -- AST traversal for new-expression detection with prisma model matching
427
- function findDtoCreationOutsideConverters(
428
- filePath: string,
429
- workspaceRoot: string,
430
- prismaModels: Set<string>,
431
- convertersPaths: string[],
432
- disableAllowed: boolean
433
- ): PrismaConverterViolation[] {
351
+ function findDtoCreationOutsideConverters(filePath, workspaceRoot, prismaModels, convertersPaths, disableAllowed) {
434
352
  const fullPath = path.join(workspaceRoot, filePath);
435
- if (!fs.existsSync(fullPath)) return [];
436
-
353
+ if (!fs.existsSync(fullPath))
354
+ return [];
437
355
  const content = fs.readFileSync(fullPath, 'utf-8');
438
356
  const fileLines = content.split('\n');
439
357
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
440
-
441
- const violations: PrismaConverterViolation[] = [];
442
-
443
- function visitNode(node: ts.Node): void {
358
+ const violations = [];
359
+ function visitNode(node) {
444
360
  // Detect `new XxxDto(...)` expressions
445
361
  if (ts.isNewExpression(node) && ts.isIdentifier(node.expression)) {
446
362
  const className = node.expression.text;
447
363
  const expectedDbo = deriveExpectedDboName(className);
448
-
449
364
  if (expectedDbo && prismaModels.has(expectedDbo)) {
450
365
  const startPos = node.getStart(sourceFile);
451
366
  const pos = sourceFile.getLineAndCharacterOfPosition(startPos);
452
367
  const line = pos.line + 1;
453
-
454
368
  if (!disableAllowed || !hasDisableComment(fileLines, line)) {
455
369
  const dirs = convertersPaths.map((p) => `"${p}"`).join(', ');
456
370
  violations.push({
@@ -462,99 +376,73 @@ function findDtoCreationOutsideConverters(
462
376
  }
463
377
  }
464
378
  }
465
-
466
379
  ts.forEachChild(node, visitNode);
467
380
  }
468
-
469
381
  visitNode(sourceFile);
470
382
  return violations;
471
383
  }
472
-
473
384
  /**
474
385
  * Find converter violations only for new/modified methods (NEW_AND_MODIFIED_METHODS mode).
475
386
  * For converter files: only check methods/functions that are new or have changed lines in their range.
476
387
  */
477
388
  // webpieces-disable max-lines-new-methods -- AST traversal with method boundary filtering for new/modified detection
478
- function findConverterViolationsForModifiedMethods(
479
- filePath: string,
480
- workspaceRoot: string,
481
- prismaModels: Set<string>,
482
- disableAllowed: boolean,
483
- changedLines: Set<number>,
484
- newMethodNames: Set<string>
485
- ): PrismaConverterViolation[] {
389
+ function findConverterViolationsForModifiedMethods(filePath, workspaceRoot, prismaModels, disableAllowed, changedLines, newMethodNames) {
486
390
  const fullPath = path.join(workspaceRoot, filePath);
487
- if (!fs.existsSync(fullPath)) return [];
488
-
391
+ if (!fs.existsSync(fullPath))
392
+ return [];
489
393
  const content = fs.readFileSync(fullPath, 'utf-8');
490
- const ctx: FileContext = {
394
+ const ctx = {
491
395
  filePath,
492
396
  fileLines: content.split('\n'),
493
397
  sourceFile: ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true),
494
398
  prismaModels,
495
399
  disableAllowed,
496
400
  };
497
-
498
- const violations: PrismaConverterViolation[] = [];
499
-
500
- function visitNode(node: ts.Node): void {
401
+ const violations = [];
402
+ function visitNode(node) {
501
403
  if (ts.isFunctionDeclaration(node) && node.name) {
502
404
  const start = ctx.sourceFile.getLineAndCharacterOfPosition(node.getStart(ctx.sourceFile));
503
405
  const end = ctx.sourceFile.getLineAndCharacterOfPosition(node.getEnd());
504
- if (isNewOrModified(node.name.text, start.line + 1, end.line + 1, changedLines, newMethodNames)) {
406
+ if ((0, diff_utils_1.isNewOrModified)(node.name.text, start.line + 1, end.line + 1, changedLines, newMethodNames)) {
505
407
  const violation = checkStandaloneFunction(node, ctx);
506
- if (violation) violations.push(violation);
408
+ if (violation)
409
+ violations.push(violation);
507
410
  }
508
411
  }
509
-
510
412
  if (ts.isMethodDeclaration(node) && node.name) {
511
413
  const start = ctx.sourceFile.getLineAndCharacterOfPosition(node.getStart(ctx.sourceFile));
512
414
  const end = ctx.sourceFile.getLineAndCharacterOfPosition(node.getEnd());
513
415
  const methodName = node.name.getText(ctx.sourceFile);
514
- if (isNewOrModified(methodName, start.line + 1, end.line + 1, changedLines, newMethodNames)) {
416
+ if ((0, diff_utils_1.isNewOrModified)(methodName, start.line + 1, end.line + 1, changedLines, newMethodNames)) {
515
417
  violations.push(...checkConverterMethod(node, ctx));
516
418
  }
517
419
  }
518
-
519
420
  ts.forEachChild(node, visitNode);
520
421
  }
521
-
522
422
  visitNode(ctx.sourceFile);
523
423
  return violations;
524
424
  }
525
-
526
425
  /**
527
426
  * Find Dto creation violations only on changed lines (NEW_AND_MODIFIED_METHODS mode).
528
427
  * For non-converter files: only flag `new XxxDto(...)` on changed lines in the diff.
529
428
  */
530
429
  // webpieces-disable max-lines-new-methods -- AST traversal for new-expression detection with changed-line filtering
531
- function findDtoCreationOnChangedLines(
532
- filePath: string,
533
- workspaceRoot: string,
534
- prismaModels: Set<string>,
535
- convertersPaths: string[],
536
- disableAllowed: boolean,
537
- changedLines: Set<number>
538
- ): PrismaConverterViolation[] {
430
+ function findDtoCreationOnChangedLines(filePath, workspaceRoot, prismaModels, convertersPaths, disableAllowed, changedLines) {
539
431
  const fullPath = path.join(workspaceRoot, filePath);
540
- if (!fs.existsSync(fullPath)) return [];
541
-
432
+ if (!fs.existsSync(fullPath))
433
+ return [];
542
434
  const content = fs.readFileSync(fullPath, 'utf-8');
543
435
  const fileLines = content.split('\n');
544
436
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
545
-
546
- const violations: PrismaConverterViolation[] = [];
547
-
548
- function visitNode(node: ts.Node): void {
437
+ const violations = [];
438
+ function visitNode(node) {
549
439
  if (ts.isNewExpression(node) && ts.isIdentifier(node.expression)) {
550
440
  const className = node.expression.text;
551
441
  const expectedDbo = deriveExpectedDboName(className);
552
-
553
442
  if (expectedDbo && prismaModels.has(expectedDbo)) {
554
443
  const startPos = node.getStart(sourceFile);
555
444
  const pos = sourceFile.getLineAndCharacterOfPosition(startPos);
556
445
  const line = pos.line + 1;
557
-
558
446
  if (changedLines.has(line) && (!disableAllowed || !hasDisableComment(fileLines, line))) {
559
447
  const dirs = convertersPaths.map((p) => `"${p}"`).join(', ');
560
448
  violations.push({
@@ -566,70 +454,48 @@ function findDtoCreationOnChangedLines(
566
454
  }
567
455
  }
568
456
  }
569
-
570
457
  ts.forEachChild(node, visitNode);
571
458
  }
572
-
573
459
  visitNode(sourceFile);
574
460
  return violations;
575
461
  }
576
-
577
462
  /**
578
463
  * Collect violations for NEW_AND_MODIFIED_METHODS mode.
579
464
  * Converter files: method-level — only check new/modified methods.
580
465
  * Non-converter files: line-level — only flag new XxxDto() on changed lines.
581
466
  */
582
467
  // webpieces-disable max-lines-new-methods -- File classification and diff-based violation collection
583
- function collectViolationsForModifiedMethodAndCode(
584
- changedFiles: string[],
585
- convertersPaths: string[],
586
- workspaceRoot: string,
587
- prismaModels: Set<string>,
588
- disableAllowed: boolean,
589
- base: string,
590
- head: string | undefined
591
- ): PrismaConverterViolation[] {
592
- const converterFiles = changedFiles.filter((f) =>
593
- convertersPaths.some((cp) => f.startsWith(cp))
594
- );
595
- const nonConverterFiles = changedFiles.filter((f) =>
596
- !convertersPaths.some((cp) => f.startsWith(cp))
597
- );
598
-
599
- const allViolations: PrismaConverterViolation[] = [];
600
-
468
+ function collectViolationsForModifiedMethodAndCode(changedFiles, convertersPaths, workspaceRoot, prismaModels, disableAllowed, base, head) {
469
+ const converterFiles = changedFiles.filter((f) => convertersPaths.some((cp) => f.startsWith(cp)));
470
+ const nonConverterFiles = changedFiles.filter((f) => !convertersPaths.some((cp) => f.startsWith(cp)));
471
+ const allViolations = [];
601
472
  if (converterFiles.length > 0) {
602
473
  console.log(`📂 Checking ${converterFiles.length} converter file(s) (new/modified methods only)...`);
603
474
  for (const file of converterFiles) {
604
- const diff = getFileDiff(workspaceRoot, file, base, head);
605
- const changedLines = getChangedLineNumbers(diff);
606
- const newMethodNames = findNewMethodSignaturesInDiff(diff);
607
- if (changedLines.size === 0 && newMethodNames.size === 0) continue;
608
- allViolations.push(...findConverterViolationsForModifiedMethods(
609
- file, workspaceRoot, prismaModels, disableAllowed, changedLines, newMethodNames
610
- ));
475
+ const diff = (0, diff_utils_1.getFileDiff)(workspaceRoot, file, base, head);
476
+ const changedLines = (0, diff_utils_1.getChangedLineNumbers)(diff);
477
+ const newMethodNames = (0, diff_utils_1.findNewMethodSignaturesInDiff)(diff);
478
+ if (changedLines.size === 0 && newMethodNames.size === 0)
479
+ continue;
480
+ allViolations.push(...findConverterViolationsForModifiedMethods(file, workspaceRoot, prismaModels, disableAllowed, changedLines, newMethodNames));
611
481
  }
612
482
  }
613
-
614
483
  if (nonConverterFiles.length > 0) {
615
484
  console.log(`📂 Checking ${nonConverterFiles.length} non-converter file(s) for Dto creation (changed lines only)...`);
616
485
  for (const file of nonConverterFiles) {
617
- const diff = getFileDiff(workspaceRoot, file, base, head);
618
- const changedLines = getChangedLineNumbers(diff);
619
- if (changedLines.size === 0) continue;
620
- allViolations.push(...findDtoCreationOnChangedLines(
621
- file, workspaceRoot, prismaModels, convertersPaths, disableAllowed, changedLines
622
- ));
486
+ const diff = (0, diff_utils_1.getFileDiff)(workspaceRoot, file, base, head);
487
+ const changedLines = (0, diff_utils_1.getChangedLineNumbers)(diff);
488
+ if (changedLines.size === 0)
489
+ continue;
490
+ allViolations.push(...findDtoCreationOnChangedLines(file, workspaceRoot, prismaModels, convertersPaths, disableAllowed, changedLines));
623
491
  }
624
492
  }
625
-
626
493
  return allViolations;
627
494
  }
628
-
629
495
  /**
630
496
  * Report violations to console.
631
497
  */
632
- function reportViolations(violations: PrismaConverterViolation[], mode: PrismaConverterMode): void {
498
+ function reportViolations(violations, mode) {
633
499
  console.error('');
634
500
  console.error('❌ Prisma converter violations found!');
635
501
  console.error('');
@@ -646,133 +512,90 @@ function reportViolations(violations: PrismaConverterViolation[], mode: PrismaCo
646
512
  console.error(' BAD: convertUser(dbo: UserDbo, name: string): UserDto { } // extra non-boolean');
647
513
  console.error(' BAD: export function convertSession(s: SessionDbo): SessionDto // standalone function');
648
514
  console.error('');
649
-
650
515
  for (const v of violations) {
651
516
  console.error(` ❌ ${v.file}:${v.line}`);
652
517
  console.error(` ${v.message}`);
653
518
  }
654
519
  console.error('');
655
-
656
520
  console.error(' Escape hatch (use sparingly):');
657
521
  console.error(' // webpieces-disable prisma-converter -- [your reason]');
658
522
  console.error('');
659
523
  console.error(` Current mode: ${mode}`);
660
524
  console.error('');
661
525
  }
662
-
663
526
  /**
664
527
  * Resolve git base ref from env vars or auto-detection.
665
528
  */
666
- function resolveBase(workspaceRoot: string): string | undefined {
529
+ function resolveBase(workspaceRoot) {
667
530
  const envBase = process.env['NX_BASE'];
668
- if (envBase) return envBase;
531
+ if (envBase)
532
+ return envBase;
669
533
  return detectBase(workspaceRoot) ?? undefined;
670
534
  }
671
-
672
535
  /**
673
536
  * Collect all violations from converter and non-converter files.
674
537
  */
675
- function collectAllViolations(
676
- changedFiles: string[],
677
- convertersPaths: string[],
678
- workspaceRoot: string,
679
- prismaModels: Set<string>,
680
- disableAllowed: boolean
681
- ): PrismaConverterViolation[] {
682
- const converterFiles = changedFiles.filter((f) =>
683
- convertersPaths.some((cp) => f.startsWith(cp))
684
- );
685
- const nonConverterFiles = changedFiles.filter((f) =>
686
- !convertersPaths.some((cp) => f.startsWith(cp))
687
- );
688
-
689
- const allViolations: PrismaConverterViolation[] = [];
690
-
538
+ function collectAllViolations(changedFiles, convertersPaths, workspaceRoot, prismaModels, disableAllowed) {
539
+ const converterFiles = changedFiles.filter((f) => convertersPaths.some((cp) => f.startsWith(cp)));
540
+ const nonConverterFiles = changedFiles.filter((f) => !convertersPaths.some((cp) => f.startsWith(cp)));
541
+ const allViolations = [];
691
542
  if (converterFiles.length > 0) {
692
543
  console.log(`📂 Checking ${converterFiles.length} converter file(s)...`);
693
544
  for (const file of converterFiles) {
694
545
  allViolations.push(...findConverterViolationsInFile(file, workspaceRoot, prismaModels, disableAllowed));
695
546
  }
696
547
  }
697
-
698
548
  if (nonConverterFiles.length > 0) {
699
549
  console.log(`📂 Checking ${nonConverterFiles.length} non-converter file(s) for Dto creation...`);
700
550
  for (const file of nonConverterFiles) {
701
551
  allViolations.push(...findDtoCreationOutsideConverters(file, workspaceRoot, prismaModels, convertersPaths, disableAllowed));
702
552
  }
703
553
  }
704
-
705
554
  return allViolations;
706
555
  }
707
-
708
556
  /**
709
557
  * Run validation after early-exit checks have passed.
710
558
  */
711
- function validateChangedFiles(
712
- workspaceRoot: string,
713
- schemaPath: string,
714
- convertersPaths: string[],
715
- enforcePaths: string[],
716
- base: string,
717
- mode: PrismaConverterMode,
718
- disableAllowed: boolean
719
- ): ExecutorResult {
559
+ function validateChangedFiles(workspaceRoot, schemaPath, convertersPaths, enforcePaths, base, mode, disableAllowed) {
720
560
  const head = process.env['NX_HEAD'];
721
-
722
561
  console.log(` Base: ${base}`);
723
562
  console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
724
563
  console.log('');
725
-
726
564
  const fullSchemaPath = path.join(workspaceRoot, schemaPath);
727
565
  const prismaModels = parsePrismaModels(fullSchemaPath);
728
-
729
566
  if (prismaModels.size === 0) {
730
567
  console.log('⏭️ No models found in schema.prisma');
731
568
  console.log('');
732
569
  return { success: true };
733
570
  }
734
-
735
571
  console.log(` Found ${prismaModels.size} model(s) in schema.prisma`);
736
-
737
572
  let changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
738
573
  if (enforcePaths.length > 0) {
739
- changedFiles = changedFiles.filter((f) =>
740
- enforcePaths.some((ep) => f.startsWith(ep))
741
- );
574
+ changedFiles = changedFiles.filter((f) => enforcePaths.some((ep) => f.startsWith(ep)));
742
575
  }
743
-
744
576
  if (changedFiles.length === 0) {
745
577
  console.log('✅ No TypeScript files changed');
746
578
  return { success: true };
747
579
  }
748
-
749
- let allViolations: PrismaConverterViolation[];
750
-
580
+ let allViolations;
751
581
  if (mode === 'NEW_AND_MODIFIED_METHODS') {
752
- allViolations = collectViolationsForModifiedMethodAndCode(
753
- changedFiles, convertersPaths, workspaceRoot, prismaModels, disableAllowed, base, head
754
- );
755
- } else {
582
+ allViolations = collectViolationsForModifiedMethodAndCode(changedFiles, convertersPaths, workspaceRoot, prismaModels, disableAllowed, base, head);
583
+ }
584
+ else {
756
585
  allViolations = collectAllViolations(changedFiles, convertersPaths, workspaceRoot, prismaModels, disableAllowed);
757
586
  }
758
-
759
587
  if (allViolations.length === 0) {
760
588
  console.log('✅ All converter patterns are valid');
761
589
  return { success: true };
762
590
  }
763
-
764
591
  reportViolations(allViolations, mode);
765
592
  return { success: false };
766
593
  }
767
-
768
594
  /**
769
595
  * Resolve mode considering ignoreModifiedUntilEpoch override.
770
596
  * When active, downgrades to OFF. When expired, logs a warning.
771
597
  */
772
- function resolvePrismaConverterMode(
773
- normalMode: PrismaConverterMode,
774
- epoch: number | undefined
775
- ): PrismaConverterMode {
598
+ function resolvePrismaConverterMode(normalMode, epoch) {
776
599
  if (epoch === undefined || normalMode === 'OFF') {
777
600
  return normalMode;
778
601
  }
@@ -785,30 +608,22 @@ function resolvePrismaConverterMode(
785
608
  }
786
609
  return normalMode;
787
610
  }
788
-
789
- export default async function runValidator(
790
- options: ValidatePrismaConvertersOptions,
791
- workspaceRoot: string
792
- ): Promise<ExecutorResult> {
611
+ async function runValidator(options, workspaceRoot) {
793
612
  const mode = resolvePrismaConverterMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch);
794
-
795
613
  if (mode === 'OFF') {
796
614
  console.log('\n⏭️ Skipping prisma-converter validation (mode: OFF)');
797
615
  console.log('');
798
616
  return { success: true };
799
617
  }
800
-
801
618
  const schemaPath = options.schemaPath;
802
619
  const convertersPaths = options.convertersPaths ?? [];
803
620
  const enforcePaths = options.enforcePaths ?? [];
804
-
805
621
  if (!schemaPath || convertersPaths.length === 0) {
806
622
  const reason = !schemaPath ? 'no schemaPath configured' : 'no convertersPaths configured';
807
623
  console.log(`\n⏭️ Skipping prisma-converter validation (${reason})`);
808
624
  console.log('');
809
625
  return { success: true };
810
626
  }
811
-
812
627
  console.log('\n📏 Validating Prisma Converters\n');
813
628
  console.log(` Mode: ${mode}`);
814
629
  console.log(` Schema: ${schemaPath}`);
@@ -816,15 +631,13 @@ export default async function runValidator(
816
631
  if (enforcePaths.length > 0) {
817
632
  console.log(` Enforce paths: ${enforcePaths.join(', ')}`);
818
633
  }
819
-
820
634
  const base = resolveBase(workspaceRoot);
821
-
822
635
  if (!base) {
823
636
  console.log('\n⏭️ Skipping prisma-converter validation (could not detect base branch)');
824
637
  console.log('');
825
638
  return { success: true };
826
639
  }
827
-
828
640
  const disableAllowed = options.disableAllowed ?? true;
829
641
  return validateChangedFiles(workspaceRoot, schemaPath, convertersPaths, enforcePaths, base, mode, disableAllowed);
830
642
  }
643
+ //# sourceMappingURL=validate-prisma-converters.js.map