@webpieces/dev-config 0.2.47 → 0.2.51

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.
@@ -47,20 +47,20 @@ function formatGraphJson(graph) {
47
47
  const entry = graph[key];
48
48
  const isLast = index === keys.length - 1;
49
49
  const comma = isLast ? '' : ',';
50
- lines.push(` "${key}": {`);
51
- lines.push(` "level": ${entry.level},`);
50
+ lines.push(` "${key}": {`);
51
+ lines.push(` "level": ${entry.level},`);
52
52
  if (entry.dependsOn.length === 0) {
53
- lines.push(` "dependsOn": []`);
53
+ lines.push(` "dependsOn": []`);
54
54
  }
55
55
  else {
56
- lines.push(` "dependsOn": [`);
56
+ lines.push(` "dependsOn": [`);
57
57
  entry.dependsOn.forEach((dep, depIndex) => {
58
58
  const depComma = depIndex === entry.dependsOn.length - 1 ? '' : ',';
59
- lines.push(` "${dep}"${depComma}`);
59
+ lines.push(` "${dep}"${depComma}`);
60
60
  });
61
- lines.push(` ]`);
61
+ lines.push(` ]`);
62
62
  }
63
- lines.push(` }${comma}`);
63
+ lines.push(` }${comma}`);
64
64
  });
65
65
  lines.push('}');
66
66
  return lines.join('\n') + '\n';
@@ -1 +1 @@
1
- {"version":3,"file":"graph-loader.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/architecture/lib/graph-loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph Loader\n *\n * Handles loading and saving the blessed dependency graph file.\n * The graph is stored at architecture/dependencies.json in the workspace root.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/**\n * Default path for the dependencies file (relative to workspace root)\n */\nexport const DEFAULT_GRAPH_PATH = 'architecture/dependencies.json';\n\n/**\n * Load the blessed graph from disk\n *\n * @param workspaceRoot - Absolute path to workspace root\n * @param graphPath - Relative path to graph file (default: .graphs/dependencies.json)\n * @returns The blessed graph, or null if file doesn't exist\n */\nexport function loadBlessedGraph(\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): EnhancedGraph | null {\n const fullPath = path.join(workspaceRoot, graphPath);\n\n if (!fs.existsSync(fullPath)) {\n return null;\n }\n\n try {\n const content = fs.readFileSync(fullPath, 'utf-8');\n return JSON.parse(content) as EnhancedGraph;\n } catch (err: unknown) {\n throw new Error(`Failed to load graph from ${fullPath}: ${err}`);\n }\n}\n\n/**\n * Format a graph as JSON with multi-line arrays for readability\n */\nfunction formatGraphJson(graph: EnhancedGraph): string {\n const lines: string[] = ['{'];\n const keys = Object.keys(graph).sort();\n\n keys.forEach((key, index) => {\n const entry = graph[key];\n const isLast = index === keys.length - 1;\n const comma = isLast ? '' : ',';\n\n lines.push(` \"${key}\": {`);\n lines.push(` \"level\": ${entry.level},`);\n\n if (entry.dependsOn.length === 0) {\n lines.push(` \"dependsOn\": []`);\n } else {\n lines.push(` \"dependsOn\": [`);\n entry.dependsOn.forEach((dep, depIndex) => {\n const depComma = depIndex === entry.dependsOn.length - 1 ? '' : ',';\n lines.push(` \"${dep}\"${depComma}`);\n });\n lines.push(` ]`);\n }\n\n lines.push(` }${comma}`);\n });\n\n lines.push('}');\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Save the graph to disk\n *\n * @param graph - The graph to save\n * @param workspaceRoot - Absolute path to workspace root\n * @param graphPath - Relative path to graph file (default: .graphs/dependencies.json)\n */\nexport function saveGraph(\n graph: EnhancedGraph,\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): void {\n const fullPath = path.join(workspaceRoot, graphPath);\n const dir = path.dirname(fullPath);\n\n // Ensure directory exists\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n // Sort keys for deterministic output\n const sortedGraph: EnhancedGraph = {};\n const sortedKeys = Object.keys(graph).sort();\n for (const key of sortedKeys) {\n sortedGraph[key] = graph[key];\n }\n\n const content = formatGraphJson(sortedGraph);\n fs.writeFileSync(fullPath, content, 'utf-8');\n}\n\n/**\n * Check if the graph file exists\n */\nexport function graphFileExists(\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): boolean {\n const fullPath = path.join(workspaceRoot, graphPath);\n return fs.existsSync(fullPath);\n}\n"]}
1
+ {"version":3,"file":"graph-loader.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/architecture/lib/graph-loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph Loader\n *\n * Handles loading and saving the blessed dependency graph file.\n * The graph is stored at architecture/dependencies.json in the workspace root.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/**\n * Default path for the dependencies file (relative to workspace root)\n */\nexport const DEFAULT_GRAPH_PATH = 'architecture/dependencies.json';\n\n/**\n * Load the blessed graph from disk\n *\n * @param workspaceRoot - Absolute path to workspace root\n * @param graphPath - Relative path to graph file (default: .graphs/dependencies.json)\n * @returns The blessed graph, or null if file doesn't exist\n */\nexport function loadBlessedGraph(\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): EnhancedGraph | null {\n const fullPath = path.join(workspaceRoot, graphPath);\n\n if (!fs.existsSync(fullPath)) {\n return null;\n }\n\n try {\n const content = fs.readFileSync(fullPath, 'utf-8');\n return JSON.parse(content) as EnhancedGraph;\n } catch (err: unknown) {\n throw new Error(`Failed to load graph from ${fullPath}: ${err}`);\n }\n}\n\n/**\n * Format a graph as JSON with multi-line arrays for readability\n */\nfunction formatGraphJson(graph: EnhancedGraph): string {\n const lines: string[] = ['{'];\n const keys = Object.keys(graph).sort();\n\n keys.forEach((key, index) => {\n const entry = graph[key];\n const isLast = index === keys.length - 1;\n const comma = isLast ? '' : ',';\n\n lines.push(` \"${key}\": {`);\n lines.push(` \"level\": ${entry.level},`);\n\n if (entry.dependsOn.length === 0) {\n lines.push(` \"dependsOn\": []`);\n } else {\n lines.push(` \"dependsOn\": [`);\n entry.dependsOn.forEach((dep, depIndex) => {\n const depComma = depIndex === entry.dependsOn.length - 1 ? '' : ',';\n lines.push(` \"${dep}\"${depComma}`);\n });\n lines.push(` ]`);\n }\n\n lines.push(` }${comma}`);\n\n });\n\n lines.push('}');\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Save the graph to disk\n *\n * @param graph - The graph to save\n * @param workspaceRoot - Absolute path to workspace root\n * @param graphPath - Relative path to graph file (default: .graphs/dependencies.json)\n */\nexport function saveGraph(\n graph: EnhancedGraph,\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): void {\n const fullPath = path.join(workspaceRoot, graphPath);\n const dir = path.dirname(fullPath);\n\n // Ensure directory exists\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n // Sort keys for deterministic output\n const sortedGraph: EnhancedGraph = {};\n const sortedKeys = Object.keys(graph).sort();\n for (const key of sortedKeys) {\n sortedGraph[key] = graph[key];\n }\n\n const content = formatGraphJson(sortedGraph);\n fs.writeFileSync(fullPath, content, 'utf-8');\n}\n\n/**\n * Check if the graph file exists\n */\nexport function graphFileExists(\n workspaceRoot: string,\n graphPath: string = DEFAULT_GRAPH_PATH\n): boolean {\n const fullPath = path.join(workspaceRoot, graphPath);\n return fs.existsSync(fullPath);\n}\n"]}
@@ -51,21 +51,22 @@ function formatGraphJson(graph: EnhancedGraph): string {
51
51
  const isLast = index === keys.length - 1;
52
52
  const comma = isLast ? '' : ',';
53
53
 
54
- lines.push(` "${key}": {`);
55
- lines.push(` "level": ${entry.level},`);
54
+ lines.push(` "${key}": {`);
55
+ lines.push(` "level": ${entry.level},`);
56
56
 
57
57
  if (entry.dependsOn.length === 0) {
58
- lines.push(` "dependsOn": []`);
58
+ lines.push(` "dependsOn": []`);
59
59
  } else {
60
- lines.push(` "dependsOn": [`);
60
+ lines.push(` "dependsOn": [`);
61
61
  entry.dependsOn.forEach((dep, depIndex) => {
62
62
  const depComma = depIndex === entry.dependsOn.length - 1 ? '' : ',';
63
- lines.push(` "${dep}"${depComma}`);
63
+ lines.push(` "${dep}"${depComma}`);
64
64
  });
65
- lines.push(` ]`);
65
+ lines.push(` ]`);
66
66
  }
67
67
 
68
- lines.push(` }${comma}`);
68
+ lines.push(` }${comma}`);
69
+
69
70
  });
70
71
 
71
72
  lines.push('}');
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Validate Versions Locked Executor
3
+ *
4
+ * Validates that package.json versions are LOCKED (exact versions, no semver ranges)
5
+ * and checks npm ci compatibility for peer dependency conflicts.
6
+ *
7
+ * Why locked versions matter:
8
+ * - Micro bugs ARE introduced via patch versions (1.4.5 → 1.4.6)
9
+ * - git bisect fails when software changes OUTSIDE of git
10
+ * - Library upgrades must be explicit via PR/commit, not implicit drift
11
+ *
12
+ * Usage:
13
+ * nx run architecture:validate-versions-locked
14
+ */
15
+ import type { ExecutorContext } from '@nx/devkit';
16
+ export interface ValidateVersionsLockedOptions {
17
+ }
18
+ export interface ExecutorResult {
19
+ success: boolean;
20
+ }
21
+ export default function runExecutor(_options: ValidateVersionsLockedOptions, context: ExecutorContext): Promise<ExecutorResult>;
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
  /**
3
- * Validate Versions Executor
3
+ * Validate Versions Locked Executor
4
4
  *
5
- * Validates package.json versions and checks npm ci compatibility.
6
- * This catches peer dependency conflicts that npm ci catches but npm install doesn't.
5
+ * Validates that package.json versions are LOCKED (exact versions, no semver ranges)
6
+ * and checks npm ci compatibility for peer dependency conflicts.
7
+ *
8
+ * Why locked versions matter:
9
+ * - Micro bugs ARE introduced via patch versions (1.4.5 → 1.4.6)
10
+ * - git bisect fails when software changes OUTSIDE of git
11
+ * - Library upgrades must be explicit via PR/commit, not implicit drift
7
12
  *
8
13
  * Usage:
9
- * nx run dev-config:validate-versions
14
+ * nx run architecture:validate-versions-locked
10
15
  */
11
16
  Object.defineProperty(exports, "__esModule", { value: true });
12
17
  exports.default = runExecutor;
@@ -14,6 +19,7 @@ const tslib_1 = require("tslib");
14
19
  const child_process_1 = require("child_process");
15
20
  const fs = tslib_1.__importStar(require("fs"));
16
21
  const path = tslib_1.__importStar(require("path"));
22
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
17
23
  // Find all package.json files except node_modules, dist, .nx, .angular
18
24
  function findPackageJsonFiles(dir, basePath = '') {
19
25
  const files = [];
@@ -67,6 +73,7 @@ function hasSemverRange(version) {
67
73
  ];
68
74
  return semverPatterns.some((pattern) => pattern.test(version));
69
75
  }
76
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
70
77
  // Validate a single package.json file for semver ranges
71
78
  function validatePackageJson(filePath) {
72
79
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
@@ -82,7 +89,7 @@ function validatePackageJson(filePath) {
82
89
  continue;
83
90
  }
84
91
  if (hasSemverRange(version)) {
85
- errors.push(`dependencies.${name}: "${version}" uses semver range (should be fixed version)`);
92
+ errors.push(`dependencies.${name}: "${version}" uses semver range (must be locked to exact version)`);
86
93
  }
87
94
  }
88
95
  }
@@ -94,7 +101,7 @@ function validatePackageJson(filePath) {
94
101
  continue;
95
102
  }
96
103
  if (hasSemverRange(version)) {
97
- errors.push(`devDependencies.${name}: "${version}" uses semver range (should be fixed version)`);
104
+ errors.push(`devDependencies.${name}: "${version}" uses semver range (must be locked to exact version)`);
98
105
  }
99
106
  }
100
107
  }
@@ -107,6 +114,7 @@ function validatePackageJson(filePath) {
107
114
  return [`Failed to parse ${filePath}: ${err.message}`];
108
115
  }
109
116
  }
117
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
110
118
  // Check npm ci compatibility
111
119
  function checkNpmCiCompatibility(workspaceRoot) {
112
120
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
@@ -131,6 +139,12 @@ function checkNpmCiCompatibility(workspaceRoot) {
131
139
  }
132
140
  // Parse the error output to extract peer dependency conflicts
133
141
  const output = err.stdout || err.stderr || err.message;
142
+ // Ignore errors about internal @webpieces/* packages with 0.0.0-dev versions
143
+ // These don't exist on npm registry (only locally) but that's expected in development/CI
144
+ if (output.includes('npm error') && output.includes('@webpieces/') && output.includes('0.0.0-dev')) {
145
+ console.log(' ⏭️ Skipping npm ci check - internal @webpieces packages use local 0.0.0-dev versions');
146
+ return [];
147
+ }
134
148
  // Check if it's a peer dependency error (npm error, not npm warn)
135
149
  if (output.includes('npm error') && (output.includes('ERESOLVE') || output.includes('peer'))) {
136
150
  return [output];
@@ -143,32 +157,84 @@ function checkNpmCiCompatibility(workspaceRoot) {
143
157
  return [`npm dependency check failed: ${output}`];
144
158
  }
145
159
  }
146
- // Check semver ranges in all package.json files
160
+ /**
161
+ * Prints the educational message explaining why semver ranges are forbidden.
162
+ * This helps developers understand the rationale behind locked versions.
163
+ */
164
+ // webpieces-disable max-lines-new-methods -- Educational message template, splitting reduces clarity
165
+ function printSemverRangeEducationalMessage(semverErrors) {
166
+ console.log(`
167
+ ❌ SEMVER RANGES DETECTED - BUILD FAILED
168
+
169
+ Found ${semverErrors} package(s) using semver ranges (^, ~, *, etc.) instead of locked versions.
170
+
171
+ WHY THIS IS A HARD FAILURE:
172
+ ═══════════════════════════════════════════════════════════════════════════════
173
+
174
+ 1. MICRO BUGS ARE REAL
175
+ Thinking that patch versions (1.4.5 → 1.4.6) don't introduce bugs is wrong.
176
+ They do. Sometimes what looks like an "easy fix" breaks things in subtle ways.
177
+
178
+ 2. GIT BISECT BECOMES USELESS
179
+ When you run "git bisect" to find when a bug was introduced, it fails if
180
+ software changed OUTSIDE of git. You checkout an old commit, but node_modules
181
+ has different versions than when that commit was made. The bug persists even
182
+ in "known good" commits because the library versions drifted.
183
+
184
+ 3. THE "MAGIC BUG" PROBLEM
185
+ You checkout code from 6 months ago to debug an issue. The bug is still there!
186
+ But it wasn't there 6 months ago... The culprit: a minor version upgrade that
187
+ happened silently without any PR or git commit. Impossible to track down.
188
+
189
+ 4. CHANGES OUTSIDE GIT = BAD
190
+ Every change to your software should be tracked in version control.
191
+ Implicit library upgrades via semver ranges violate this principle.
192
+
193
+ THE SOLUTION:
194
+ ═══════════════════════════════════════════════════════════════════════════════
195
+
196
+ Use LOCKED (exact) versions for all dependencies:
197
+ ❌ "lodash": "^4.17.21" <- BAD: allows 4.17.22, 4.18.0, etc.
198
+ ❌ "lodash": "~4.17.21" <- BAD: allows 4.17.22, 4.17.23, etc.
199
+ ✅ "lodash": "4.17.21" <- GOOD: locked to this exact version
200
+
201
+ To upgrade libraries, use an explicit process:
202
+ 1. Run: npm update <package-name>
203
+ 2. Test thoroughly
204
+ 3. Commit the package.json AND package-lock.json changes
205
+ 4. Create a PR so the upgrade is reviewed and tracked in git history
206
+
207
+ This way, every library change is:
208
+ • Intentional (not accidental)
209
+ • Reviewed (via PR)
210
+ • Tracked (in git history)
211
+ • Bisectable (git bisect works correctly)
212
+
213
+ `);
214
+ }
215
+ // Check semver ranges in all package.json files - FAILS if any found
147
216
  function checkSemverRanges(workspaceRoot) {
148
- console.log('\n📋 Checking for semver ranges (warnings only):');
217
+ console.log('\n📋 Checking for unlocked versions (semver ranges):');
149
218
  const packageFiles = findPackageJsonFiles(workspaceRoot);
150
- let semverWarnings = 0;
219
+ let semverErrors = 0;
151
220
  for (const filePath of packageFiles) {
152
221
  const relativePath = path.relative(workspaceRoot, filePath);
153
222
  const errors = validatePackageJson(filePath);
154
223
  if (errors.length > 0) {
155
- console.log(` ⚠️ ${relativePath}:`);
224
+ console.log(` ${relativePath}:`);
156
225
  for (const error of errors) {
157
226
  console.log(` ${error}`);
158
227
  }
159
- semverWarnings += errors.length;
228
+ semverErrors += errors.length;
160
229
  }
161
230
  else {
162
231
  console.log(` ✅ ${relativePath}`);
163
232
  }
164
233
  }
165
- if (semverWarnings > 0) {
166
- console.log(`\n ⚠️ Note: ${semverWarnings} semver ranges found (consider using fixed versions for reproducibility)`);
167
- }
168
- return { warnings: semverWarnings };
234
+ return { errors: semverErrors };
169
235
  }
170
236
  async function runExecutor(_options, context) {
171
- console.log('\n🔍 Validating Package Versions and npm ci Compatibility\n');
237
+ console.log('\n🔒 Validating Package Versions are LOCKED (no semver ranges)\n');
172
238
  const workspaceRoot = context.root;
173
239
  // Step 1: Check npm ci compatibility
174
240
  console.log('🔄 Checking npm ci compatibility (peer dependencies):');
@@ -190,21 +256,27 @@ async function runExecutor(_options, context) {
190
256
  else {
191
257
  console.log(' ✅ npm ci compatibility check passed');
192
258
  }
193
- // Step 2: Check for semver ranges
194
- const { warnings: semverWarnings } = checkSemverRanges(workspaceRoot);
259
+ // Step 2: Check for semver ranges (FAILS if any found)
260
+ const { errors: semverErrors } = checkSemverRanges(workspaceRoot);
195
261
  const packageFiles = findPackageJsonFiles(workspaceRoot);
196
262
  // Summary
197
263
  console.log(`\n📊 Summary:`);
198
264
  console.log(` npm ci compatibility: ${npmCiErrors.length === 0 ? '✅' : '❌'}`);
199
265
  console.log(` Files checked: ${packageFiles.length}`);
200
- console.log(` Semver warnings: ${semverWarnings}`);
201
- console.log(` Errors: ${npmCiErrors.length}`);
266
+ console.log(` Unlocked versions: ${semverErrors}`);
267
+ console.log(` Peer dep errors: ${npmCiErrors.length}`);
268
+ // Fail on npm ci errors
202
269
  if (npmCiErrors.length > 0) {
203
270
  console.log('\n❌ VALIDATION FAILED!');
204
271
  console.log(' Fix peer dependency conflicts to avoid CI failures.\n');
205
272
  return { success: false };
206
273
  }
207
- console.log('\n✅ VALIDATION PASSED!');
274
+ // Fail on semver ranges with educational message
275
+ if (semverErrors > 0) {
276
+ printSemverRangeEducationalMessage(semverErrors);
277
+ return { success: false };
278
+ }
279
+ console.log('\n✅ VALIDATION PASSED! All versions are locked.');
208
280
  return { success: true };
209
281
  }
210
282
  //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/executors/validate-versions-locked/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Versions Locked Executor\n *\n * Validates that package.json versions are LOCKED (exact versions, no semver ranges)\n * and checks npm ci compatibility for peer dependency conflicts.\n *\n * Why locked versions matter:\n * - Micro bugs ARE introduced via patch versions (1.4.5 → 1.4.6)\n * - git bisect fails when software changes OUTSIDE of git\n * - Library upgrades must be explicit via PR/commit, not implicit drift\n *\n * Usage:\n * nx run architecture:validate-versions-locked\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateVersionsLockedOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file\n// Find all package.json files except node_modules, dist, .nx, .angular\nfunction findPackageJsonFiles(dir: string, basePath = ''): string[] {\n const files: string[] = [];\n const items = fs.readdirSync(dir);\n\n for (const item of items) {\n const fullPath = path.join(dir, item);\n const relativePath = path.join(basePath, item);\n\n // Skip these directories\n if (\n ['node_modules', 'dist', '.nx', '.angular', 'tmp', '.git'].includes(\n item,\n )\n ) {\n continue;\n }\n\n // Skip all hidden directories (starting with .)\n if (item.startsWith('.')) {\n continue;\n }\n\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) {\n files.push(...findPackageJsonFiles(fullPath, relativePath));\n } else if (item === 'package.json') {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n// Check if a version string uses semver ranges\nfunction hasSemverRange(version: string): boolean {\n // Allow workspace protocol\n if (version.startsWith('workspace:')) {\n return false;\n }\n\n // Allow file: protocol (for local packages)\n if (version.startsWith('file:')) {\n return false;\n }\n\n // Check for common semver range patterns\n const semverPatterns = [\n /^\\^/, // ^1.2.3\n /^~/, // ~1.2.3\n /^\\+/, // +1.2.3\n /^\\*/, // *\n /^>/, // >1.2.3\n /^</, // <1.2.3\n /^>=/, // >=1.2.3\n /^<=/, // <=1.2.3\n /\\|\\|/, // 1.2.3 || 2.x\n / - /, // 1.2.3 - 2.3.4\n /^\\d+\\.x/, // 1.x, 1.2.x\n /^latest$/, // latest\n /^next$/, // next\n ];\n\n return semverPatterns.some((pattern) => pattern.test(version));\n}\n\n// webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file\n// Validate a single package.json file for semver ranges\nfunction validatePackageJson(filePath: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const pkg = JSON.parse(content);\n const errors: string[] = [];\n\n // Check dependencies\n if (pkg.dependencies) {\n for (const [name, version] of Object.entries(pkg.dependencies)) {\n // Skip internal workspace packages\n if (name.startsWith('@webpieces/')) {\n continue;\n }\n\n if (hasSemverRange(version as string)) {\n errors.push(\n `dependencies.${name}: \"${version}\" uses semver range (must be locked to exact version)`,\n );\n }\n }\n }\n\n // Check devDependencies\n if (pkg.devDependencies) {\n for (const [name, version] of Object.entries(pkg.devDependencies)) {\n // Skip internal workspace packages\n if (name.startsWith('@webpieces/')) {\n continue;\n }\n\n if (hasSemverRange(version as string)) {\n errors.push(\n `devDependencies.${name}: \"${version}\" uses semver range (must be locked to exact version)`,\n );\n }\n }\n }\n\n // Check peerDependencies (these can have ranges for compatibility)\n // We don't validate peerDependencies for semver ranges since they're meant to be flexible\n\n return errors;\n } catch (err: any) {\n //const error = toError(err);\n return [`Failed to parse ${filePath}: ${err.message}`];\n }\n}\n\n// webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file\n// Check npm ci compatibility\nfunction checkNpmCiCompatibility(workspaceRoot: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Run npm install --package-lock-only to check for peer dependency conflicts\n // This simulates what npm ci does without actually installing\n // Use --ignore-scripts to prevent infinite recursion (avoid triggering preinstall hook)\n console.log(' Running npm dependency resolution check (10s timeout)...');\n execSync('npm install --package-lock-only --ignore-scripts 2>&1', {\n cwd: workspaceRoot,\n stdio: 'pipe',\n encoding: 'utf-8',\n timeout: 10000 // 10 second timeout\n });\n return [];\n } catch (err: any) {\n //const error = toError(err);\n // Check if it's a timeout\n if (err.killed) {\n return ['npm dependency check timed out - this might indicate a hang or network issue'];\n }\n\n // Parse the error output to extract peer dependency conflicts\n const output = err.stdout || err.stderr || err.message;\n\n // Ignore errors about internal @webpieces/* packages with 0.0.0-dev versions\n // These don't exist on npm registry (only locally) but that's expected in development/CI\n if (output.includes('npm error') && output.includes('@webpieces/') && output.includes('0.0.0-dev')) {\n console.log(' ⏭️ Skipping npm ci check - internal @webpieces packages use local 0.0.0-dev versions');\n return [];\n }\n\n // Check if it's a peer dependency error (npm error, not npm warn)\n if (output.includes('npm error') && (output.includes('ERESOLVE') || output.includes('peer'))) {\n return [output];\n }\n\n // If it's just warnings, not errors, we're OK\n if (output.includes('npm warn') && !output.includes('npm error')) {\n return [];\n }\n\n // Some other error - return it\n return [`npm dependency check failed: ${output}`];\n }\n}\n\n/**\n * Prints the educational message explaining why semver ranges are forbidden.\n * This helps developers understand the rationale behind locked versions.\n */\n// webpieces-disable max-lines-new-methods -- Educational message template, splitting reduces clarity\nfunction printSemverRangeEducationalMessage(semverErrors: number): void {\n console.log(`\n❌ SEMVER RANGES DETECTED - BUILD FAILED\n\nFound ${semverErrors} package(s) using semver ranges (^, ~, *, etc.) instead of locked versions.\n\nWHY THIS IS A HARD FAILURE:\n═══════════════════════════════════════════════════════════════════════════════\n\n1. MICRO BUGS ARE REAL\n Thinking that patch versions (1.4.5 → 1.4.6) don't introduce bugs is wrong.\n They do. Sometimes what looks like an \"easy fix\" breaks things in subtle ways.\n\n2. GIT BISECT BECOMES USELESS\n When you run \"git bisect\" to find when a bug was introduced, it fails if\n software changed OUTSIDE of git. You checkout an old commit, but node_modules\n has different versions than when that commit was made. The bug persists even\n in \"known good\" commits because the library versions drifted.\n\n3. THE \"MAGIC BUG\" PROBLEM\n You checkout code from 6 months ago to debug an issue. The bug is still there!\n But it wasn't there 6 months ago... The culprit: a minor version upgrade that\n happened silently without any PR or git commit. Impossible to track down.\n\n4. CHANGES OUTSIDE GIT = BAD\n Every change to your software should be tracked in version control.\n Implicit library upgrades via semver ranges violate this principle.\n\nTHE SOLUTION:\n═══════════════════════════════════════════════════════════════════════════════\n\nUse LOCKED (exact) versions for all dependencies:\n ❌ \"lodash\": \"^4.17.21\" <- BAD: allows 4.17.22, 4.18.0, etc.\n ❌ \"lodash\": \"~4.17.21\" <- BAD: allows 4.17.22, 4.17.23, etc.\n ✅ \"lodash\": \"4.17.21\" <- GOOD: locked to this exact version\n\nTo upgrade libraries, use an explicit process:\n 1. Run: npm update <package-name>\n 2. Test thoroughly\n 3. Commit the package.json AND package-lock.json changes\n 4. Create a PR so the upgrade is reviewed and tracked in git history\n\nThis way, every library change is:\n • Intentional (not accidental)\n • Reviewed (via PR)\n • Tracked (in git history)\n • Bisectable (git bisect works correctly)\n\n`);\n}\n\n// Check semver ranges in all package.json files - FAILS if any found\nfunction checkSemverRanges(workspaceRoot: string): { errors: number } {\n console.log('\\n📋 Checking for unlocked versions (semver ranges):');\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n let semverErrors = 0;\n\n for (const filePath of packageFiles) {\n const relativePath = path.relative(workspaceRoot, filePath);\n const errors = validatePackageJson(filePath);\n\n if (errors.length > 0) {\n console.log(` ❌ ${relativePath}:`);\n for (const error of errors) {\n console.log(` ${error}`);\n }\n semverErrors += errors.length;\n } else {\n console.log(` ✅ ${relativePath}`);\n }\n }\n\n return { errors: semverErrors };\n}\n\nexport default async function runExecutor(\n _options: ValidateVersionsLockedOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n console.log('\\n🔒 Validating Package Versions are LOCKED (no semver ranges)\\n');\n\n const workspaceRoot = context.root;\n\n // Step 1: Check npm ci compatibility\n console.log('🔄 Checking npm ci compatibility (peer dependencies):');\n const npmCiErrors = checkNpmCiCompatibility(workspaceRoot);\n if (npmCiErrors.length > 0) {\n console.log(' ❌ npm ci compatibility check failed:');\n console.log(' This means \"npm ci\" will fail in CI even though \"npm install\" works locally.\\n');\n for (const error of npmCiErrors) {\n const errorLines = error.split('\\n').slice(0, 30);\n for (const line of errorLines) {\n console.log(` ${line}`);\n }\n if (error.split('\\n').length > 30) {\n console.log(' ... (truncated)');\n }\n }\n console.log('');\n } else {\n console.log(' ✅ npm ci compatibility check passed');\n }\n\n // Step 2: Check for semver ranges (FAILS if any found)\n const { errors: semverErrors } = checkSemverRanges(workspaceRoot);\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n // Summary\n console.log(`\\n📊 Summary:`);\n console.log(` npm ci compatibility: ${npmCiErrors.length === 0 ? '✅' : '❌'}`);\n console.log(` Files checked: ${packageFiles.length}`);\n console.log(` Unlocked versions: ${semverErrors}`);\n console.log(` Peer dep errors: ${npmCiErrors.length}`);\n\n // Fail on npm ci errors\n if (npmCiErrors.length > 0) {\n console.log('\\n❌ VALIDATION FAILED!');\n console.log(' Fix peer dependency conflicts to avoid CI failures.\\n');\n return { success: false };\n }\n\n // Fail on semver ranges with educational message\n if (semverErrors > 0) {\n printSemverRangeEducationalMessage(semverErrors);\n return { success: false };\n }\n\n console.log('\\n✅ VALIDATION PASSED! All versions are locked.');\n return { success: true };\n}\n"]}
@@ -1,11 +1,16 @@
1
1
  /**
2
- * Validate Versions Executor
2
+ * Validate Versions Locked Executor
3
3
  *
4
- * Validates package.json versions and checks npm ci compatibility.
5
- * This catches peer dependency conflicts that npm ci catches but npm install doesn't.
4
+ * Validates that package.json versions are LOCKED (exact versions, no semver ranges)
5
+ * and checks npm ci compatibility for peer dependency conflicts.
6
+ *
7
+ * Why locked versions matter:
8
+ * - Micro bugs ARE introduced via patch versions (1.4.5 → 1.4.6)
9
+ * - git bisect fails when software changes OUTSIDE of git
10
+ * - Library upgrades must be explicit via PR/commit, not implicit drift
6
11
  *
7
12
  * Usage:
8
- * nx run dev-config:validate-versions
13
+ * nx run architecture:validate-versions-locked
9
14
  */
10
15
 
11
16
  import type { ExecutorContext } from '@nx/devkit';
@@ -13,7 +18,7 @@ import { execSync } from 'child_process';
13
18
  import * as fs from 'fs';
14
19
  import * as path from 'path';
15
20
 
16
- export interface ValidateVersionsOptions {
21
+ export interface ValidateVersionsLockedOptions {
17
22
  // No options needed
18
23
  }
19
24
 
@@ -21,6 +26,7 @@ export interface ExecutorResult {
21
26
  success: boolean;
22
27
  }
23
28
 
29
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
24
30
  // Find all package.json files except node_modules, dist, .nx, .angular
25
31
  function findPackageJsonFiles(dir: string, basePath = ''): string[] {
26
32
  const files: string[] = [];
@@ -87,6 +93,7 @@ function hasSemverRange(version: string): boolean {
87
93
  return semverPatterns.some((pattern) => pattern.test(version));
88
94
  }
89
95
 
96
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
90
97
  // Validate a single package.json file for semver ranges
91
98
  function validatePackageJson(filePath: string): string[] {
92
99
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
@@ -105,7 +112,7 @@ function validatePackageJson(filePath: string): string[] {
105
112
 
106
113
  if (hasSemverRange(version as string)) {
107
114
  errors.push(
108
- `dependencies.${name}: "${version}" uses semver range (should be fixed version)`,
115
+ `dependencies.${name}: "${version}" uses semver range (must be locked to exact version)`,
109
116
  );
110
117
  }
111
118
  }
@@ -121,7 +128,7 @@ function validatePackageJson(filePath: string): string[] {
121
128
 
122
129
  if (hasSemverRange(version as string)) {
123
130
  errors.push(
124
- `devDependencies.${name}: "${version}" uses semver range (should be fixed version)`,
131
+ `devDependencies.${name}: "${version}" uses semver range (must be locked to exact version)`,
125
132
  );
126
133
  }
127
134
  }
@@ -137,6 +144,7 @@ function validatePackageJson(filePath: string): string[] {
137
144
  }
138
145
  }
139
146
 
147
+ // webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
140
148
  // Check npm ci compatibility
141
149
  function checkNpmCiCompatibility(workspaceRoot: string): string[] {
142
150
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
@@ -162,6 +170,13 @@ function checkNpmCiCompatibility(workspaceRoot: string): string[] {
162
170
  // Parse the error output to extract peer dependency conflicts
163
171
  const output = err.stdout || err.stderr || err.message;
164
172
 
173
+ // Ignore errors about internal @webpieces/* packages with 0.0.0-dev versions
174
+ // These don't exist on npm registry (only locally) but that's expected in development/CI
175
+ if (output.includes('npm error') && output.includes('@webpieces/') && output.includes('0.0.0-dev')) {
176
+ console.log(' ⏭️ Skipping npm ci check - internal @webpieces packages use local 0.0.0-dev versions');
177
+ return [];
178
+ }
179
+
165
180
  // Check if it's a peer dependency error (npm error, not npm warn)
166
181
  if (output.includes('npm error') && (output.includes('ERESOLVE') || output.includes('peer'))) {
167
182
  return [output];
@@ -177,39 +192,91 @@ function checkNpmCiCompatibility(workspaceRoot: string): string[] {
177
192
  }
178
193
  }
179
194
 
180
- // Check semver ranges in all package.json files
181
- function checkSemverRanges(workspaceRoot: string): { warnings: number } {
182
- console.log('\n📋 Checking for semver ranges (warnings only):');
195
+ /**
196
+ * Prints the educational message explaining why semver ranges are forbidden.
197
+ * This helps developers understand the rationale behind locked versions.
198
+ */
199
+ // webpieces-disable max-lines-new-methods -- Educational message template, splitting reduces clarity
200
+ function printSemverRangeEducationalMessage(semverErrors: number): void {
201
+ console.log(`
202
+ ❌ SEMVER RANGES DETECTED - BUILD FAILED
203
+
204
+ Found ${semverErrors} package(s) using semver ranges (^, ~, *, etc.) instead of locked versions.
205
+
206
+ WHY THIS IS A HARD FAILURE:
207
+ ═══════════════════════════════════════════════════════════════════════════════
208
+
209
+ 1. MICRO BUGS ARE REAL
210
+ Thinking that patch versions (1.4.5 → 1.4.6) don't introduce bugs is wrong.
211
+ They do. Sometimes what looks like an "easy fix" breaks things in subtle ways.
212
+
213
+ 2. GIT BISECT BECOMES USELESS
214
+ When you run "git bisect" to find when a bug was introduced, it fails if
215
+ software changed OUTSIDE of git. You checkout an old commit, but node_modules
216
+ has different versions than when that commit was made. The bug persists even
217
+ in "known good" commits because the library versions drifted.
218
+
219
+ 3. THE "MAGIC BUG" PROBLEM
220
+ You checkout code from 6 months ago to debug an issue. The bug is still there!
221
+ But it wasn't there 6 months ago... The culprit: a minor version upgrade that
222
+ happened silently without any PR or git commit. Impossible to track down.
223
+
224
+ 4. CHANGES OUTSIDE GIT = BAD
225
+ Every change to your software should be tracked in version control.
226
+ Implicit library upgrades via semver ranges violate this principle.
227
+
228
+ THE SOLUTION:
229
+ ═══════════════════════════════════════════════════════════════════════════════
230
+
231
+ Use LOCKED (exact) versions for all dependencies:
232
+ ❌ "lodash": "^4.17.21" <- BAD: allows 4.17.22, 4.18.0, etc.
233
+ ❌ "lodash": "~4.17.21" <- BAD: allows 4.17.22, 4.17.23, etc.
234
+ ✅ "lodash": "4.17.21" <- GOOD: locked to this exact version
235
+
236
+ To upgrade libraries, use an explicit process:
237
+ 1. Run: npm update <package-name>
238
+ 2. Test thoroughly
239
+ 3. Commit the package.json AND package-lock.json changes
240
+ 4. Create a PR so the upgrade is reviewed and tracked in git history
241
+
242
+ This way, every library change is:
243
+ • Intentional (not accidental)
244
+ • Reviewed (via PR)
245
+ • Tracked (in git history)
246
+ • Bisectable (git bisect works correctly)
247
+
248
+ `);
249
+ }
250
+
251
+ // Check semver ranges in all package.json files - FAILS if any found
252
+ function checkSemverRanges(workspaceRoot: string): { errors: number } {
253
+ console.log('\n📋 Checking for unlocked versions (semver ranges):');
183
254
  const packageFiles = findPackageJsonFiles(workspaceRoot);
184
- let semverWarnings = 0;
255
+ let semverErrors = 0;
185
256
 
186
257
  for (const filePath of packageFiles) {
187
258
  const relativePath = path.relative(workspaceRoot, filePath);
188
259
  const errors = validatePackageJson(filePath);
189
260
 
190
261
  if (errors.length > 0) {
191
- console.log(` ⚠️ ${relativePath}:`);
262
+ console.log(` ${relativePath}:`);
192
263
  for (const error of errors) {
193
264
  console.log(` ${error}`);
194
265
  }
195
- semverWarnings += errors.length;
266
+ semverErrors += errors.length;
196
267
  } else {
197
268
  console.log(` ✅ ${relativePath}`);
198
269
  }
199
270
  }
200
271
 
201
- if (semverWarnings > 0) {
202
- console.log(`\n ⚠️ Note: ${semverWarnings} semver ranges found (consider using fixed versions for reproducibility)`);
203
- }
204
-
205
- return { warnings: semverWarnings };
272
+ return { errors: semverErrors };
206
273
  }
207
274
 
208
275
  export default async function runExecutor(
209
- _options: ValidateVersionsOptions,
276
+ _options: ValidateVersionsLockedOptions,
210
277
  context: ExecutorContext
211
278
  ): Promise<ExecutorResult> {
212
- console.log('\n🔍 Validating Package Versions and npm ci Compatibility\n');
279
+ console.log('\n🔒 Validating Package Versions are LOCKED (no semver ranges)\n');
213
280
 
214
281
  const workspaceRoot = context.root;
215
282
 
@@ -233,23 +300,30 @@ export default async function runExecutor(
233
300
  console.log(' ✅ npm ci compatibility check passed');
234
301
  }
235
302
 
236
- // Step 2: Check for semver ranges
237
- const { warnings: semverWarnings } = checkSemverRanges(workspaceRoot);
303
+ // Step 2: Check for semver ranges (FAILS if any found)
304
+ const { errors: semverErrors } = checkSemverRanges(workspaceRoot);
238
305
  const packageFiles = findPackageJsonFiles(workspaceRoot);
239
306
 
240
307
  // Summary
241
308
  console.log(`\n📊 Summary:`);
242
309
  console.log(` npm ci compatibility: ${npmCiErrors.length === 0 ? '✅' : '❌'}`);
243
310
  console.log(` Files checked: ${packageFiles.length}`);
244
- console.log(` Semver warnings: ${semverWarnings}`);
245
- console.log(` Errors: ${npmCiErrors.length}`);
311
+ console.log(` Unlocked versions: ${semverErrors}`);
312
+ console.log(` Peer dep errors: ${npmCiErrors.length}`);
246
313
 
314
+ // Fail on npm ci errors
247
315
  if (npmCiErrors.length > 0) {
248
316
  console.log('\n❌ VALIDATION FAILED!');
249
317
  console.log(' Fix peer dependency conflicts to avoid CI failures.\n');
250
318
  return { success: false };
251
319
  }
252
320
 
253
- console.log('\n✅ VALIDATION PASSED!');
321
+ // Fail on semver ranges with educational message
322
+ if (semverErrors > 0) {
323
+ printSemverRangeEducationalMessage(semverErrors);
324
+ return { success: false };
325
+ }
326
+
327
+ console.log('\n✅ VALIDATION PASSED! All versions are locked.');
254
328
  return { success: true };
255
329
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Versions Locked Executor",
4
+ "description": "Validates package.json versions are locked (no semver ranges) and npm ci compatible",
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": []
8
+ }
package/executors.json CHANGED
@@ -50,10 +50,10 @@
50
50
  "schema": "./executors/validate-eslint-sync/schema.json",
51
51
  "description": "Validate that workspace and template eslint.webpieces.config.mjs files have identical rules"
52
52
  },
53
- "validate-versions": {
54
- "implementation": "./executors/validate-versions/executor",
55
- "schema": "./executors/validate-versions/schema.json",
56
- "description": "Validate package.json versions and npm ci compatibility"
53
+ "validate-versions-locked": {
54
+ "implementation": "./executors/validate-versions-locked/executor",
55
+ "schema": "./executors/validate-versions-locked/schema.json",
56
+ "description": "Validate package.json versions are locked (no semver ranges) and npm ci compatible"
57
57
  }
58
58
  }
59
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/dev-config",
3
- "version": "0.2.47",
3
+ "version": "0.2.51",
4
4
  "description": "Development configuration, scripts, and patterns for WebPieces projects",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/plugin.js CHANGED
@@ -37,6 +37,7 @@ const DEFAULT_OPTIONS = {
37
37
  validatePackageJson: true,
38
38
  validateNewMethods: true,
39
39
  validateModifiedMethods: true,
40
+ validateVersionsLocked: true,
40
41
  newMethodsMaxLines: 30,
41
42
  modifiedAndNewMethodsMaxLines: 80,
42
43
  },
@@ -115,8 +116,9 @@ function addPerProjectTargets(results, projectFiles, opts, context) {
115
116
  targets[targetName] = createCircularDepsTarget(projectRoot, targetName);
116
117
  }
117
118
  }
118
- // Add ci target - composite target that runs lint, build, test in parallel
119
- // (with test depending on build via targetDefaults)
119
+ // Add ci target - composite target that runs lint, build, test, and typecheck
120
+ // The 'typecheck' target from @nx/js/typescript plugin already type-checks ALL files
121
+ // including *.spec.ts and *.test.ts via tsconfig.json references
120
122
  targets['ci'] = createCiTarget();
121
123
  if (Object.keys(targets).length === 0)
122
124
  continue;
@@ -140,6 +142,27 @@ exports.createNodesV2 = [
140
142
  // Inference function
141
143
  createNodesFunction,
142
144
  ];
145
+ /**
146
+ * Build list of enabled validation target names for validate-complete dependency chain
147
+ */
148
+ function buildValidationTargetsList(validations) {
149
+ const targets = [];
150
+ if (validations.noCycles)
151
+ targets.push('validate-no-architecture-cycles');
152
+ if (validations.architectureUnchanged)
153
+ targets.push('validate-architecture-unchanged');
154
+ if (validations.noSkipLevelDeps)
155
+ targets.push('validate-no-skiplevel-deps');
156
+ if (validations.validatePackageJson)
157
+ targets.push('validate-packagejson');
158
+ if (validations.validateNewMethods)
159
+ targets.push('validate-new-methods');
160
+ if (validations.validateModifiedMethods)
161
+ targets.push('validate-modified-methods');
162
+ if (validations.validateVersionsLocked)
163
+ targets.push('validate-versions-locked');
164
+ return targets;
165
+ }
143
166
  /**
144
167
  * Create workspace-level architecture validation targets WITHOUT prefix
145
168
  * Used for virtual 'architecture' project
@@ -147,6 +170,7 @@ exports.createNodesV2 = [
147
170
  function createWorkspaceTargetsWithoutPrefix(opts) {
148
171
  const targets = {};
149
172
  const graphPath = opts.workspace.graphPath;
173
+ const validations = opts.workspace.validations;
150
174
  // Add help target (always available)
151
175
  targets['help'] = createHelpTarget();
152
176
  if (opts.workspace.features.generate) {
@@ -155,44 +179,29 @@ function createWorkspaceTargetsWithoutPrefix(opts) {
155
179
  if (opts.workspace.features.visualize) {
156
180
  targets['visualize'] = createVisualizeTargetWithoutPrefix(graphPath);
157
181
  }
158
- if (opts.workspace.validations.noCycles) {
182
+ if (validations.noCycles) {
159
183
  targets['validate-no-architecture-cycles'] = createValidateNoCyclesTarget();
160
184
  }
161
- if (opts.workspace.validations.architectureUnchanged) {
185
+ if (validations.architectureUnchanged) {
162
186
  targets['validate-architecture-unchanged'] = createValidateUnchangedTarget(graphPath);
163
187
  }
164
- if (opts.workspace.validations.noSkipLevelDeps) {
188
+ if (validations.noSkipLevelDeps) {
165
189
  targets['validate-no-skiplevel-deps'] = createValidateNoSkipLevelTarget();
166
190
  }
167
- if (opts.workspace.validations.validatePackageJson) {
191
+ if (validations.validatePackageJson) {
168
192
  targets['validate-packagejson'] = createValidatePackageJsonTarget();
169
193
  }
170
- if (opts.workspace.validations.validateNewMethods) {
171
- targets['validate-new-methods'] = createValidateNewMethodsTarget(opts.workspace.validations.newMethodsMaxLines);
172
- }
173
- if (opts.workspace.validations.validateModifiedMethods) {
174
- targets['validate-modified-methods'] = createValidateModifiedMethodsTarget(opts.workspace.validations.modifiedAndNewMethodsMaxLines);
175
- }
176
- // Add validate-complete target that runs all validations
177
- const validationTargets = [];
178
- if (opts.workspace.validations.noCycles) {
179
- validationTargets.push('validate-no-architecture-cycles');
180
- }
181
- if (opts.workspace.validations.architectureUnchanged) {
182
- validationTargets.push('validate-architecture-unchanged');
183
- }
184
- if (opts.workspace.validations.noSkipLevelDeps) {
185
- validationTargets.push('validate-no-skiplevel-deps');
186
- }
187
- if (opts.workspace.validations.validatePackageJson) {
188
- validationTargets.push('validate-packagejson');
194
+ if (validations.validateNewMethods) {
195
+ targets['validate-new-methods'] = createValidateNewMethodsTarget(validations.newMethodsMaxLines);
189
196
  }
190
- if (opts.workspace.validations.validateNewMethods) {
191
- validationTargets.push('validate-new-methods');
197
+ if (validations.validateModifiedMethods) {
198
+ targets['validate-modified-methods'] = createValidateModifiedMethodsTarget(validations.modifiedAndNewMethodsMaxLines);
192
199
  }
193
- if (opts.workspace.validations.validateModifiedMethods) {
194
- validationTargets.push('validate-modified-methods');
200
+ if (validations.validateVersionsLocked) {
201
+ targets['validate-versions-locked'] = createValidateVersionsLockedTarget();
195
202
  }
203
+ // Add validate-complete target that runs all enabled validations
204
+ const validationTargets = buildValidationTargetsList(validations);
196
205
  if (validationTargets.length > 0) {
197
206
  targets['validate-complete'] = createValidateCompleteTarget(validationTargets);
198
207
  }
@@ -338,6 +347,17 @@ function createValidateModifiedMethodsTarget(maxLines) {
338
347
  },
339
348
  };
340
349
  }
350
+ function createValidateVersionsLockedTarget() {
351
+ return {
352
+ executor: '@webpieces/dev-config:validate-versions-locked',
353
+ cache: true,
354
+ inputs: ['default'],
355
+ metadata: {
356
+ technologies: ['nx'],
357
+ description: 'Validate package.json versions are locked (no semver ranges) and npm ci compatible',
358
+ },
359
+ };
360
+ }
341
361
  function createValidateCompleteTarget(validationTargets) {
342
362
  return {
343
363
  executor: 'nx:noop',
@@ -351,16 +371,20 @@ function createValidateCompleteTarget(validationTargets) {
351
371
  }
352
372
  /**
353
373
  * Create per-project ci target - Gradle-style composite target
354
- * Runs lint, build, and test in parallel (with test waiting for build via targetDefaults)
374
+ * Runs lint, build, test, and typecheck in parallel
375
+ * (with test depending on build via targetDefaults)
376
+ *
377
+ * The 'typecheck' target from @nx/js/typescript plugin type-checks ALL *.ts files
378
+ * including test files (*.spec.ts, *.test.ts) via tsconfig.json references.
355
379
  */
356
380
  function createCiTarget() {
357
381
  return {
358
382
  executor: 'nx:noop',
359
383
  cache: true,
360
- dependsOn: ['lint', 'build', 'test'],
384
+ dependsOn: ['lint', 'build', 'test', 'typecheck'],
361
385
  metadata: {
362
386
  technologies: ['nx'],
363
- description: 'Run all CI checks: lint, build, and test (Gradle-style composite target)',
387
+ description: 'Run all CI checks: lint, build, test, and typecheck (Gradle-style composite target)',
364
388
  },
365
389
  };
366
390
  }
@@ -208,9 +208,11 @@ function addNpmScripts(tree) {
208
208
  pkgJson.scripts['arch:check-circular-affected'] = 'nx affected --target=validate-no-file-import-cycles';
209
209
  // Complete validation including circular deps
210
210
  pkgJson.scripts['arch:validate-complete'] = 'npm run arch:validate-all && npm run arch:check-circular';
211
+ // Add CI script that runs lint, build, test on affected projects
212
+ pkgJson.scripts['webpieces:ci'] = 'npx nx affected --target=ci';
211
213
  return pkgJson;
212
214
  });
213
- console.log('✅ Added npm scripts for architecture validation and circular dependency checking');
215
+ console.log('✅ Added npm scripts for architecture validation, circular dependency checking, and CI');
214
216
  }
215
217
  function createEslintConfig(tree) {
216
218
  const webpiecesConfigPath = 'eslint.webpieces.config.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/src/generators/init/generator.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatFiles, readNxJson, Tree, updateNxJson, updateJson, addDependenciesToPackageJson } from '@nx/devkit';\nimport { createHash } from 'crypto';\n\nexport interface InitGeneratorSchema {\n skipFormat?: boolean;\n}\n\nfunction calculateHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nfunction getPackageVersion(tree: Tree): string {\n const content = tree.read('node_modules/@webpieces/dev-config/package.json', 'utf-8');\n if (!content) {\n throw new Error('Could not read package.json from node_modules/@webpieces/dev-config');\n }\n const pkgJson = JSON.parse(content);\n return pkgJson.version;\n}\n\n/**\n * Init generator for @webpieces/dev-config\n *\n * Automatically runs when users execute: nx add @webpieces/dev-config\n *\n * Responsibilities:\n * - Registers the plugin in nx.json\n * - Adds architecture validation to targetDefaults (runs once before all builds)\n * - Creates architecture/ directory if needed\n * - Adds madge as a devDependency (required for circular dep checking)\n * - Adds convenient npm scripts to package.json\n * - Always creates eslint.webpieces.config.mjs with @webpieces rules\n * - Creates eslint.config.mjs (if not exists) that imports eslint.webpieces.config.mjs\n * - If eslint.config.mjs exists, shows user how to import eslint.webpieces.config.mjs\n * - Provides helpful output about available targets\n */\nexport default async function initGenerator(tree: Tree, options: InitGeneratorSchema) {\n registerPlugin(tree);\n addTargetDefaults(tree);\n const installTask = addMadgeDependency(tree);\n createArchitectureDirectory(tree);\n addNpmScripts(tree);\n const hasExistingEslintConfig = createEslintConfig(tree);\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n\n return createSuccessCallback(installTask, hasExistingEslintConfig);\n}\n\nfunction registerPlugin(tree: Tree): void {\n const nxJson = readNxJson(tree);\n if (!nxJson) {\n throw new Error('Could not read nx.json. Are you in an Nx workspace?');\n }\n\n if (!nxJson.plugins) {\n nxJson.plugins = [];\n }\n\n const pluginName = '@webpieces/dev-config';\n const alreadyRegistered = nxJson.plugins.some(\n (p) => typeof p === 'string' ? p === pluginName : p.plugin === pluginName\n );\n\n if (!alreadyRegistered) {\n // Register plugin with default options for method size validation\n nxJson.plugins.push({\n plugin: pluginName,\n options: {\n workspace: {\n validations: {\n newMethodsMaxLines: 30,\n modifiedAndNewMethodsMaxLines: 80,\n },\n },\n },\n });\n updateNxJson(tree, nxJson);\n console.log(`✅ Registered ${pluginName} plugin in nx.json with default options`);\n } else {\n console.log(`ℹ️ ${pluginName} plugin is already registered`);\n }\n}\n\nfunction addTargetDefaults(tree: Tree): void {\n const nxJson = readNxJson(tree);\n if (!nxJson) {\n throw new Error('Could not read nx.json. Are you in an Nx workspace?');\n }\n\n if (!nxJson.targetDefaults) {\n nxJson.targetDefaults = {};\n }\n\n // Find which executors are actually used in this workspace\n const usedExecutors = findUsedExecutors(tree);\n\n // Only add targetDefaults for executors that are actually used\n let updated = false;\n\n usedExecutors.forEach((executor) => {\n if (!nxJson.targetDefaults![executor]) {\n nxJson.targetDefaults![executor] = {};\n }\n\n const targetDef = nxJson.targetDefaults![executor];\n let dependsOn = targetDef.dependsOn || [];\n\n // Ensure dependsOn is an array\n if (!Array.isArray(dependsOn)) {\n dependsOn = [dependsOn];\n }\n\n // Check if architecture validation is already in dependsOn\n const hasArchValidation = dependsOn.some((dep) => {\n if (typeof dep === 'string') {\n return dep === 'architecture:validate-complete';\n }\n return dep.target === 'architecture:validate-complete';\n });\n\n // Check if circular deps validation is already in dependsOn\n const hasCircularDepsValidation = dependsOn.some((dep) => {\n if (typeof dep === 'string') {\n return dep === 'validate-no-file-import-cycles';\n }\n return dep.target === 'validate-no-file-import-cycles';\n });\n\n if (!hasCircularDepsValidation) {\n // Add circular deps validation (per-project check)\n dependsOn.unshift('validate-no-file-import-cycles');\n targetDef.dependsOn = dependsOn;\n updated = true;\n console.log(` ✅ Added circular deps validation to ${executor}`);\n }\n\n if (!hasArchValidation) {\n // Add architecture validation before other dependencies\n dependsOn.unshift('architecture:validate-complete');\n targetDef.dependsOn = dependsOn;\n updated = true;\n console.log(` ✅ Added architecture validation to ${executor}`);\n }\n });\n\n if (updated) {\n updateNxJson(tree, nxJson);\n console.log('✅ Added architecture validation to targetDefaults for used executors');\n } else {\n console.log('ℹ️ Architecture validation already configured in targetDefaults');\n }\n}\n\n/**\n * Scan all project.json files to find which build executors are actually used\n */\nfunction findUsedExecutors(tree: Tree): Set<string> {\n const usedExecutors = new Set<string>();\n\n // Known build executors we care about\n const buildExecutors = new Set([\n '@nx/js:tsc',\n '@nx/esbuild:esbuild',\n '@nx/webpack:webpack',\n '@nx/rollup:rollup',\n '@nx/vite:build',\n '@angular/build:application',\n '@angular-devkit/build-angular:browser',\n '@angular-devkit/build-angular:application'\n ]);\n\n // Scan all project.json files\n tree.listChanges(); // Force tree to be aware of all files\n\n const scanDir = (dir: string) => {\n for (const child of tree.children(dir)) {\n const childPath = dir === '.' ? child : `${dir}/${child}`;\n\n if (tree.isFile(childPath)) {\n if (child === 'project.json') {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Intentionally ignoring JSON parse errors for malformed project.json files\n try {\n const content = tree.read(childPath, 'utf-8');\n if (content) {\n const projectJson = JSON.parse(content);\n if (projectJson.targets) {\n for (const target of Object.values(projectJson.targets)) {\n const executor = (target as { executor?: string })?.executor;\n if (executor && buildExecutors.has(executor)) {\n usedExecutors.add(executor);\n }\n }\n }\n }\n } catch (err: any) {\n //const error = toError(err);\n }\n }\n } else {\n // Skip node_modules and dist\n if (child !== 'node_modules' && child !== 'dist' && child !== '.git') {\n scanDir(childPath);\n }\n }\n }\n };\n\n scanDir('.');\n\n console.log(`ℹ️ Found ${usedExecutors.size} build executors in use: ${[...usedExecutors].join(', ')}`);\n\n return usedExecutors;\n}\n\nfunction addMadgeDependency(tree: Tree) {\n return addDependenciesToPackageJson(tree, {}, { 'madge': '^8.0.0' });\n}\n\nfunction createArchitectureDirectory(tree: Tree): void {\n if (!tree.exists('architecture')) {\n tree.write('architecture/.gitkeep', '');\n console.log('✅ Created architecture/ directory');\n }\n}\n\nfunction addNpmScripts(tree: Tree): void {\n updateJson(tree, 'package.json', (pkgJson) => {\n pkgJson.scripts = pkgJson.scripts ?? {};\n\n // Add architecture validation scripts\n pkgJson.scripts['arch:generate'] = 'nx run architecture:generate';\n pkgJson.scripts['arch:visualize'] = 'nx run architecture:visualize';\n pkgJson.scripts['arch:validate'] = 'nx run architecture:validate-no-architecture-cycles && nx run architecture:validate-no-skiplevel-deps';\n pkgJson.scripts['arch:validate-all'] = 'nx run architecture:validate-no-architecture-cycles && nx run architecture:validate-no-skiplevel-deps && nx run architecture:validate-architecture-unchanged';\n\n // Add file import cycle checking scripts\n pkgJson.scripts['arch:check-circular'] = 'nx run-many --target=validate-no-file-import-cycles --all';\n pkgJson.scripts['arch:check-circular-affected'] = 'nx affected --target=validate-no-file-import-cycles';\n\n // Complete validation including circular deps\n pkgJson.scripts['arch:validate-complete'] = 'npm run arch:validate-all && npm run arch:check-circular';\n\n return pkgJson;\n });\n\n console.log('✅ Added npm scripts for architecture validation and circular dependency checking');\n}\n\nfunction createEslintConfig(tree: Tree): boolean {\n const webpiecesConfigPath = 'eslint.webpieces.config.mjs';\n const mainConfigPath = 'eslint.config.mjs';\n\n // Always create eslint.webpieces.config.mjs with our rules\n createWebpiecesEslintConfig(tree, webpiecesConfigPath);\n\n // Check if main eslint.config.mjs exists\n const hasExistingConfig = tree.exists(mainConfigPath);\n\n if (!hasExistingConfig) {\n // No existing config - create one that imports webpieces config\n const mainConfig = `// ESLint configuration\n// Imports @webpieces/dev-config rules\n\nimport webpiecesConfig from './eslint.webpieces.config.mjs';\n\n// Export the webpieces configuration\n// You can add your own rules after spreading webpiecesConfig\nexport default [\n ...webpiecesConfig,\n // Add your custom ESLint configuration here\n];\n`;\n\n tree.write(mainConfigPath, mainConfig);\n console.log('✅ Created eslint.config.mjs with @webpieces/dev-config rules');\n }\n\n return hasExistingConfig;\n}\n\nfunction getWebpiecesEslintConfigTemplate(tree: Tree): string {\n // Read from canonical template file (single source of truth)\n const templatePath = 'node_modules/@webpieces/dev-config/templates/eslint.webpieces.config.mjs';\n const template = tree.read(templatePath, 'utf-8');\n\n if (!template) {\n throw new Error(`Could not read ESLint template from ${templatePath}`);\n }\n\n return template;\n}\n\nfunction warnConfigChanges(tree: Tree, configPath: string, newConfig: string): void {\n const version = getPackageVersion(tree);\n const versionedFilename = `${configPath}.v${version}`;\n\n tree.write(versionedFilename, newConfig);\n\n console.log('');\n console.log(`⚠️ ${configPath} has changes`);\n console.log('');\n console.log(' Either you modified the file OR @webpieces/dev-config has updates.');\n console.log('');\n console.log(` Created: ${versionedFilename} with latest version`);\n console.log('');\n console.log(' Please review and merge if needed:');\n console.log(` - Your current: ${configPath}`);\n console.log(` - New version: ${versionedFilename}`);\n console.log('');\n}\n\nfunction createWebpiecesEslintConfig(tree: Tree, configPath: string): void {\n const webpiecesConfig = getWebpiecesEslintConfigTemplate(tree);\n\n if (!tree.exists(configPath)) {\n tree.write(configPath, webpiecesConfig);\n console.log(`✅ Created ${configPath}`);\n return;\n }\n\n const currentContent = tree.read(configPath, 'utf-8');\n if (!currentContent) {\n tree.write(configPath, webpiecesConfig);\n console.log(`✅ Created ${configPath}`);\n return;\n }\n\n const currentHash = calculateHash(currentContent);\n const newHash = calculateHash(webpiecesConfig);\n\n if (currentHash === newHash) {\n console.log(`✅ ${configPath} is up to date`);\n return;\n }\n\n warnConfigChanges(tree, configPath, webpiecesConfig);\n}\n\nfunction createSuccessCallback(\n installTask: ReturnType<typeof addDependenciesToPackageJson>,\n hasExistingEslintConfig: boolean\n) {\n return async () => {\n await installTask();\n\n // ANSI color codes for formatted output\n const GREEN = '\\x1b[32m\\x1b[1m';\n const BOLD = '\\x1b[1m';\n const RESET = '\\x1b[0m';\n\n console.log('');\n console.log('✅ Added madge to devDependencies');\n console.log('');\n console.log(`${GREEN}✅ @webpieces/dev-config plugin initialized!${RESET}`);\n console.log('');\n console.log(`${GREEN}💡 Quick start:${RESET}`);\n console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the dependency graph`);\n console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);\n console.log('');\n console.log(`💡 For full documentation, run: ${BOLD}nx run architecture:help${RESET}`);\n\n // Show ESLint integration instructions if they have an existing config\n if (hasExistingEslintConfig) {\n console.log('');\n console.log('📋 Existing eslint.config.mjs detected');\n console.log('');\n console.log('To use @webpieces/dev-config ESLint rules, add this import to your eslint.config.mjs:');\n console.log('');\n console.log(' import webpiecesConfig from \\'./eslint.webpieces.config.mjs\\';');\n console.log('');\n console.log('Then spread it into your config array:');\n console.log('');\n console.log(' export default [');\n console.log(' ...webpiecesConfig, // Add this line');\n console.log(' // ... your existing config');\n console.log(' ];');\n }\n\n console.log('');\n };\n}\n"]}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/src/generators/init/generator.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatFiles, readNxJson, Tree, updateNxJson, updateJson, addDependenciesToPackageJson } from '@nx/devkit';\nimport { createHash } from 'crypto';\n\nexport interface InitGeneratorSchema {\n skipFormat?: boolean;\n}\n\nfunction calculateHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nfunction getPackageVersion(tree: Tree): string {\n const content = tree.read('node_modules/@webpieces/dev-config/package.json', 'utf-8');\n if (!content) {\n throw new Error('Could not read package.json from node_modules/@webpieces/dev-config');\n }\n const pkgJson = JSON.parse(content);\n return pkgJson.version;\n}\n\n/**\n * Init generator for @webpieces/dev-config\n *\n * Automatically runs when users execute: nx add @webpieces/dev-config\n *\n * Responsibilities:\n * - Registers the plugin in nx.json\n * - Adds architecture validation to targetDefaults (runs once before all builds)\n * - Creates architecture/ directory if needed\n * - Adds madge as a devDependency (required for circular dep checking)\n * - Adds convenient npm scripts to package.json\n * - Always creates eslint.webpieces.config.mjs with @webpieces rules\n * - Creates eslint.config.mjs (if not exists) that imports eslint.webpieces.config.mjs\n * - If eslint.config.mjs exists, shows user how to import eslint.webpieces.config.mjs\n * - Provides helpful output about available targets\n */\nexport default async function initGenerator(tree: Tree, options: InitGeneratorSchema) {\n registerPlugin(tree);\n addTargetDefaults(tree);\n const installTask = addMadgeDependency(tree);\n createArchitectureDirectory(tree);\n addNpmScripts(tree);\n const hasExistingEslintConfig = createEslintConfig(tree);\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n\n return createSuccessCallback(installTask, hasExistingEslintConfig);\n}\n\nfunction registerPlugin(tree: Tree): void {\n const nxJson = readNxJson(tree);\n if (!nxJson) {\n throw new Error('Could not read nx.json. Are you in an Nx workspace?');\n }\n\n if (!nxJson.plugins) {\n nxJson.plugins = [];\n }\n\n const pluginName = '@webpieces/dev-config';\n const alreadyRegistered = nxJson.plugins.some(\n (p) => typeof p === 'string' ? p === pluginName : p.plugin === pluginName\n );\n\n if (!alreadyRegistered) {\n // Register plugin with default options for method size validation\n nxJson.plugins.push({\n plugin: pluginName,\n options: {\n workspace: {\n validations: {\n newMethodsMaxLines: 30,\n modifiedAndNewMethodsMaxLines: 80,\n },\n },\n },\n });\n updateNxJson(tree, nxJson);\n console.log(`✅ Registered ${pluginName} plugin in nx.json with default options`);\n } else {\n console.log(`ℹ️ ${pluginName} plugin is already registered`);\n }\n}\n\nfunction addTargetDefaults(tree: Tree): void {\n const nxJson = readNxJson(tree);\n if (!nxJson) {\n throw new Error('Could not read nx.json. Are you in an Nx workspace?');\n }\n\n if (!nxJson.targetDefaults) {\n nxJson.targetDefaults = {};\n }\n\n // Find which executors are actually used in this workspace\n const usedExecutors = findUsedExecutors(tree);\n\n // Only add targetDefaults for executors that are actually used\n let updated = false;\n\n usedExecutors.forEach((executor) => {\n if (!nxJson.targetDefaults![executor]) {\n nxJson.targetDefaults![executor] = {};\n }\n\n const targetDef = nxJson.targetDefaults![executor];\n let dependsOn = targetDef.dependsOn || [];\n\n // Ensure dependsOn is an array\n if (!Array.isArray(dependsOn)) {\n dependsOn = [dependsOn];\n }\n\n // Check if architecture validation is already in dependsOn\n const hasArchValidation = dependsOn.some((dep) => {\n if (typeof dep === 'string') {\n return dep === 'architecture:validate-complete';\n }\n return dep.target === 'architecture:validate-complete';\n });\n\n // Check if circular deps validation is already in dependsOn\n const hasCircularDepsValidation = dependsOn.some((dep) => {\n if (typeof dep === 'string') {\n return dep === 'validate-no-file-import-cycles';\n }\n return dep.target === 'validate-no-file-import-cycles';\n });\n\n if (!hasCircularDepsValidation) {\n // Add circular deps validation (per-project check)\n dependsOn.unshift('validate-no-file-import-cycles');\n targetDef.dependsOn = dependsOn;\n updated = true;\n console.log(` ✅ Added circular deps validation to ${executor}`);\n }\n\n if (!hasArchValidation) {\n // Add architecture validation before other dependencies\n dependsOn.unshift('architecture:validate-complete');\n targetDef.dependsOn = dependsOn;\n updated = true;\n console.log(` ✅ Added architecture validation to ${executor}`);\n }\n });\n\n if (updated) {\n updateNxJson(tree, nxJson);\n console.log('✅ Added architecture validation to targetDefaults for used executors');\n } else {\n console.log('ℹ️ Architecture validation already configured in targetDefaults');\n }\n}\n\n/**\n * Scan all project.json files to find which build executors are actually used\n */\nfunction findUsedExecutors(tree: Tree): Set<string> {\n const usedExecutors = new Set<string>();\n\n // Known build executors we care about\n const buildExecutors = new Set([\n '@nx/js:tsc',\n '@nx/esbuild:esbuild',\n '@nx/webpack:webpack',\n '@nx/rollup:rollup',\n '@nx/vite:build',\n '@angular/build:application',\n '@angular-devkit/build-angular:browser',\n '@angular-devkit/build-angular:application'\n ]);\n\n // Scan all project.json files\n tree.listChanges(); // Force tree to be aware of all files\n\n const scanDir = (dir: string) => {\n for (const child of tree.children(dir)) {\n const childPath = dir === '.' ? child : `${dir}/${child}`;\n\n if (tree.isFile(childPath)) {\n if (child === 'project.json') {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Intentionally ignoring JSON parse errors for malformed project.json files\n try {\n const content = tree.read(childPath, 'utf-8');\n if (content) {\n const projectJson = JSON.parse(content);\n if (projectJson.targets) {\n for (const target of Object.values(projectJson.targets)) {\n const executor = (target as { executor?: string })?.executor;\n if (executor && buildExecutors.has(executor)) {\n usedExecutors.add(executor);\n }\n }\n }\n }\n } catch (err: any) {\n //const error = toError(err);\n }\n }\n } else {\n // Skip node_modules and dist\n if (child !== 'node_modules' && child !== 'dist' && child !== '.git') {\n scanDir(childPath);\n }\n }\n }\n };\n\n scanDir('.');\n\n console.log(`ℹ️ Found ${usedExecutors.size} build executors in use: ${[...usedExecutors].join(', ')}`);\n\n return usedExecutors;\n}\n\nfunction addMadgeDependency(tree: Tree) {\n return addDependenciesToPackageJson(tree, {}, { 'madge': '^8.0.0' });\n}\n\nfunction createArchitectureDirectory(tree: Tree): void {\n if (!tree.exists('architecture')) {\n tree.write('architecture/.gitkeep', '');\n console.log('✅ Created architecture/ directory');\n }\n}\n\nfunction addNpmScripts(tree: Tree): void {\n updateJson(tree, 'package.json', (pkgJson) => {\n pkgJson.scripts = pkgJson.scripts ?? {};\n\n // Add architecture validation scripts\n pkgJson.scripts['arch:generate'] = 'nx run architecture:generate';\n pkgJson.scripts['arch:visualize'] = 'nx run architecture:visualize';\n pkgJson.scripts['arch:validate'] = 'nx run architecture:validate-no-architecture-cycles && nx run architecture:validate-no-skiplevel-deps';\n pkgJson.scripts['arch:validate-all'] = 'nx run architecture:validate-no-architecture-cycles && nx run architecture:validate-no-skiplevel-deps && nx run architecture:validate-architecture-unchanged';\n\n // Add file import cycle checking scripts\n pkgJson.scripts['arch:check-circular'] = 'nx run-many --target=validate-no-file-import-cycles --all';\n pkgJson.scripts['arch:check-circular-affected'] = 'nx affected --target=validate-no-file-import-cycles';\n\n // Complete validation including circular deps\n pkgJson.scripts['arch:validate-complete'] = 'npm run arch:validate-all && npm run arch:check-circular';\n\n // Add CI script that runs lint, build, test on affected projects\n pkgJson.scripts['webpieces:ci'] = 'npx nx affected --target=ci';\n\n return pkgJson;\n });\n\n console.log('✅ Added npm scripts for architecture validation, circular dependency checking, and CI');\n}\n\nfunction createEslintConfig(tree: Tree): boolean {\n const webpiecesConfigPath = 'eslint.webpieces.config.mjs';\n const mainConfigPath = 'eslint.config.mjs';\n\n // Always create eslint.webpieces.config.mjs with our rules\n createWebpiecesEslintConfig(tree, webpiecesConfigPath);\n\n // Check if main eslint.config.mjs exists\n const hasExistingConfig = tree.exists(mainConfigPath);\n\n if (!hasExistingConfig) {\n // No existing config - create one that imports webpieces config\n const mainConfig = `// ESLint configuration\n// Imports @webpieces/dev-config rules\n\nimport webpiecesConfig from './eslint.webpieces.config.mjs';\n\n// Export the webpieces configuration\n// You can add your own rules after spreading webpiecesConfig\nexport default [\n ...webpiecesConfig,\n // Add your custom ESLint configuration here\n];\n`;\n\n tree.write(mainConfigPath, mainConfig);\n console.log('✅ Created eslint.config.mjs with @webpieces/dev-config rules');\n }\n\n return hasExistingConfig;\n}\n\nfunction getWebpiecesEslintConfigTemplate(tree: Tree): string {\n // Read from canonical template file (single source of truth)\n const templatePath = 'node_modules/@webpieces/dev-config/templates/eslint.webpieces.config.mjs';\n const template = tree.read(templatePath, 'utf-8');\n\n if (!template) {\n throw new Error(`Could not read ESLint template from ${templatePath}`);\n }\n\n return template;\n}\n\nfunction warnConfigChanges(tree: Tree, configPath: string, newConfig: string): void {\n const version = getPackageVersion(tree);\n const versionedFilename = `${configPath}.v${version}`;\n\n tree.write(versionedFilename, newConfig);\n\n console.log('');\n console.log(`⚠️ ${configPath} has changes`);\n console.log('');\n console.log(' Either you modified the file OR @webpieces/dev-config has updates.');\n console.log('');\n console.log(` Created: ${versionedFilename} with latest version`);\n console.log('');\n console.log(' Please review and merge if needed:');\n console.log(` - Your current: ${configPath}`);\n console.log(` - New version: ${versionedFilename}`);\n console.log('');\n}\n\nfunction createWebpiecesEslintConfig(tree: Tree, configPath: string): void {\n const webpiecesConfig = getWebpiecesEslintConfigTemplate(tree);\n\n if (!tree.exists(configPath)) {\n tree.write(configPath, webpiecesConfig);\n console.log(`✅ Created ${configPath}`);\n return;\n }\n\n const currentContent = tree.read(configPath, 'utf-8');\n if (!currentContent) {\n tree.write(configPath, webpiecesConfig);\n console.log(`✅ Created ${configPath}`);\n return;\n }\n\n const currentHash = calculateHash(currentContent);\n const newHash = calculateHash(webpiecesConfig);\n\n if (currentHash === newHash) {\n console.log(`✅ ${configPath} is up to date`);\n return;\n }\n\n warnConfigChanges(tree, configPath, webpiecesConfig);\n}\n\nfunction createSuccessCallback(\n installTask: ReturnType<typeof addDependenciesToPackageJson>,\n hasExistingEslintConfig: boolean\n) {\n return async () => {\n await installTask();\n\n // ANSI color codes for formatted output\n const GREEN = '\\x1b[32m\\x1b[1m';\n const BOLD = '\\x1b[1m';\n const RESET = '\\x1b[0m';\n\n console.log('');\n console.log('✅ Added madge to devDependencies');\n console.log('');\n console.log(`${GREEN}✅ @webpieces/dev-config plugin initialized!${RESET}`);\n console.log('');\n console.log(`${GREEN}💡 Quick start:${RESET}`);\n console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the dependency graph`);\n console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);\n console.log('');\n console.log(`💡 For full documentation, run: ${BOLD}nx run architecture:help${RESET}`);\n\n // Show ESLint integration instructions if they have an existing config\n if (hasExistingEslintConfig) {\n console.log('');\n console.log('📋 Existing eslint.config.mjs detected');\n console.log('');\n console.log('To use @webpieces/dev-config ESLint rules, add this import to your eslint.config.mjs:');\n console.log('');\n console.log(' import webpiecesConfig from \\'./eslint.webpieces.config.mjs\\';');\n console.log('');\n console.log('Then spread it into your config array:');\n console.log('');\n console.log(' export default [');\n console.log(' ...webpiecesConfig, // Add this line');\n console.log(' // ... your existing config');\n console.log(' ];');\n }\n\n console.log('');\n };\n}\n"]}
@@ -1,16 +0,0 @@
1
- /**
2
- * Validate Versions Executor
3
- *
4
- * Validates package.json versions and checks npm ci compatibility.
5
- * This catches peer dependency conflicts that npm ci catches but npm install doesn't.
6
- *
7
- * Usage:
8
- * nx run dev-config:validate-versions
9
- */
10
- import type { ExecutorContext } from '@nx/devkit';
11
- export interface ValidateVersionsOptions {
12
- }
13
- export interface ExecutorResult {
14
- success: boolean;
15
- }
16
- export default function runExecutor(_options: ValidateVersionsOptions, context: ExecutorContext): Promise<ExecutorResult>;
@@ -1 +0,0 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/executors/validate-versions/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Versions Executor\n *\n * Validates package.json versions and checks npm ci compatibility.\n * This catches peer dependency conflicts that npm ci catches but npm install doesn't.\n *\n * Usage:\n * nx run dev-config:validate-versions\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateVersionsOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// Find all package.json files except node_modules, dist, .nx, .angular\nfunction findPackageJsonFiles(dir: string, basePath = ''): string[] {\n const files: string[] = [];\n const items = fs.readdirSync(dir);\n\n for (const item of items) {\n const fullPath = path.join(dir, item);\n const relativePath = path.join(basePath, item);\n\n // Skip these directories\n if (\n ['node_modules', 'dist', '.nx', '.angular', 'tmp', '.git'].includes(\n item,\n )\n ) {\n continue;\n }\n\n // Skip all hidden directories (starting with .)\n if (item.startsWith('.')) {\n continue;\n }\n\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) {\n files.push(...findPackageJsonFiles(fullPath, relativePath));\n } else if (item === 'package.json') {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n// Check if a version string uses semver ranges\nfunction hasSemverRange(version: string): boolean {\n // Allow workspace protocol\n if (version.startsWith('workspace:')) {\n return false;\n }\n\n // Allow file: protocol (for local packages)\n if (version.startsWith('file:')) {\n return false;\n }\n\n // Check for common semver range patterns\n const semverPatterns = [\n /^\\^/, // ^1.2.3\n /^~/, // ~1.2.3\n /^\\+/, // +1.2.3\n /^\\*/, // *\n /^>/, // >1.2.3\n /^</, // <1.2.3\n /^>=/, // >=1.2.3\n /^<=/, // <=1.2.3\n /\\|\\|/, // 1.2.3 || 2.x\n / - /, // 1.2.3 - 2.3.4\n /^\\d+\\.x/, // 1.x, 1.2.x\n /^latest$/, // latest\n /^next$/, // next\n ];\n\n return semverPatterns.some((pattern) => pattern.test(version));\n}\n\n// Validate a single package.json file for semver ranges\nfunction validatePackageJson(filePath: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const pkg = JSON.parse(content);\n const errors: string[] = [];\n\n // Check dependencies\n if (pkg.dependencies) {\n for (const [name, version] of Object.entries(pkg.dependencies)) {\n // Skip internal workspace packages\n if (name.startsWith('@webpieces/')) {\n continue;\n }\n\n if (hasSemverRange(version as string)) {\n errors.push(\n `dependencies.${name}: \"${version}\" uses semver range (should be fixed version)`,\n );\n }\n }\n }\n\n // Check devDependencies\n if (pkg.devDependencies) {\n for (const [name, version] of Object.entries(pkg.devDependencies)) {\n // Skip internal workspace packages\n if (name.startsWith('@webpieces/')) {\n continue;\n }\n\n if (hasSemverRange(version as string)) {\n errors.push(\n `devDependencies.${name}: \"${version}\" uses semver range (should be fixed version)`,\n );\n }\n }\n }\n\n // Check peerDependencies (these can have ranges for compatibility)\n // We don't validate peerDependencies for semver ranges since they're meant to be flexible\n\n return errors;\n } catch (err: any) {\n //const error = toError(err);\n return [`Failed to parse ${filePath}: ${err.message}`];\n }\n}\n\n// Check npm ci compatibility\nfunction checkNpmCiCompatibility(workspaceRoot: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Run npm install --package-lock-only to check for peer dependency conflicts\n // This simulates what npm ci does without actually installing\n // Use --ignore-scripts to prevent infinite recursion (avoid triggering preinstall hook)\n console.log(' Running npm dependency resolution check (10s timeout)...');\n execSync('npm install --package-lock-only --ignore-scripts 2>&1', {\n cwd: workspaceRoot,\n stdio: 'pipe',\n encoding: 'utf-8',\n timeout: 10000 // 10 second timeout\n });\n return [];\n } catch (err: any) {\n //const error = toError(err);\n // Check if it's a timeout\n if (err.killed) {\n return ['npm dependency check timed out - this might indicate a hang or network issue'];\n }\n\n // Parse the error output to extract peer dependency conflicts\n const output = err.stdout || err.stderr || err.message;\n\n // Check if it's a peer dependency error (npm error, not npm warn)\n if (output.includes('npm error') && (output.includes('ERESOLVE') || output.includes('peer'))) {\n return [output];\n }\n\n // If it's just warnings, not errors, we're OK\n if (output.includes('npm warn') && !output.includes('npm error')) {\n return [];\n }\n\n // Some other error - return it\n return [`npm dependency check failed: ${output}`];\n }\n}\n\n// Check semver ranges in all package.json files\nfunction checkSemverRanges(workspaceRoot: string): { warnings: number } {\n console.log('\\n📋 Checking for semver ranges (warnings only):');\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n let semverWarnings = 0;\n\n for (const filePath of packageFiles) {\n const relativePath = path.relative(workspaceRoot, filePath);\n const errors = validatePackageJson(filePath);\n\n if (errors.length > 0) {\n console.log(` ⚠️ ${relativePath}:`);\n for (const error of errors) {\n console.log(` ${error}`);\n }\n semverWarnings += errors.length;\n } else {\n console.log(` ✅ ${relativePath}`);\n }\n }\n\n if (semverWarnings > 0) {\n console.log(`\\n ⚠️ Note: ${semverWarnings} semver ranges found (consider using fixed versions for reproducibility)`);\n }\n\n return { warnings: semverWarnings };\n}\n\nexport default async function runExecutor(\n _options: ValidateVersionsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n console.log('\\n🔍 Validating Package Versions and npm ci Compatibility\\n');\n\n const workspaceRoot = context.root;\n\n // Step 1: Check npm ci compatibility\n console.log('🔄 Checking npm ci compatibility (peer dependencies):');\n const npmCiErrors = checkNpmCiCompatibility(workspaceRoot);\n if (npmCiErrors.length > 0) {\n console.log(' ❌ npm ci compatibility check failed:');\n console.log(' This means \"npm ci\" will fail in CI even though \"npm install\" works locally.\\n');\n for (const error of npmCiErrors) {\n const errorLines = error.split('\\n').slice(0, 30);\n for (const line of errorLines) {\n console.log(` ${line}`);\n }\n if (error.split('\\n').length > 30) {\n console.log(' ... (truncated)');\n }\n }\n console.log('');\n } else {\n console.log(' ✅ npm ci compatibility check passed');\n }\n\n // Step 2: Check for semver ranges\n const { warnings: semverWarnings } = checkSemverRanges(workspaceRoot);\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n // Summary\n console.log(`\\n📊 Summary:`);\n console.log(` npm ci compatibility: ${npmCiErrors.length === 0 ? '✅' : '❌'}`);\n console.log(` Files checked: ${packageFiles.length}`);\n console.log(` Semver warnings: ${semverWarnings}`);\n console.log(` Errors: ${npmCiErrors.length}`);\n\n if (npmCiErrors.length > 0) {\n console.log('\\n❌ VALIDATION FAILED!');\n console.log(' Fix peer dependency conflicts to avoid CI failures.\\n');\n return { success: false };\n }\n\n console.log('\\n✅ VALIDATION PASSED!');\n return { success: true };\n}\n"]}
@@ -1,8 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/schema",
3
- "title": "Validate Versions Executor",
4
- "description": "Validates package.json versions and checks npm ci compatibility",
5
- "type": "object",
6
- "properties": {},
7
- "required": []
8
- }