@webpieces/dev-config 0.2.84 → 0.2.86

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 (31) hide show
  1. package/architecture/executors/diff-utils.d.ts +24 -0
  2. package/architecture/executors/diff-utils.js +119 -0
  3. package/architecture/executors/diff-utils.js.map +1 -0
  4. package/architecture/executors/diff-utils.ts +127 -0
  5. package/architecture/executors/validate-code/executor.d.ts +7 -0
  6. package/architecture/executors/validate-code/executor.js +57 -2
  7. package/architecture/executors/validate-code/executor.js.map +1 -1
  8. package/architecture/executors/validate-code/executor.ts +72 -2
  9. package/architecture/executors/validate-code/schema.json +21 -8
  10. package/architecture/executors/validate-no-destructure/executor.d.ts +52 -0
  11. package/architecture/executors/validate-no-destructure/executor.js +493 -0
  12. package/architecture/executors/validate-no-destructure/executor.js.map +1 -0
  13. package/architecture/executors/validate-no-destructure/executor.ts +580 -0
  14. package/architecture/executors/validate-no-destructure/schema.json +24 -0
  15. package/architecture/executors/validate-no-inline-types/executor.d.ts +2 -2
  16. package/architecture/executors/validate-no-inline-types/executor.js +3 -3
  17. package/architecture/executors/validate-no-inline-types/executor.js.map +1 -1
  18. package/architecture/executors/validate-no-inline-types/executor.ts +4 -4
  19. package/architecture/executors/validate-no-inline-types/schema.json +2 -2
  20. package/architecture/executors/validate-prisma-converters/executor.d.ts +4 -3
  21. package/architecture/executors/validate-prisma-converters/executor.js +122 -3
  22. package/architecture/executors/validate-prisma-converters/executor.js.map +1 -1
  23. package/architecture/executors/validate-prisma-converters/executor.ts +170 -4
  24. package/architecture/executors/validate-prisma-converters/schema.json +2 -2
  25. package/architecture/executors/validate-return-types/executor.d.ts +2 -2
  26. package/architecture/executors/validate-return-types/executor.js +3 -3
  27. package/architecture/executors/validate-return-types/executor.js.map +1 -1
  28. package/architecture/executors/validate-return-types/executor.ts +4 -4
  29. package/architecture/executors/validate-return-types/schema.json +2 -2
  30. package/executors.json +5 -0
  31. package/package.json +1 -1
@@ -7,6 +7,7 @@ import runNoInlineTypesExecutor, { NoInlineTypesMode } from '../validate-no-inli
7
7
  import runNoAnyUnknownExecutor, { NoAnyUnknownMode } from '../validate-no-any-unknown/executor';
8
8
  import runValidateDtosExecutor, { ValidateDtosMode } from '../validate-dtos/executor';
9
9
  import runPrismaConvertersExecutor, { PrismaConverterMode } from '../validate-prisma-converters/executor';
10
+ import runNoDestructureExecutor, { NoDestructureMode } from '../validate-no-destructure/executor';
10
11
 
11
12
  export type MethodMaxLimitMode = 'OFF' | 'NEW_METHODS' | 'NEW_AND_MODIFIED_METHODS' | 'MODIFIED_FILES';
12
13
  export type FileMaxLimitMode = 'OFF' | 'MODIFIED_FILES';
@@ -59,6 +60,12 @@ export interface PrismaConverterConfig {
59
60
  ignoreModifiedUntilEpoch?: number;
60
61
  }
61
62
 
63
+ export interface NoDestructureConfig {
64
+ mode?: NoDestructureMode;
65
+ disableAllowed?: boolean;
66
+ ignoreModifiedUntilEpoch?: number;
67
+ }
68
+
62
69
  export interface ValidateCodeOptions {
63
70
  methodMaxLimit?: MethodMaxLimitConfig;
64
71
  fileMaxLimit?: FileMaxLimitConfig;
@@ -67,6 +74,7 @@ export interface ValidateCodeOptions {
67
74
  noAnyUnknown?: NoAnyUnknownConfig;
68
75
  validateDtos?: ValidateDtosConfig;
69
76
  prismaConverter?: PrismaConverterConfig;
77
+ noDestructure?: NoDestructureConfig;
70
78
  }
71
79
 
72
80
  export interface ExecutorResult {
@@ -107,6 +115,9 @@ interface ParsedConfig {
107
115
  prismaConverterSchemaPath: string | undefined;
108
116
  prismaConverterConvertersPaths: string[];
109
117
  prismaConverterIgnoreEpoch: number | undefined;
118
+ noDestructureMode: NoDestructureMode;
119
+ noDestructureDisableAllowed: boolean;
120
+ noDestructureIgnoreEpoch: number | undefined;
110
121
  }
111
122
 
112
123
  interface ResolvedMethodMode {
@@ -119,6 +130,45 @@ interface ResolvedFileMode {
119
130
  override: OverrideInfo | undefined;
120
131
  }
121
132
 
133
+ const VALID_MODES: Record<string, string[]> = {
134
+ methodMaxLimit: ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'MODIFIED_FILES'],
135
+ fileMaxLimit: ['OFF', 'MODIFIED_FILES'],
136
+ requireReturnType: ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'MODIFIED_FILES'],
137
+ noInlineTypeLiterals: ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'MODIFIED_FILES'],
138
+ noAnyUnknown: ['OFF', 'MODIFIED_CODE', 'MODIFIED_FILES'],
139
+ validateDtos: ['OFF', 'MODIFIED_CLASS', 'MODIFIED_FILES'],
140
+ prismaConverter: ['OFF', 'MODIFIED_METHOD_AND_CODE', 'MODIFIED_FILES'],
141
+ noDestructure: ['OFF', 'MODIFIED_CODE', 'MODIFIED_FILES'],
142
+ };
143
+
144
+ /**
145
+ * Validate that all configured modes are valid. Produces clear error messages naming the rule.
146
+ */
147
+ function validateModes(options: ValidateCodeOptions): string[] {
148
+ const errors: string[] = [];
149
+
150
+ const modeEntries: [string, string | undefined][] = [
151
+ ['methodMaxLimit', options.methodMaxLimit?.mode],
152
+ ['fileMaxLimit', options.fileMaxLimit?.mode],
153
+ ['requireReturnType', options.requireReturnType?.mode],
154
+ ['noInlineTypeLiterals', options.noInlineTypeLiterals?.mode],
155
+ ['noAnyUnknown', options.noAnyUnknown?.mode],
156
+ ['validateDtos', options.validateDtos?.mode],
157
+ ['prismaConverter', options.prismaConverter?.mode],
158
+ ['noDestructure', options.noDestructure?.mode],
159
+ ];
160
+
161
+ for (const [ruleName, modeValue] of modeEntries) {
162
+ if (modeValue === undefined) continue;
163
+ const validModes = VALID_MODES[ruleName];
164
+ if (!validModes.includes(modeValue)) {
165
+ errors.push(`${ruleName}.mode = '${modeValue}' is invalid. Valid modes: ${validModes.join(', ')}`);
166
+ }
167
+ }
168
+
169
+ return errors;
170
+ }
171
+
122
172
  function formatEpochDate(epoch: number): string {
123
173
  return new Date(epoch * 1000).toISOString().split('T')[0];
124
174
  }
@@ -201,6 +251,9 @@ function parseConfig(options: ValidateCodeOptions): ParsedConfig {
201
251
  prismaConverterSchemaPath: options.prismaConverter?.schemaPath,
202
252
  prismaConverterConvertersPaths: options.prismaConverter?.convertersPaths ?? [],
203
253
  prismaConverterIgnoreEpoch: options.prismaConverter?.ignoreModifiedUntilEpoch,
254
+ noDestructureMode: options.noDestructure?.mode ?? 'OFF',
255
+ noDestructureDisableAllowed: options.noDestructure?.disableAllowed ?? true,
256
+ noDestructureIgnoreEpoch: options.noDestructure?.ignoreModifiedUntilEpoch,
204
257
  };
205
258
  }
206
259
 
@@ -220,6 +273,7 @@ function logConfig(config: ParsedConfig): void {
220
273
  console.log(` No any/unknown: mode=${config.noAnyUnknownMode}, disableAllowed=${config.noAnyUnknownDisableAllowed}`);
221
274
  console.log(` Validate DTOs: mode=${config.validateDtosMode}, disableAllowed=${config.validateDtosDisableAllowed}`);
222
275
  console.log(` Prisma converters: mode=${config.prismaConverterMode}, disableAllowed=${config.prismaConverterDisableAllowed}`);
276
+ console.log(` No destructure: mode=${config.noDestructureMode}, disableAllowed=${config.noDestructureDisableAllowed}`);
223
277
  console.log('');
224
278
  }
225
279
 
@@ -227,7 +281,7 @@ function isAllOff(config: ParsedConfig): boolean {
227
281
  return config.methodMode === 'OFF' && config.fileMode === 'OFF' &&
228
282
  config.returnTypeMode === 'OFF' && config.noInlineTypesMode === 'OFF' &&
229
283
  config.noAnyUnknownMode === 'OFF' && config.validateDtosMode === 'OFF' &&
230
- config.prismaConverterMode === 'OFF';
284
+ config.prismaConverterMode === 'OFF' && config.noDestructureMode === 'OFF';
231
285
  }
232
286
 
233
287
  async function runMethodValidators(config: ParsedConfig, context: ExecutorContext): Promise<ExecutorResult[]> {
@@ -253,6 +307,16 @@ export default async function runExecutor(
253
307
  options: ValidateCodeOptions,
254
308
  context: ExecutorContext
255
309
  ): Promise<ExecutorResult> {
310
+ const modeErrors = validateModes(options);
311
+ if (modeErrors.length > 0) {
312
+ console.error('');
313
+ for (const err of modeErrors) {
314
+ console.error(`❌ ${err}`);
315
+ }
316
+ console.error('');
317
+ return { success: false };
318
+ }
319
+
256
320
  const config = parseConfig(options);
257
321
 
258
322
  if (isAllOff(config)) {
@@ -295,11 +359,17 @@ export default async function runExecutor(
295
359
  convertersPaths: config.prismaConverterConvertersPaths,
296
360
  ignoreModifiedUntilEpoch: config.prismaConverterIgnoreEpoch,
297
361
  }, context);
362
+ const noDestructureResult = await runNoDestructureExecutor({
363
+ mode: config.noDestructureMode,
364
+ disableAllowed: config.noDestructureDisableAllowed,
365
+ ignoreModifiedUntilEpoch: config.noDestructureIgnoreEpoch,
366
+ }, context);
298
367
 
299
368
  const allSuccess = methodResults.every((r) => r.success) &&
300
369
  fileResult.success && returnTypesResult.success &&
301
370
  noInlineTypesResult.success && noAnyUnknownResult.success &&
302
- validateDtosResult.success && prismaConverterResult.success;
371
+ validateDtosResult.success && prismaConverterResult.success &&
372
+ noDestructureResult.success;
303
373
 
304
374
  console.log(allSuccess ? '\n\u2705 All code validations passed\n' : '\n\u274c Some code validations failed\n');
305
375
  return { success: allSuccess };
@@ -15,7 +15,6 @@
15
15
  },
16
16
  "mode": {
17
17
  "type": "string",
18
- "enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
19
18
  "description": "OFF: skip validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
20
19
  "default": "NEW_AND_MODIFIED_METHODS"
21
20
  },
@@ -41,7 +40,6 @@
41
40
  },
42
41
  "mode": {
43
42
  "type": "string",
44
- "enum": ["OFF", "MODIFIED_FILES"],
45
43
  "description": "OFF: skip validation. MODIFIED_FILES: all code in modified files.",
46
44
  "default": "MODIFIED_FILES"
47
45
  },
@@ -62,7 +60,6 @@
62
60
  "properties": {
63
61
  "mode": {
64
62
  "type": "string",
65
- "enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
66
63
  "description": "OFF: skip return type validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
67
64
  "default": "OFF"
68
65
  },
@@ -83,7 +80,6 @@
83
80
  "properties": {
84
81
  "mode": {
85
82
  "type": "string",
86
- "enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
87
83
  "description": "OFF: skip validation. NEW_METHODS: only new methods. NEW_AND_MODIFIED_METHODS: new + modified methods. MODIFIED_FILES: all in modified files. Disallows inline type literals like { x: number } - use named types instead.",
88
84
  "default": "OFF"
89
85
  },
@@ -104,7 +100,6 @@
104
100
  "properties": {
105
101
  "mode": {
106
102
  "type": "string",
107
- "enum": ["OFF", "MODIFIED_CODE", "MODIFIED_FILES"],
108
103
  "description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows `any` and `unknown` TypeScript keywords.",
109
104
  "default": "OFF"
110
105
  },
@@ -125,7 +120,6 @@
125
120
  "properties": {
126
121
  "mode": {
127
122
  "type": "string",
128
- "enum": ["OFF", "MODIFIED_CLASS", "MODIFIED_FILES"],
129
123
  "description": "OFF: skip validation. MODIFIED_CLASS: only validate Dto classes with changed lines. MODIFIED_FILES: validate all Dtos in modified files.",
130
124
  "default": "OFF"
131
125
  },
@@ -155,8 +149,7 @@
155
149
  "properties": {
156
150
  "mode": {
157
151
  "type": "string",
158
- "enum": ["OFF", "MODIFIED_FILES"],
159
- "description": "OFF: skip validation. MODIFIED_FILES: validate converter and non-converter files modified in the diff.",
152
+ "description": "OFF: skip validation. MODIFIED_METHOD_AND_CODE: validate new/modified methods in converters + changed lines in non-converters. MODIFIED_FILES: validate all methods in modified files.",
160
153
  "default": "OFF"
161
154
  },
162
155
  "disableAllowed": {
@@ -178,6 +171,26 @@
178
171
  "description": "Epoch seconds. Until this time, skip prisma-converter validation entirely. When expired, normal mode resumes. Omit when not needed."
179
172
  }
180
173
  }
174
+ },
175
+ "noDestructure": {
176
+ "type": "object",
177
+ "description": "Validate no destructuring patterns are used. Destructuring hurts code traceability.",
178
+ "properties": {
179
+ "mode": {
180
+ "type": "string",
181
+ "description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows destructuring patterns.",
182
+ "default": "OFF"
183
+ },
184
+ "disableAllowed": {
185
+ "type": "boolean",
186
+ "description": "Whether disable comments work. When false, no escape hatch.",
187
+ "default": true
188
+ },
189
+ "ignoreModifiedUntilEpoch": {
190
+ "type": "number",
191
+ "description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
192
+ }
193
+ }
181
194
  }
182
195
  },
183
196
  "required": []
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Validate No Destructure Executor
3
+ *
4
+ * Validates that destructuring patterns are not used in TypeScript code.
5
+ * Uses LINE-BASED detection (not method-based) for git diff filtering.
6
+ *
7
+ * ============================================================================
8
+ * VIOLATIONS (BAD) - These patterns are flagged:
9
+ * ============================================================================
10
+ *
11
+ * - const { x, y } = obj — object destructuring in variable declarations
12
+ * - const [a, b] = fn() — array destructuring (except Promise.all)
13
+ * - for (const { email } of items) — object destructuring in for-of loops
14
+ * - for (const [a, b] of items) — array destructuring in for-of (except Object.entries)
15
+ * - const { page = 0 } = opts — destructuring with defaults
16
+ * - const { done: streamDone } = obj — destructuring with renaming
17
+ * - function foo({ x, y }: Type) — function parameter destructuring
18
+ *
19
+ * ============================================================================
20
+ * ALLOWED (skip — NOT violations)
21
+ * ============================================================================
22
+ *
23
+ * - const [a, b] = await Promise.all([...]) — Promise.all array destructuring
24
+ * - for (const [key, value] of Object.entries(obj)) — Object.entries in for-of
25
+ * - const { extracted, ...rest } = obj — rest operator separation
26
+ * - Lines with // webpieces-disable no-destructure -- [reason] (only when disableAllowed: true)
27
+ *
28
+ * ============================================================================
29
+ * MODES (LINE-BASED)
30
+ * ============================================================================
31
+ * - OFF: Skip validation entirely
32
+ * - MODIFIED_CODE: Flag destructuring on changed lines (lines in diff hunks)
33
+ * - MODIFIED_FILES: Flag ALL destructuring in files that were modified
34
+ *
35
+ * ============================================================================
36
+ * ESCAPE HATCH
37
+ * ============================================================================
38
+ * Add comment above the violation:
39
+ * // webpieces-disable no-destructure -- [your justification]
40
+ * const { x, y } = obj;
41
+ */
42
+ import type { ExecutorContext } from '@nx/devkit';
43
+ export type NoDestructureMode = 'OFF' | 'MODIFIED_CODE' | 'MODIFIED_FILES';
44
+ export interface ValidateNoDestructureOptions {
45
+ mode?: NoDestructureMode;
46
+ disableAllowed?: boolean;
47
+ ignoreModifiedUntilEpoch?: number;
48
+ }
49
+ export interface ExecutorResult {
50
+ success: boolean;
51
+ }
52
+ export default function runExecutor(options: ValidateNoDestructureOptions, context: ExecutorContext): Promise<ExecutorResult>;