@webpieces/dev-config 0.2.99 ā 0.2.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -4
- package/src/executors/validate-versions-locked/executor.js +4 -0
- package/src/executors/validate-versions-locked/executor.js.map +1 -1
- package/src/generators/init/generator.js +23 -16
- package/src/generators/init/generator.js.map +1 -1
- package/templates/eslint.webpieces-angular.config.mjs +189 -0
- package/templates/eslint.webpieces.config.mjs +6 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/dev-config",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.102",
|
|
4
4
|
"description": "Development configuration, scripts, and patterns for WebPieces projects",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -34,12 +34,22 @@
|
|
|
34
34
|
"README.md"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@webpieces/eslint-plugin": "0.2.
|
|
38
|
-
"@webpieces/architecture-validators": "0.2.
|
|
37
|
+
"@webpieces/eslint-plugin": "0.2.102",
|
|
38
|
+
"@webpieces/architecture-validators": "0.2.102"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"eslint": ">=8.0.0",
|
|
42
|
-
"@nx/devkit": ">=18.0.0"
|
|
42
|
+
"@nx/devkit": ">=18.0.0",
|
|
43
|
+
"@angular-eslint/eslint-plugin-template": ">=18.0.0",
|
|
44
|
+
"@angular-eslint/template-parser": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@angular-eslint/eslint-plugin-template": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"@angular-eslint/template-parser": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
43
53
|
},
|
|
44
54
|
"author": "Dean Hiller",
|
|
45
55
|
"license": "Apache-2.0",
|
|
@@ -32,6 +32,10 @@ function findPackageJsonFiles(dir, basePath = '') {
|
|
|
32
32
|
if (['node_modules', 'dist', '.nx', '.angular', 'tmp', '.git'].includes(item)) {
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
+
// Skip platform-specific node_modules backups (node_modules_mac, node_modules_linux, etc.)
|
|
36
|
+
if (item.startsWith('node_modules_')) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
35
39
|
// Skip all hidden directories (starting with .)
|
|
36
40
|
if (item.startsWith('.')) {
|
|
37
41
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/src/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:\n * 1. LOCKED (exact versions, no semver ranges like ^, ~, *)\n * 2. CONSISTENT across all package.json files (no version 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 * as fs from 'fs';\nimport * as path from 'path';\nimport { toError } from '../../toError';\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: unknown) {\n const error = toError(err);\n return [`Failed to parse ${filePath}: ${error.message}`];\n }\n}\n\n// Track all dependency versions across the monorepo\ninterface DependencyUsage {\n version: string;\n file: string;\n type: 'dependencies' | 'devDependencies';\n}\n\n// webpieces-disable max-lines-new-methods -- Collecting dependencies from all package.json files\n// Collect all dependency versions from all package.json files\nfunction collectAllDependencies(workspaceRoot: string): Map<string, DependencyUsage[]> {\n const dependencyMap = new Map<string, DependencyUsage[]>();\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n for (const filePath of packageFiles) {\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 relativePath = path.relative(workspaceRoot, filePath);\n\n // Collect 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/')) continue;\n\n const usage: DependencyUsage = {\n version: version as string,\n file: relativePath,\n type: 'dependencies'\n };\n\n if (!dependencyMap.has(name)) {\n dependencyMap.set(name, []);\n }\n dependencyMap.get(name)!.push(usage);\n }\n }\n\n // Collect 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/')) continue;\n\n const usage: DependencyUsage = {\n version: version as string,\n file: relativePath,\n type: 'devDependencies'\n };\n\n if (!dependencyMap.has(name)) {\n dependencyMap.set(name, []);\n }\n dependencyMap.get(name)!.push(usage);\n }\n }\n } catch (err: unknown) {\n // const error = toError(err);\n // Intentionally skip files that can't be parsed - this is expected for some package.json files\n }\n }\n\n return dependencyMap;\n}\n\n// webpieces-disable max-lines-new-methods -- Simple iteration logic, splitting would reduce clarity\n// Check for version conflicts across package.json files\nfunction checkVersionConflicts(workspaceRoot: string): string[] {\n console.log('\\nš Checking for version conflicts across package.json files:');\n\n const dependencyMap = collectAllDependencies(workspaceRoot);\n const conflicts: string[] = [];\n\n for (const [packageName, usages] of dependencyMap.entries()) {\n // Get unique versions (ignoring workspace: and file: protocols)\n const versions = new Set(\n usages\n .map(u => u.version)\n .filter(v => !v.startsWith('workspace:') && !v.startsWith('file:'))\n );\n\n if (versions.size > 1) {\n const conflictDetails = usages\n .filter(u => !u.version.startsWith('workspace:') && !u.version.startsWith('file:'))\n .map(u => ` ${u.file} (${u.type}): ${u.version}`)\n .join('\\n');\n\n conflicts.push(` ā ${packageName} has ${versions.size} different versions:\\n${conflictDetails}`);\n }\n }\n\n if (conflicts.length === 0) {\n console.log(' ā
No version conflicts found');\n } else {\n for (const conflict of conflicts) {\n console.log(conflict);\n }\n }\n\n return conflicts;\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\ntype SemverRangeResult = { errors: number };\n\n// Check semver ranges in all package.json files - FAILS if any found\nfunction checkSemverRanges(workspaceRoot: string): SemverRangeResult {\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 and CONSISTENT\\n');\n\n const workspaceRoot = context.root;\n\n // Step 1: Check for semver ranges (FAILS if any found)\n const semverResult = checkSemverRanges(workspaceRoot);\n const semverErrors = semverResult.errors;\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n // Step 2: Check for version conflicts across package.json files\n const versionConflicts = checkVersionConflicts(workspaceRoot);\n\n // Summary\n console.log(`\\nš Summary:`);\n console.log(` Files checked: ${packageFiles.length}`);\n console.log(` Unlocked versions (semver ranges): ${semverErrors}`);\n console.log(` Version conflicts: ${versionConflicts.length}`);\n\n // Fail on semver ranges with educational message\n if (semverErrors > 0) {\n printSemverRangeEducationalMessage(semverErrors);\n return { success: false };\n }\n\n // Fail on version conflicts\n if (versionConflicts.length > 0) {\n console.log('\\nā VALIDATION FAILED!');\n console.log(' Fix version conflicts - all package.json files must use the same version for each dependency.');\n console.log(' This prevents \"works on my machine\" bugs where different projects use different library versions.\\n');\n return { success: false };\n }\n\n console.log('\\nā
VALIDATION PASSED! All versions are locked and consistent.');\n return { success: true };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/src/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:\n * 1. LOCKED (exact versions, no semver ranges like ^, ~, *)\n * 2. CONSISTENT across all package.json files (no version 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 * as fs from 'fs';\nimport * as path from 'path';\nimport { toError } from '../../toError';\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 platform-specific node_modules backups (node_modules_mac, node_modules_linux, etc.)\n if (item.startsWith('node_modules_')) {\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: unknown) {\n const error = toError(err);\n return [`Failed to parse ${filePath}: ${error.message}`];\n }\n}\n\n// Track all dependency versions across the monorepo\ninterface DependencyUsage {\n version: string;\n file: string;\n type: 'dependencies' | 'devDependencies';\n}\n\n// webpieces-disable max-lines-new-methods -- Collecting dependencies from all package.json files\n// Collect all dependency versions from all package.json files\nfunction collectAllDependencies(workspaceRoot: string): Map<string, DependencyUsage[]> {\n const dependencyMap = new Map<string, DependencyUsage[]>();\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n for (const filePath of packageFiles) {\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 relativePath = path.relative(workspaceRoot, filePath);\n\n // Collect 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/')) continue;\n\n const usage: DependencyUsage = {\n version: version as string,\n file: relativePath,\n type: 'dependencies'\n };\n\n if (!dependencyMap.has(name)) {\n dependencyMap.set(name, []);\n }\n dependencyMap.get(name)!.push(usage);\n }\n }\n\n // Collect 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/')) continue;\n\n const usage: DependencyUsage = {\n version: version as string,\n file: relativePath,\n type: 'devDependencies'\n };\n\n if (!dependencyMap.has(name)) {\n dependencyMap.set(name, []);\n }\n dependencyMap.get(name)!.push(usage);\n }\n }\n } catch (err: unknown) {\n // const error = toError(err);\n // Intentionally skip files that can't be parsed - this is expected for some package.json files\n }\n }\n\n return dependencyMap;\n}\n\n// webpieces-disable max-lines-new-methods -- Simple iteration logic, splitting would reduce clarity\n// Check for version conflicts across package.json files\nfunction checkVersionConflicts(workspaceRoot: string): string[] {\n console.log('\\nš Checking for version conflicts across package.json files:');\n\n const dependencyMap = collectAllDependencies(workspaceRoot);\n const conflicts: string[] = [];\n\n for (const [packageName, usages] of dependencyMap.entries()) {\n // Get unique versions (ignoring workspace: and file: protocols)\n const versions = new Set(\n usages\n .map(u => u.version)\n .filter(v => !v.startsWith('workspace:') && !v.startsWith('file:'))\n );\n\n if (versions.size > 1) {\n const conflictDetails = usages\n .filter(u => !u.version.startsWith('workspace:') && !u.version.startsWith('file:'))\n .map(u => ` ${u.file} (${u.type}): ${u.version}`)\n .join('\\n');\n\n conflicts.push(` ā ${packageName} has ${versions.size} different versions:\\n${conflictDetails}`);\n }\n }\n\n if (conflicts.length === 0) {\n console.log(' ā
No version conflicts found');\n } else {\n for (const conflict of conflicts) {\n console.log(conflict);\n }\n }\n\n return conflicts;\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\ntype SemverRangeResult = { errors: number };\n\n// Check semver ranges in all package.json files - FAILS if any found\nfunction checkSemverRanges(workspaceRoot: string): SemverRangeResult {\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 and CONSISTENT\\n');\n\n const workspaceRoot = context.root;\n\n // Step 1: Check for semver ranges (FAILS if any found)\n const semverResult = checkSemverRanges(workspaceRoot);\n const semverErrors = semverResult.errors;\n const packageFiles = findPackageJsonFiles(workspaceRoot);\n\n // Step 2: Check for version conflicts across package.json files\n const versionConflicts = checkVersionConflicts(workspaceRoot);\n\n // Summary\n console.log(`\\nš Summary:`);\n console.log(` Files checked: ${packageFiles.length}`);\n console.log(` Unlocked versions (semver ranges): ${semverErrors}`);\n console.log(` Version conflicts: ${versionConflicts.length}`);\n\n // Fail on semver ranges with educational message\n if (semverErrors > 0) {\n printSemverRangeEducationalMessage(semverErrors);\n return { success: false };\n }\n\n // Fail on version conflicts\n if (versionConflicts.length > 0) {\n console.log('\\nā VALIDATION FAILED!');\n console.log(' Fix version conflicts - all package.json files must use the same version for each dependency.');\n console.log(' This prevents \"works on my machine\" bugs where different projects use different library versions.\\n');\n return { success: false };\n }\n\n console.log('\\nā
VALIDATION PASSED! All versions are locked and consistent.');\n return { success: true };\n}\n"]}
|
|
@@ -209,22 +209,27 @@ function addNpmScripts(tree) {
|
|
|
209
209
|
}
|
|
210
210
|
function createEslintConfig(tree) {
|
|
211
211
|
const webpiecesConfigPath = 'eslint.webpieces.config.mjs';
|
|
212
|
+
const angularConfigPath = 'eslint.webpieces-angular.config.mjs';
|
|
212
213
|
const mainConfigPath = 'eslint.config.mjs';
|
|
213
|
-
// Always create
|
|
214
|
-
|
|
214
|
+
// Always create/update both config files from their canonical templates
|
|
215
|
+
createConfigFromTemplate(tree, webpiecesConfigPath, getTemplateContent(tree, webpiecesConfigPath));
|
|
216
|
+
createConfigFromTemplate(tree, angularConfigPath, getTemplateContent(tree, angularConfigPath));
|
|
215
217
|
// Check if main eslint.config.mjs exists
|
|
216
218
|
const hasExistingConfig = tree.exists(mainConfigPath);
|
|
217
219
|
if (!hasExistingConfig) {
|
|
218
|
-
// No existing config - create one that imports webpieces
|
|
220
|
+
// No existing config - create one that imports both webpieces configs
|
|
219
221
|
const mainConfig = `// ESLint configuration
|
|
220
222
|
// Imports @webpieces/dev-config rules
|
|
221
223
|
|
|
222
224
|
import webpiecesConfig from './eslint.webpieces.config.mjs';
|
|
225
|
+
import angularConfig from './eslint.webpieces-angular.config.mjs';
|
|
223
226
|
|
|
224
227
|
// Export the webpieces configuration
|
|
225
|
-
// You can add your own rules after spreading
|
|
228
|
+
// You can add your own rules after spreading the configs
|
|
229
|
+
// If NOT using Angular: delete eslint.webpieces-angular.config.mjs and remove the angularConfig lines
|
|
226
230
|
export default [
|
|
227
231
|
...webpiecesConfig,
|
|
232
|
+
...angularConfig,
|
|
228
233
|
// Add your custom ESLint configuration here
|
|
229
234
|
];
|
|
230
235
|
`;
|
|
@@ -233,9 +238,8 @@ export default [
|
|
|
233
238
|
}
|
|
234
239
|
return hasExistingConfig;
|
|
235
240
|
}
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
const templatePath = 'node_modules/@webpieces/dev-config/templates/eslint.webpieces.config.mjs';
|
|
241
|
+
function getTemplateContent(tree, configFilename) {
|
|
242
|
+
const templatePath = `node_modules/@webpieces/dev-config/templates/${configFilename}`;
|
|
239
243
|
const template = tree.read(templatePath, 'utf-8');
|
|
240
244
|
if (!template) {
|
|
241
245
|
throw new Error(`Could not read ESLint template from ${templatePath}`);
|
|
@@ -258,26 +262,25 @@ function warnConfigChanges(tree, configPath, newConfig) {
|
|
|
258
262
|
console.log(` - New version: ${versionedFilename}`);
|
|
259
263
|
console.log('');
|
|
260
264
|
}
|
|
261
|
-
function
|
|
262
|
-
const webpiecesConfig = getWebpiecesEslintConfigTemplate(tree);
|
|
265
|
+
function createConfigFromTemplate(tree, configPath, templateContent) {
|
|
263
266
|
if (!tree.exists(configPath)) {
|
|
264
|
-
tree.write(configPath,
|
|
267
|
+
tree.write(configPath, templateContent);
|
|
265
268
|
console.log(`ā
Created ${configPath}`);
|
|
266
269
|
return;
|
|
267
270
|
}
|
|
268
271
|
const currentContent = tree.read(configPath, 'utf-8');
|
|
269
272
|
if (!currentContent) {
|
|
270
|
-
tree.write(configPath,
|
|
273
|
+
tree.write(configPath, templateContent);
|
|
271
274
|
console.log(`ā
Created ${configPath}`);
|
|
272
275
|
return;
|
|
273
276
|
}
|
|
274
277
|
const currentHash = calculateHash(currentContent);
|
|
275
|
-
const newHash = calculateHash(
|
|
278
|
+
const newHash = calculateHash(templateContent);
|
|
276
279
|
if (currentHash === newHash) {
|
|
277
280
|
console.log(`ā
${configPath} is up to date`);
|
|
278
281
|
return;
|
|
279
282
|
}
|
|
280
|
-
warnConfigChanges(tree, configPath,
|
|
283
|
+
warnConfigChanges(tree, configPath, templateContent);
|
|
281
284
|
}
|
|
282
285
|
function createSuccessCallback(installTask, hasExistingEslintConfig) {
|
|
283
286
|
return async () => {
|
|
@@ -301,16 +304,20 @@ function createSuccessCallback(installTask, hasExistingEslintConfig) {
|
|
|
301
304
|
console.log('');
|
|
302
305
|
console.log('š Existing eslint.config.mjs detected');
|
|
303
306
|
console.log('');
|
|
304
|
-
console.log('To use @webpieces/dev-config ESLint rules, add
|
|
307
|
+
console.log('To use @webpieces/dev-config ESLint rules, add these imports to your eslint.config.mjs:');
|
|
305
308
|
console.log('');
|
|
306
309
|
console.log(' import webpiecesConfig from \'./eslint.webpieces.config.mjs\';');
|
|
310
|
+
console.log(' import angularConfig from \'./eslint.webpieces-angular.config.mjs\';');
|
|
307
311
|
console.log('');
|
|
308
|
-
console.log('Then spread
|
|
312
|
+
console.log('Then spread them into your config array:');
|
|
309
313
|
console.log('');
|
|
310
314
|
console.log(' export default [');
|
|
311
|
-
console.log(' ...webpiecesConfig,
|
|
315
|
+
console.log(' ...webpiecesConfig, // Base rules');
|
|
316
|
+
console.log(' ...angularConfig, // Angular rules (delete file + this line if not Angular)');
|
|
312
317
|
console.log(' // ... your existing config');
|
|
313
318
|
console.log(' ];');
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log('š” Not using Angular? Delete eslint.webpieces-angular.config.mjs and remove its import.');
|
|
314
321
|
}
|
|
315
322
|
console.log('');
|
|
316
323
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/src/generators/init/generator.ts"],"names":[],"mappings":";;AAwCA,gCAaC;AArDD,uCAAmH;AACnH,mCAAoC;AAUpC,SAAS,aAAa,CAAC,OAAe;IAClC,OAAO,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAU;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,iDAAiD,EAAE,OAAO,CAAC,CAAC;IACtF,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC,OAAO,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACY,KAAK,UAAU,aAAa,CAAC,IAAU,EAAE,OAA4B;IAChF,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxB,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,2BAA2B,CAAC,IAAI,CAAC,CAAC;IAClC,aAAa,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAEzD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,qBAAqB,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,cAAc,CAAC,IAAU;IAC9B,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,MAAM,UAAU,GAAG,uBAAuB,CAAC;IAC3C,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CACzC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAC5E,CAAC;IAEF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,kEAAkE;QAClE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE;gBACL,SAAS,EAAE;oBACP,WAAW,EAAE;wBACT,kBAAkB,EAAE,EAAE;wBACtB,6BAA6B,EAAE,EAAE;qBACpC;iBACJ;aACJ;SACJ,CAAC,CAAC;QACH,IAAA,qBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,gBAAgB,UAAU,yCAAyC,CAAC,CAAC;IACrF,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,OAAO,UAAU,+BAA+B,CAAC,CAAC;IAClE,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAU;IACjC,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QACzB,MAAM,CAAC,cAAc,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED,2DAA2D;IAC3D,MAAM,aAAa,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAE9C,+DAA+D;IAC/D,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,aAAa,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC/B,IAAI,CAAC,MAAM,CAAC,cAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,cAAe,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC1C,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,cAAe,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,SAAS,GAAG,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC;QAE1C,+BAA+B;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,SAAS,GAAG,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QAED,2DAA2D;QAC3D,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,GAAG,KAAK,gCAAgC,CAAC;YACpD,CAAC;YACD,OAAO,GAAG,CAAC,MAAM,KAAK,gCAAgC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,4DAA4D;QAC5D,MAAM,yBAAyB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACrD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,GAAG,KAAK,gCAAgC,CAAC;YACpD,CAAC;YACD,OAAO,GAAG,CAAC,MAAM,KAAK,gCAAgC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC7B,mDAAmD;YACnD,SAAS,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;YACpD,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC;YAChC,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrB,wDAAwD;YACxD,SAAS,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;YACpD,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC;YAChC,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,wCAAwC,QAAQ,EAAE,CAAC,CAAC;QACpE,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,OAAO,EAAE,CAAC;QACV,IAAA,qBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;IACxF,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;IACpF,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAU;IACjC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IAExC,sCAAsC;IACtC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;QAC3B,YAAY;QACZ,qBAAqB;QACrB,qBAAqB;QACrB,mBAAmB;QACnB,gBAAgB;QAChB,4BAA4B;QAC5B,uCAAuC;QACvC,2CAA2C;KAC9C,CAAC,CAAC;IAEH,8BAA8B;IAC9B,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,sCAAsC;IAE1D,MAAM,OAAO,GAAG,CAAC,GAAW,EAAQ,EAAE;QAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;YAE1D,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzB,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;oBAC3B,2IAA2I;oBAC3I,IAAI,CAAC;wBACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;wBAC9C,IAAI,OAAO,EAAE,CAAC;4BACV,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;4BACxC,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gCACtB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;oCACtD,MAAM,QAAQ,GAAI,MAAwB,EAAE,QAAQ,CAAC;oCACrD,IAAI,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;wCAC3C,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oCAChC,CAAC;gCACL,CAAC;4BACL,CAAC;wBACL,CAAC;oBACL,CAAC;oBAAC,OAAO,GAAY,EAAE,CAAC;wBACpB,6BAA6B;oBACjC,CAAC;gBACL,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,6BAA6B;gBAC7B,IAAI,KAAK,KAAK,cAAc,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;oBACnE,OAAO,CAAC,SAAS,CAAC,CAAC;gBACvB,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,CAAC,GAAG,CAAC,aAAa,aAAa,CAAC,IAAI,4BAA4B,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAExG,OAAO,aAAa,CAAC;AACzB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU;IAClC,OAAO,IAAA,qCAA4B,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,2BAA2B,CAAC,IAAU;IAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACrD,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,IAAU;IAC7B,IAAA,mBAAU,EAAC,IAAI,EAAE,cAAc,EAAE,CAAC,OAAO,EAAE,EAAE;QACzC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAExC,2BAA2B;QAC3B,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,8BAA8B,CAAC;QAClE,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,+BAA+B,CAAC;QAEpE,iEAAiE;QACjE,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,6BAA6B,CAAC;QAEhE,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU;IAClC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;IAC1D,MAAM,cAAc,GAAG,mBAAmB,CAAC;IAE3C,2DAA2D;IAC3D,2BAA2B,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;IAEvD,yCAAyC;IACzC,MAAM,iBAAiB,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAEtD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,gEAAgE;QAChE,MAAM,UAAU,GAAG;;;;;;;;;;;CAW1B,CAAC;QAEM,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,iBAAiB,CAAC;AAC7B,CAAC;AAED,SAAS,gCAAgC,CAAC,IAAU;IAChD,6DAA6D;IAC7D,MAAM,YAAY,GAAG,0EAA0E,CAAC;IAChG,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAElD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,uCAAuC,YAAY,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAU,EAAE,UAAkB,EAAE,SAAiB;IACxE,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,iBAAiB,GAAG,GAAG,UAAU,KAAK,OAAO,EAAE,CAAC;IAEtD,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IAEzC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,UAAU,cAAc,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,eAAe,iBAAiB,sBAAsB,CAAC,CAAC;IACpE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,wBAAwB,iBAAiB,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,2BAA2B,CAAC,IAAU,EAAE,UAAkB;IAC/D,MAAM,eAAe,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;IAE/D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;QACvC,OAAO;IACX,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,cAAc,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;QACvC,OAAO;IACX,CAAC;IAED,MAAM,WAAW,GAAG,aAAa,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAE/C,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,UAAU,gBAAgB,CAAC,CAAC;QAC7C,OAAO;IACX,CAAC;IAED,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,qBAAqB,CAC1B,WAA4D,EAC5D,uBAAgC;IAEhC,OAAO,KAAK,IAAI,EAAE;QACd,MAAM,WAAW,EAAE,CAAC;QAEpB,wCAAwC;QACxC,MAAM,KAAK,GAAG,iBAAiB,CAAC;QAChC,MAAM,IAAI,GAAG,SAAS,CAAC;QACvB,MAAM,KAAK,GAAG,SAAS,CAAC;QAExB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,8CAA8C,KAAK,EAAE,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,kBAAkB,KAAK,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,wBAAwB,KAAK,qCAAqC,CAAC,CAAC;QAC1F,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,uBAAuB,KAAK,oCAAoC,CAAC,CAAC;QACxF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,2BAA2B,KAAK,EAAE,CAAC,CAAC;QAEvF,uEAAuE;QACvE,IAAI,uBAAuB,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;YACrG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;YAChF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC,CAAC;AACN,CAAC","sourcesContent":["import { formatFiles, readNxJson, Tree, updateNxJson, updateJson, addDependenciesToPackageJson } from '@nx/devkit';\nimport { createHash } from 'crypto';\n\nexport interface InitGeneratorSchema {\n skipFormat?: boolean;\n}\n\ninterface ProjectTarget {\n executor?: string;\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): void => {\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 ProjectTarget)?.executor;\n if (executor && buildExecutors.has(executor)) {\n usedExecutors.add(executor);\n }\n }\n }\n }\n } catch (err: unknown) {\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 scripts\n pkgJson.scripts['arch:generate'] = 'nx run architecture:generate';\n pkgJson.scripts['arch:visualize'] = 'nx run architecture:visualize';\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 generation 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 webpieces:ci${RESET} # Run CI on affected projects`);\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\ninterface ProjectTarget {\n executor?: string;\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): void => {\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 ProjectTarget)?.executor;\n if (executor && buildExecutors.has(executor)) {\n usedExecutors.add(executor);\n }\n }\n }\n }\n } catch (err: unknown) {\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 scripts\n pkgJson.scripts['arch:generate'] = 'nx run architecture:generate';\n pkgJson.scripts['arch:visualize'] = 'nx run architecture:visualize';\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 generation and CI');\n}\n\nfunction createEslintConfig(tree: Tree): boolean {\n const webpiecesConfigPath = 'eslint.webpieces.config.mjs';\n const angularConfigPath = 'eslint.webpieces-angular.config.mjs';\n const mainConfigPath = 'eslint.config.mjs';\n\n // Always create/update both config files from their canonical templates\n createConfigFromTemplate(tree, webpiecesConfigPath, getTemplateContent(tree, webpiecesConfigPath));\n createConfigFromTemplate(tree, angularConfigPath, getTemplateContent(tree, angularConfigPath));\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 both webpieces configs\n const mainConfig = `// ESLint configuration\n// Imports @webpieces/dev-config rules\n\nimport webpiecesConfig from './eslint.webpieces.config.mjs';\nimport angularConfig from './eslint.webpieces-angular.config.mjs';\n\n// Export the webpieces configuration\n// You can add your own rules after spreading the configs\n// If NOT using Angular: delete eslint.webpieces-angular.config.mjs and remove the angularConfig lines\nexport default [\n ...webpiecesConfig,\n ...angularConfig,\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 getTemplateContent(tree: Tree, configFilename: string): string {\n const templatePath = `node_modules/@webpieces/dev-config/templates/${configFilename}`;\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 createConfigFromTemplate(tree: Tree, configPath: string, templateContent: string): void {\n if (!tree.exists(configPath)) {\n tree.write(configPath, templateContent);\n console.log(`ā
Created ${configPath}`);\n return;\n }\n\n const currentContent = tree.read(configPath, 'utf-8');\n if (!currentContent) {\n tree.write(configPath, templateContent);\n console.log(`ā
Created ${configPath}`);\n return;\n }\n\n const currentHash = calculateHash(currentContent);\n const newHash = calculateHash(templateContent);\n\n if (currentHash === newHash) {\n console.log(`ā
${configPath} is up to date`);\n return;\n }\n\n warnConfigChanges(tree, configPath, templateContent);\n}\n\nfunction createSuccessCallback(\n installTask: ReturnType<typeof addDependenciesToPackageJson>,\n hasExistingEslintConfig: boolean\n): () => Promise<void> {\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 webpieces:ci${RESET} # Run CI on affected projects`);\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 these imports to your eslint.config.mjs:');\n console.log('');\n console.log(' import webpiecesConfig from \\'./eslint.webpieces.config.mjs\\';');\n console.log(' import angularConfig from \\'./eslint.webpieces-angular.config.mjs\\';');\n console.log('');\n console.log('Then spread them into your config array:');\n console.log('');\n console.log(' export default [');\n console.log(' ...webpiecesConfig, // Base rules');\n console.log(' ...angularConfig, // Angular rules (delete file + this line if not Angular)');\n console.log(' // ... your existing config');\n console.log(' ];');\n console.log('');\n console.log('š” Not using Angular? Delete eslint.webpieces-angular.config.mjs and remove its import.');\n }\n\n console.log('');\n };\n}\n"]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// @webpieces/dev-config Angular ESLint Configuration
|
|
2
|
+
// This is the canonical template for Angular projects using external clients
|
|
3
|
+
//
|
|
4
|
+
// IMPORTANT: When modifying rules here, also update:
|
|
5
|
+
// - /eslint.webpieces-angular.config.mjs (webpieces workspace version with loadWorkspaceRules)
|
|
6
|
+
//
|
|
7
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
8
|
+
// ā NOT USING ANGULAR? ā
|
|
9
|
+
// ā 1. Delete this file ā
|
|
10
|
+
// ā 2. Remove its import from eslint.config.mjs ā
|
|
11
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
12
|
+
//
|
|
13
|
+
// SETUP: Replace the placeholder paths below with your actual source paths:
|
|
14
|
+
// YOUR_CLIENT_PATH ā e.g. services/website/client
|
|
15
|
+
// YOUR_SERVER_PATH ā e.g. services/website/server
|
|
16
|
+
|
|
17
|
+
import webpiecesPlugin from '@webpieces/eslint-plugin';
|
|
18
|
+
import angularTemplatePlugin from '@angular-eslint/eslint-plugin-template';
|
|
19
|
+
import angularTemplateParser from '@angular-eslint/template-parser';
|
|
20
|
+
|
|
21
|
+
export default [
|
|
22
|
+
// āāā Angular HTML template rules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
23
|
+
// Applies to all Angular HTML template files
|
|
24
|
+
{
|
|
25
|
+
files: ['**/*.html'],
|
|
26
|
+
languageOptions: {
|
|
27
|
+
parser: angularTemplateParser,
|
|
28
|
+
},
|
|
29
|
+
plugins: {
|
|
30
|
+
'@webpieces': webpiecesPlugin,
|
|
31
|
+
'@angular-eslint/template': angularTemplatePlugin,
|
|
32
|
+
},
|
|
33
|
+
rules: {
|
|
34
|
+
// Require [templateClassType] on <ng-template> with let- variables
|
|
35
|
+
'@webpieces/require-typed-template': 'error',
|
|
36
|
+
// Ban *matCellDef/*matHeaderCellDef ā use div-grid tables instead
|
|
37
|
+
'@webpieces/no-mat-cell-def': 'error',
|
|
38
|
+
// Enforce modern Angular control flow (@if, @for, @switch)
|
|
39
|
+
'@angular-eslint/template/prefer-control-flow': 'error',
|
|
40
|
+
// Accessibility rules ā adjust to your project's needs
|
|
41
|
+
'@angular-eslint/template/click-events-have-key-events': 'off',
|
|
42
|
+
'@angular-eslint/template/interactive-supports-focus': 'off',
|
|
43
|
+
'@angular-eslint/template/alt-text': 'off',
|
|
44
|
+
'@angular-eslint/template/label-has-associated-control': 'off',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// āāā Angular client TypeScript rules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
49
|
+
// Replace YOUR_CLIENT_PATH with your client source path (e.g. services/website/client)
|
|
50
|
+
{
|
|
51
|
+
files: ['YOUR_CLIENT_PATH/**/*.ts', 'YOUR_CLIENT_PATH/**/*.tsx'],
|
|
52
|
+
rules: {
|
|
53
|
+
// Prevent console.log leaking to the browser
|
|
54
|
+
'no-console': 'error',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
files: ['YOUR_CLIENT_PATH/**/*.ts', 'YOUR_CLIENT_PATH/**/*.tsx'],
|
|
59
|
+
rules: {
|
|
60
|
+
'no-restricted-syntax': [
|
|
61
|
+
'error',
|
|
62
|
+
// Ban this.route.data ā use the service pattern instead
|
|
63
|
+
{
|
|
64
|
+
selector:
|
|
65
|
+
'MemberExpression[object.type="MemberExpression"][object.object.type="ThisExpression"][object.property.name="route"][property.name="data"]',
|
|
66
|
+
message:
|
|
67
|
+
'Do not use this.route.data ā use the service pattern instead. ' +
|
|
68
|
+
'The service pattern is more flexible, allowing other components to listen as well.',
|
|
69
|
+
},
|
|
70
|
+
// Ban async ngOnInit ā Angular does NOT await the Promise return value
|
|
71
|
+
{
|
|
72
|
+
selector: 'MethodDefinition[key.name="ngOnInit"][value.async=true]',
|
|
73
|
+
message:
|
|
74
|
+
'async ngOnInit() is NOT allowed ā Angular does NOT await the Promise return value! ' +
|
|
75
|
+
'Use resolvers for data loading, not async ngOnInit.',
|
|
76
|
+
},
|
|
77
|
+
// Ban Angular signals ā use plain class properties with RxJS subscriptions
|
|
78
|
+
{
|
|
79
|
+
selector: 'CallExpression[callee.name="signal"]',
|
|
80
|
+
message:
|
|
81
|
+
'Angular signal() is banned. Use plain class properties set in ngOnInit via RxJS subscriptions. ' +
|
|
82
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
selector: 'CallExpression[callee.name="computed"]',
|
|
86
|
+
message:
|
|
87
|
+
'Angular computed() is banned. Use getter methods or update properties in ngOnInit subscriptions. ' +
|
|
88
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
selector: 'CallExpression[callee.name="effect"]',
|
|
92
|
+
message:
|
|
93
|
+
'Angular effect() is banned. Use RxJS subscriptions in ngOnInit instead. ' +
|
|
94
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
selector: 'CallExpression[callee.name="model"]',
|
|
98
|
+
message:
|
|
99
|
+
'Angular model() is banned. Use plain class properties with RxJS. ' +
|
|
100
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
selector: 'CallExpression[callee.name="input"]',
|
|
104
|
+
message:
|
|
105
|
+
'Angular signal-based input() is banned. Use @Input() decorator instead. ' +
|
|
106
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
selector: 'CallExpression[callee.name="output"]',
|
|
110
|
+
message:
|
|
111
|
+
'Angular signal-based output() is banned. Use @Output() decorator instead. ' +
|
|
112
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
selector: 'CallExpression[callee.name="toSignal"]',
|
|
116
|
+
message:
|
|
117
|
+
'Angular toSignal() is banned. Keep using RxJS observables with subscriptions in ngOnInit. ' +
|
|
118
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
selector: 'TSTypeReference[typeName.name="Signal"]',
|
|
122
|
+
message:
|
|
123
|
+
'Signal type is banned. Use plain property types instead. ' +
|
|
124
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
selector: 'TSTypeReference[typeName.name="WritableSignal"]',
|
|
128
|
+
message:
|
|
129
|
+
'WritableSignal type is banned. Use plain property types instead. ' +
|
|
130
|
+
'Use eslint-disable-next-line no-restricted-syntax for case-by-case exceptions.',
|
|
131
|
+
},
|
|
132
|
+
// Ban direct Sentry.captureException ā use your wrapper function instead
|
|
133
|
+
{
|
|
134
|
+
selector:
|
|
135
|
+
'CallExpression[callee.object.name="Sentry"][callee.property.name="captureException"]',
|
|
136
|
+
message:
|
|
137
|
+
'Direct Sentry.captureException() is banned. ' +
|
|
138
|
+
'Use your reportSentryError() wrapper function instead.',
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
// Ban MatTableModule ā use div-grid tables instead
|
|
144
|
+
{
|
|
145
|
+
files: ['YOUR_CLIENT_PATH/**/*.ts'],
|
|
146
|
+
rules: {
|
|
147
|
+
'no-restricted-imports': [
|
|
148
|
+
'error',
|
|
149
|
+
{
|
|
150
|
+
paths: [
|
|
151
|
+
{
|
|
152
|
+
name: '@angular/material/table',
|
|
153
|
+
message:
|
|
154
|
+
'MatTableModule is banned. Use the div-grid table pattern instead. ' +
|
|
155
|
+
'Div-grid tables are inherently type-safe with @for loops + strictTemplates.',
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// āāā Server TypeScript rules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
164
|
+
// Replace YOUR_SERVER_PATH with your server source path (e.g. services/website/server)
|
|
165
|
+
{
|
|
166
|
+
files: ['YOUR_SERVER_PATH/**/*.ts'],
|
|
167
|
+
rules: {
|
|
168
|
+
'no-restricted-syntax': [
|
|
169
|
+
'error',
|
|
170
|
+
// Ban direct Sentry.captureException ā use your wrapper function instead
|
|
171
|
+
{
|
|
172
|
+
selector:
|
|
173
|
+
'CallExpression[callee.object.name="Sentry"][callee.property.name="captureException"]',
|
|
174
|
+
message:
|
|
175
|
+
'Direct Sentry.captureException() is banned. ' +
|
|
176
|
+
'Use your reportSentryException() wrapper function instead.',
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// āāā TypeScript preferences āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
183
|
+
{
|
|
184
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
185
|
+
rules: {
|
|
186
|
+
'@typescript-eslint/no-inferrable-types': 'off',
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
];
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
// IMPORTANT: When modifying rules here, also update:
|
|
5
5
|
// - /eslint.webpieces.config.mjs (webpieces workspace version with loadWorkspaceRules)
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// Base rules only ā no Angular dependencies.
|
|
8
|
+
// For Angular projects, also use eslint.webpieces-angular.config.mjs
|
|
9
9
|
|
|
10
10
|
import webpiecesPlugin from '@webpieces/eslint-plugin';
|
|
11
11
|
|
|
12
12
|
export default [
|
|
13
13
|
{
|
|
14
|
-
ignores: ['**/dist', '**/node_modules', '**/coverage', '**/.nx'],
|
|
14
|
+
ignores: ['**/dist', '**/node_modules', '**/coverage', '**/.nx', '**/generated'],
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
@@ -22,9 +22,10 @@ export default [
|
|
|
22
22
|
'@webpieces/catch-error-pattern': 'error',
|
|
23
23
|
// READ tmp/webpieces/webpieces.exceptions.md for AI rollout instructions and rationale
|
|
24
24
|
'@webpieces/no-unmanaged-exceptions': 'error',
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
'@webpieces/max-method-lines': ['error', { max: 150 }],
|
|
26
|
+
'@webpieces/max-file-lines': ['error', { max: 901 }],
|
|
27
27
|
'@webpieces/enforce-architecture': 'error',
|
|
28
|
+
'@webpieces/no-json-property-primitive-type': 'error',
|
|
28
29
|
},
|
|
29
30
|
},
|
|
30
31
|
];
|