@webpieces/nx-webpieces-rules 0.2.127 → 0.3.128

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.2.127",
3
+ "version": "0.3.128",
4
4
  "description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -18,10 +18,10 @@
18
18
  "README.md"
19
19
  ],
20
20
  "dependencies": {
21
- "@webpieces/ai-hook-rules": "0.2.127",
22
- "@webpieces/code-rules": "0.2.127",
23
- "@webpieces/eslint-rules": "0.2.127",
24
- "@webpieces/rules-config": "0.2.127"
21
+ "@webpieces/ai-hook-rules": "0.3.128",
22
+ "@webpieces/code-rules": "0.3.128",
23
+ "@webpieces/eslint-rules": "0.3.128",
24
+ "@webpieces/rules-config": "0.3.128"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@nx/devkit": ">=18.0.0"
@@ -3,18 +3,18 @@
3
3
  *
4
4
  * Two-layer rule:
5
5
  * Layer 1: every .ts file inside an Nx project must live under src/
6
- * (jest.config.ts at project root is the only exception)
7
6
  * Layer 2: every .ts file anywhere in the workspace must belong to some
8
7
  * Nx project. Orphan files (at workspace root or in a non-project
9
8
  * directory) fail the rule unless explicitly allowlisted.
10
9
  *
11
- * Configurable via nx.json targetDefaults:
10
+ * `excludePaths` is holistic — its bare dir names and globs exempt files
11
+ * from BOTH layers. Defaults exempt **\/*.d.ts and **\/jest.config.ts.
12
+ *
13
+ * Configurable via webpieces.config.json:
12
14
  * "validate-ts-in-src": {
13
- * "options": {
14
- * "mode": "ON",
15
- * "excludePaths": [...],
16
- * "allowedRootFiles": [...]
17
- * }
15
+ * "mode": "ON", // "OFF" disables the rule
16
+ * "excludePaths": [...], // dir names + globs, e.g. "**\/codegen.ts"
17
+ * "allowedRootFiles": [...]
18
18
  * }
19
19
  *
20
20
  * Usage: nx run architecture:validate-ts-in-src
@@ -4,18 +4,18 @@
4
4
  *
5
5
  * Two-layer rule:
6
6
  * Layer 1: every .ts file inside an Nx project must live under src/
7
- * (jest.config.ts at project root is the only exception)
8
7
  * Layer 2: every .ts file anywhere in the workspace must belong to some
9
8
  * Nx project. Orphan files (at workspace root or in a non-project
10
9
  * directory) fail the rule unless explicitly allowlisted.
11
10
  *
12
- * Configurable via nx.json targetDefaults:
11
+ * `excludePaths` is holistic — its bare dir names and globs exempt files
12
+ * from BOTH layers. Defaults exempt **\/*.d.ts and **\/jest.config.ts.
13
+ *
14
+ * Configurable via webpieces.config.json:
13
15
  * "validate-ts-in-src": {
14
- * "options": {
15
- * "mode": "ON",
16
- * "excludePaths": [...],
17
- * "allowedRootFiles": [...]
18
- * }
16
+ * "mode": "ON", // "OFF" disables the rule
17
+ * "excludePaths": [...], // dir names + globs, e.g. "**\/codegen.ts"
18
+ * "allowedRootFiles": [...]
19
19
  * }
20
20
  *
21
21
  * Usage: nx run architecture:validate-ts-in-src
@@ -30,6 +30,7 @@ const path = tslib_1.__importStar(require("path"));
30
30
  const DEFAULT_EXCLUDE_PATHS = [
31
31
  'node_modules', 'dist', '.nx', '.git',
32
32
  'architecture', 'tmp', 'scripts',
33
+ '**/*.d.ts', '**/jest.config.ts',
33
34
  ];
34
35
  const DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];
35
36
  class LayerOneViolation {
@@ -77,8 +78,6 @@ function findTsFilesOutsideSrc(projectDir) {
77
78
  if (entry.name === 'dist')
78
79
  continue;
79
80
  if (entry.isFile() && entry.name.endsWith('.ts')) {
80
- if (entry.name === 'jest.config.ts')
81
- continue;
82
81
  violations.push(path.join(projectDir, entry.name));
83
82
  }
84
83
  if (entry.isDirectory()) {
@@ -129,13 +128,15 @@ function findOrphanTsFiles(dir, projectRootSet, workspaceRoot, results) {
129
128
  }
130
129
  }
131
130
  }
132
- function checkLayerOne(projectRoots, workspaceRoot) {
131
+ function checkLayerOne(projectRoots, workspaceRoot, excludePaths) {
133
132
  const violations = [];
134
133
  for (const projectDir of projectRoots) {
135
134
  const projectName = path.relative(workspaceRoot, projectDir);
136
135
  const tsFiles = findTsFilesOutsideSrc(projectDir);
137
136
  for (const tsFile of tsFiles) {
138
137
  const relativePath = path.relative(workspaceRoot, tsFile);
138
+ if ((0, rules_config_1.isPathExcluded)(relativePath, excludePaths))
139
+ continue;
139
140
  violations.push(new LayerOneViolation(relativePath, projectName));
140
141
  }
141
142
  }
@@ -151,6 +152,8 @@ function checkLayerTwo(workspaceRoot, projectRoots, excludePaths, allowedRootFil
151
152
  continue;
152
153
  if (allowedRootFiles.includes(entry.name))
153
154
  continue;
155
+ if ((0, rules_config_1.isPathExcluded)(entry.name, excludePaths))
156
+ continue;
154
157
  violations.push(new LayerTwoViolation(entry.name));
155
158
  continue;
156
159
  }
@@ -161,7 +164,10 @@ function checkLayerTwo(workspaceRoot, projectRoots, excludePaths, allowedRootFil
161
164
  const orphans = [];
162
165
  findOrphanTsFiles(path.join(workspaceRoot, entry.name), projectRootSet, workspaceRoot, orphans);
163
166
  for (const orphan of orphans) {
164
- violations.push(new LayerTwoViolation(path.relative(workspaceRoot, orphan)));
167
+ const relativePath = path.relative(workspaceRoot, orphan);
168
+ if ((0, rules_config_1.isPathExcluded)(relativePath, excludePaths))
169
+ continue;
170
+ violations.push(new LayerTwoViolation(relativePath));
165
171
  }
166
172
  }
167
173
  return violations;
@@ -178,8 +184,11 @@ function reportLayerOneFailure(violations) {
178
184
  for (const v of violations) {
179
185
  console.error(` ❌ ${v.filePath}`);
180
186
  }
181
- console.error('\nTo fix: Move the .ts file(s) into the src/ directory');
182
- console.error('Only exception: jest.config.ts at project root\n');
187
+ console.error('\nTo fix, pick one:');
188
+ console.error(' (a) Move the .ts file(s) into the src/ directory');
189
+ console.error(' (b) Add a glob/dir to validate-ts-in-src.excludePaths in');
190
+ console.error(' webpieces.config.json (e.g. "**/codegen.ts"). Defaults already');
191
+ console.error(' exempt **/*.d.ts and **/jest.config.ts.\n');
183
192
  }
184
193
  function reportLayerTwoFailure(violations) {
185
194
  console.error('❌ TypeScript files found outside any Nx project!\n');
@@ -201,8 +210,8 @@ async function runExecutor(_nxOptions, context) {
201
210
  // and validate-code — via @webpieces/rules-config.
202
211
  const shared = (0, rules_config_1.loadConfig)(context.root);
203
212
  const rule = shared.rules.get('validate-ts-in-src');
204
- if (rule && rule.enabled === false) {
205
- console.log('\n⏭️ Skipping validate-ts-in-src (enabled: false)\n');
213
+ if (rule && rule.isOff) {
214
+ console.log('\n⏭️ Skipping validate-ts-in-src (mode: OFF)\n');
206
215
  return { success: true };
207
216
  }
208
217
  const workspaceRoot = context.root;
@@ -210,7 +219,7 @@ async function runExecutor(_nxOptions, context) {
210
219
  const allowedRootFiles = rule?.options['allowedRootFiles'] ?? DEFAULT_ALLOWED_ROOT_FILES;
211
220
  console.log('\n📁 Validating TypeScript files are in src/ and owned by a project\n');
212
221
  const projectRoots = await getProjectRoots(workspaceRoot);
213
- const layerOneViolations = checkLayerOne(projectRoots, workspaceRoot);
222
+ const layerOneViolations = checkLayerOne(projectRoots, workspaceRoot, excludePaths);
214
223
  const layerTwoViolations = checkLayerTwo(workspaceRoot, projectRoots, excludePaths, allowedRootFiles);
215
224
  if (layerOneViolations.length === 0 && layerTwoViolations.length === 0) {
216
225
  console.log('✅ All .ts files are inside a project\'s src/ directory\n');
@@ -222,7 +231,7 @@ async function runExecutor(_nxOptions, context) {
222
231
  if (layerTwoViolations.length > 0) {
223
232
  reportLayerTwoFailure(layerTwoViolations);
224
233
  }
225
- console.error('To disable: set rules["validate-ts-in-src"].enabled to false in webpieces.config.json\n');
234
+ console.error('To disable: set rules["validate-ts-in-src"].mode to "OFF" in webpieces.config.json\n');
226
235
  return { success: false };
227
236
  }
228
237
  //# sourceMappingURL=executor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-ts-in-src/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate TypeScript Files in src/ Executor\n *\n * Two-layer rule:\n * Layer 1: every .ts file inside an Nx project must live under src/\n * (jest.config.ts at project root is the only exception)\n * Layer 2: every .ts file anywhere in the workspace must belong to some\n * Nx project. Orphan files (at workspace root or in a non-project\n * directory) fail the rule unless explicitly allowlisted.\n *\n * Configurable via nx.json targetDefaults:\n * \"validate-ts-in-src\": {\n * \"options\": {\n * \"mode\": \"ON\",\n * \"excludePaths\": [...],\n * \"allowedRootFiles\": [...]\n * }\n * }\n *\n * Usage: nx run architecture:validate-ts-in-src\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { createProjectGraphAsync, readProjectsConfigurationFromProjectGraph } from '@nx/devkit';\nimport { loadConfig } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport type ValidateTsInSrcMode = 'ON' | 'OFF';\n\nexport interface ValidateTsInSrcOptions {\n mode?: ValidateTsInSrcMode;\n excludePaths?: string[];\n allowedRootFiles?: string[];\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst DEFAULT_EXCLUDE_PATHS: string[] = [\n 'node_modules', 'dist', '.nx', '.git',\n 'architecture', 'tmp', 'scripts',\n];\n\nconst DEFAULT_ALLOWED_ROOT_FILES: string[] = ['jest.setup.ts'];\n\nclass LayerOneViolation {\n filePath: string;\n projectName: string;\n\n constructor(filePath: string, projectName: string) {\n this.filePath = filePath;\n this.projectName = projectName;\n }\n}\n\nclass LayerTwoViolation {\n filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n}\n\nfunction isNodeModulesDir(name: string): boolean {\n return name === 'node_modules' || name.startsWith('node_modules_');\n}\n\nfunction shouldSkipTopLevelDir(name: string, excludePaths: string[]): boolean {\n if (isNodeModulesDir(name)) return true;\n return excludePaths.includes(name);\n}\n\nasync function getProjectRoots(workspaceRoot: string): Promise<string[]> {\n const projectGraph = await createProjectGraphAsync();\n const projectsConfig = readProjectsConfigurationFromProjectGraph(projectGraph);\n const roots: string[] = [];\n for (const cfg of Object.values(projectsConfig.projects)) {\n if (cfg.root === '' || cfg.root === '.') continue;\n if (cfg.root === 'architecture') continue;\n roots.push(path.join(workspaceRoot, cfg.root));\n }\n return roots;\n}\n\nfunction findTsFilesOutsideSrc(projectDir: string): string[] {\n const violations: string[] = [];\n if (!fs.existsSync(projectDir)) return violations;\n const entries = fs.readdirSync(projectDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name === 'src') continue;\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n if (entry.name === 'jest.config.ts') continue;\n violations.push(path.join(projectDir, entry.name));\n }\n\n if (entry.isDirectory()) {\n const tsFiles = findTsFilesRecursively(path.join(projectDir, entry.name));\n violations.push(...tsFiles);\n }\n }\n\n return violations;\n}\n\nfunction findTsFilesRecursively(dir: string): string[] {\n const results: string[] = [];\n if (!fs.existsSync(dir)) return results;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n results.push(...findTsFilesRecursively(fullPath));\n }\n }\n return results;\n}\n\nfunction findOrphanTsFiles(\n dir: string,\n projectRootSet: Set<string>,\n workspaceRoot: string,\n results: string[],\n): void {\n if (!fs.existsSync(dir)) return;\n\n const relDir = path.relative(workspaceRoot, dir);\n if (projectRootSet.has(relDir)) return;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n findOrphanTsFiles(fullPath, projectRootSet, workspaceRoot, results);\n }\n }\n}\n\nfunction checkLayerOne(projectRoots: string[], workspaceRoot: string): LayerOneViolation[] {\n const violations: LayerOneViolation[] = [];\n for (const projectDir of projectRoots) {\n const projectName = path.relative(workspaceRoot, projectDir);\n const tsFiles = findTsFilesOutsideSrc(projectDir);\n for (const tsFile of tsFiles) {\n const relativePath = path.relative(workspaceRoot, tsFile);\n violations.push(new LayerOneViolation(relativePath, projectName));\n }\n }\n return violations;\n}\n\nfunction checkLayerTwo(\n workspaceRoot: string,\n projectRoots: string[],\n excludePaths: string[],\n allowedRootFiles: string[],\n): LayerTwoViolation[] {\n const violations: LayerTwoViolation[] = [];\n const projectRootSet = new Set(\n projectRoots.map((p) => path.relative(workspaceRoot, p)),\n );\n\n const entries = fs.readdirSync(workspaceRoot, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n if (!entry.name.endsWith('.ts')) continue;\n if (allowedRootFiles.includes(entry.name)) continue;\n violations.push(new LayerTwoViolation(entry.name));\n continue;\n }\n if (!entry.isDirectory()) continue;\n if (shouldSkipTopLevelDir(entry.name, excludePaths)) continue;\n\n const orphans: string[] = [];\n findOrphanTsFiles(\n path.join(workspaceRoot, entry.name),\n projectRootSet,\n workspaceRoot,\n orphans,\n );\n for (const orphan of orphans) {\n violations.push(new LayerTwoViolation(path.relative(workspaceRoot, orphan)));\n }\n }\n\n return violations;\n}\n\nfunction reportLayerOneFailure(violations: LayerOneViolation[]): void {\n console.error('❌ TypeScript files found outside src/ directory!\\n');\n console.error('All .ts source files must be inside the project\\'s src/ directory.');\n console.error('This enforces the standard project structure:\\n');\n console.error(' packages/{category}/{name}/');\n console.error(' ├── src/ ← ALL .ts files here');\n console.error(' ├── package.json');\n console.error(' ├── project.json');\n console.error(' └── tsconfig.json\\n');\n\n for (const v of violations) {\n console.error(` ❌ ${v.filePath}`);\n }\n\n console.error('\\nTo fix: Move the .ts file(s) into the src/ directory');\n console.error('Only exception: jest.config.ts at project root\\n');\n}\n\nfunction reportLayerTwoFailure(violations: LayerTwoViolation[]): void {\n console.error('❌ TypeScript files found outside any Nx project!\\n');\n console.error('Every .ts file must belong to an Nx project so it is compiled,');\n console.error('linted, and tested under a known project config. Orphan files are');\n console.error('invisible to the build graph and will rot.\\n');\n\n for (const v of violations) {\n console.error(` ❌ ${v.filePath}`);\n }\n\n console.error('\\nTo fix, pick one:');\n console.error(' (a) Move the file into an existing project\\'s src/ directory');\n console.error(' (b) Create a new project (add project.json) that owns the directory');\n console.error(' (c) Add the containing top-level directory to validate-ts-in-src.excludePaths');\n console.error(' in nx.json targetDefaults, or add the filename to allowedRootFiles');\n console.error(' if it is a legitimate workspace-root file (e.g., jest.setup.ts)\\n');\n}\n\nexport default async function runExecutor(\n _nxOptions: ValidateTsInSrcOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n // Config comes from webpieces.config.json — same source as ai-hooks\n // and validate-code — via @webpieces/rules-config.\n const shared = loadConfig(context.root);\n const rule = shared.rules.get('validate-ts-in-src');\n\n if (rule && rule.enabled === false) {\n console.log('\\n⏭️ Skipping validate-ts-in-src (enabled: false)\\n');\n return { success: true };\n }\n\n const workspaceRoot = context.root;\n const excludePaths =\n (rule?.options['excludePaths'] as string[] | undefined) ?? DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles =\n (rule?.options['allowedRootFiles'] as string[] | undefined) ?? DEFAULT_ALLOWED_ROOT_FILES;\n\n console.log('\\n📁 Validating TypeScript files are in src/ and owned by a project\\n');\n\n const projectRoots = await getProjectRoots(workspaceRoot);\n\n const layerOneViolations = checkLayerOne(projectRoots, workspaceRoot);\n const layerTwoViolations = checkLayerTwo(\n workspaceRoot, projectRoots, excludePaths, allowedRootFiles,\n );\n\n if (layerOneViolations.length === 0 && layerTwoViolations.length === 0) {\n console.log('✅ All .ts files are inside a project\\'s src/ directory\\n');\n return { success: true };\n }\n\n if (layerOneViolations.length > 0) {\n reportLayerOneFailure(layerOneViolations);\n }\n if (layerTwoViolations.length > 0) {\n reportLayerTwoFailure(layerTwoViolations);\n }\n\n console.error('To disable: set rules[\"validate-ts-in-src\"].enabled to false in webpieces.config.json\\n');\n return { success: false };\n}\n"]}
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-ts-in-src/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate TypeScript Files in src/ Executor\n *\n * Two-layer rule:\n * Layer 1: every .ts file inside an Nx project must live under src/\n * Layer 2: every .ts file anywhere in the workspace must belong to some\n * Nx project. Orphan files (at workspace root or in a non-project\n * directory) fail the rule unless explicitly allowlisted.\n *\n * `excludePaths` is holistic — its bare dir names and globs exempt files\n * from BOTH layers. Defaults exempt **\\/*.d.ts and **\\/jest.config.ts.\n *\n * Configurable via webpieces.config.json:\n * \"validate-ts-in-src\": {\n * \"mode\": \"ON\", // \"OFF\" disables the rule\n * \"excludePaths\": [...], // dir names + globs, e.g. \"**\\/codegen.ts\"\n * \"allowedRootFiles\": [...]\n * }\n *\n * Usage: nx run architecture:validate-ts-in-src\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { createProjectGraphAsync, readProjectsConfigurationFromProjectGraph } from '@nx/devkit';\nimport { loadConfig, isPathExcluded } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport type ValidateTsInSrcMode = 'ON' | 'OFF';\n\nexport interface ValidateTsInSrcOptions {\n mode?: ValidateTsInSrcMode;\n excludePaths?: string[];\n allowedRootFiles?: string[];\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst DEFAULT_EXCLUDE_PATHS: string[] = [\n 'node_modules', 'dist', '.nx', '.git',\n 'architecture', 'tmp', 'scripts',\n '**/*.d.ts', '**/jest.config.ts',\n];\n\nconst DEFAULT_ALLOWED_ROOT_FILES: string[] = ['jest.setup.ts'];\n\nclass LayerOneViolation {\n filePath: string;\n projectName: string;\n\n constructor(filePath: string, projectName: string) {\n this.filePath = filePath;\n this.projectName = projectName;\n }\n}\n\nclass LayerTwoViolation {\n filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n}\n\nfunction isNodeModulesDir(name: string): boolean {\n return name === 'node_modules' || name.startsWith('node_modules_');\n}\n\nfunction shouldSkipTopLevelDir(name: string, excludePaths: string[]): boolean {\n if (isNodeModulesDir(name)) return true;\n return excludePaths.includes(name);\n}\n\nasync function getProjectRoots(workspaceRoot: string): Promise<string[]> {\n const projectGraph = await createProjectGraphAsync();\n const projectsConfig = readProjectsConfigurationFromProjectGraph(projectGraph);\n const roots: string[] = [];\n for (const cfg of Object.values(projectsConfig.projects)) {\n if (cfg.root === '' || cfg.root === '.') continue;\n if (cfg.root === 'architecture') continue;\n roots.push(path.join(workspaceRoot, cfg.root));\n }\n return roots;\n}\n\nfunction findTsFilesOutsideSrc(projectDir: string): string[] {\n const violations: string[] = [];\n if (!fs.existsSync(projectDir)) return violations;\n const entries = fs.readdirSync(projectDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name === 'src') continue;\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n violations.push(path.join(projectDir, entry.name));\n }\n\n if (entry.isDirectory()) {\n const tsFiles = findTsFilesRecursively(path.join(projectDir, entry.name));\n violations.push(...tsFiles);\n }\n }\n\n return violations;\n}\n\nfunction findTsFilesRecursively(dir: string): string[] {\n const results: string[] = [];\n if (!fs.existsSync(dir)) return results;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n results.push(...findTsFilesRecursively(fullPath));\n }\n }\n return results;\n}\n\nfunction findOrphanTsFiles(\n dir: string,\n projectRootSet: Set<string>,\n workspaceRoot: string,\n results: string[],\n): void {\n if (!fs.existsSync(dir)) return;\n\n const relDir = path.relative(workspaceRoot, dir);\n if (projectRootSet.has(relDir)) return;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (isNodeModulesDir(entry.name)) continue;\n if (entry.name === 'dist') continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile() && entry.name.endsWith('.ts')) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n findOrphanTsFiles(fullPath, projectRootSet, workspaceRoot, results);\n }\n }\n}\n\nfunction checkLayerOne(\n projectRoots: string[],\n workspaceRoot: string,\n excludePaths: string[],\n): LayerOneViolation[] {\n const violations: LayerOneViolation[] = [];\n for (const projectDir of projectRoots) {\n const projectName = path.relative(workspaceRoot, projectDir);\n const tsFiles = findTsFilesOutsideSrc(projectDir);\n for (const tsFile of tsFiles) {\n const relativePath = path.relative(workspaceRoot, tsFile);\n if (isPathExcluded(relativePath, excludePaths)) continue;\n violations.push(new LayerOneViolation(relativePath, projectName));\n }\n }\n return violations;\n}\n\nfunction checkLayerTwo(\n workspaceRoot: string,\n projectRoots: string[],\n excludePaths: string[],\n allowedRootFiles: string[],\n): LayerTwoViolation[] {\n const violations: LayerTwoViolation[] = [];\n const projectRootSet = new Set(\n projectRoots.map((p) => path.relative(workspaceRoot, p)),\n );\n\n const entries = fs.readdirSync(workspaceRoot, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n if (!entry.name.endsWith('.ts')) continue;\n if (allowedRootFiles.includes(entry.name)) continue;\n if (isPathExcluded(entry.name, excludePaths)) continue;\n violations.push(new LayerTwoViolation(entry.name));\n continue;\n }\n if (!entry.isDirectory()) continue;\n if (shouldSkipTopLevelDir(entry.name, excludePaths)) continue;\n\n const orphans: string[] = [];\n findOrphanTsFiles(\n path.join(workspaceRoot, entry.name),\n projectRootSet,\n workspaceRoot,\n orphans,\n );\n for (const orphan of orphans) {\n const relativePath = path.relative(workspaceRoot, orphan);\n if (isPathExcluded(relativePath, excludePaths)) continue;\n violations.push(new LayerTwoViolation(relativePath));\n }\n }\n\n return violations;\n}\n\nfunction reportLayerOneFailure(violations: LayerOneViolation[]): void {\n console.error('❌ TypeScript files found outside src/ directory!\\n');\n console.error('All .ts source files must be inside the project\\'s src/ directory.');\n console.error('This enforces the standard project structure:\\n');\n console.error(' packages/{category}/{name}/');\n console.error(' ├── src/ ← ALL .ts files here');\n console.error(' ├── package.json');\n console.error(' ├── project.json');\n console.error(' └── tsconfig.json\\n');\n\n for (const v of violations) {\n console.error(` ❌ ${v.filePath}`);\n }\n\n console.error('\\nTo fix, pick one:');\n console.error(' (a) Move the .ts file(s) into the src/ directory');\n console.error(' (b) Add a glob/dir to validate-ts-in-src.excludePaths in');\n console.error(' webpieces.config.json (e.g. \"**/codegen.ts\"). Defaults already');\n console.error(' exempt **/*.d.ts and **/jest.config.ts.\\n');\n}\n\nfunction reportLayerTwoFailure(violations: LayerTwoViolation[]): void {\n console.error('❌ TypeScript files found outside any Nx project!\\n');\n console.error('Every .ts file must belong to an Nx project so it is compiled,');\n console.error('linted, and tested under a known project config. Orphan files are');\n console.error('invisible to the build graph and will rot.\\n');\n\n for (const v of violations) {\n console.error(` ❌ ${v.filePath}`);\n }\n\n console.error('\\nTo fix, pick one:');\n console.error(' (a) Move the file into an existing project\\'s src/ directory');\n console.error(' (b) Create a new project (add project.json) that owns the directory');\n console.error(' (c) Add the containing top-level directory to validate-ts-in-src.excludePaths');\n console.error(' in nx.json targetDefaults, or add the filename to allowedRootFiles');\n console.error(' if it is a legitimate workspace-root file (e.g., jest.setup.ts)\\n');\n}\n\nexport default async function runExecutor(\n _nxOptions: ValidateTsInSrcOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n // Config comes from webpieces.config.json — same source as ai-hooks\n // and validate-code — via @webpieces/rules-config.\n const shared = loadConfig(context.root);\n const rule = shared.rules.get('validate-ts-in-src');\n\n if (rule && rule.isOff) {\n console.log('\\n⏭️ Skipping validate-ts-in-src (mode: OFF)\\n');\n return { success: true };\n }\n\n const workspaceRoot = context.root;\n const excludePaths =\n (rule?.options['excludePaths'] as string[] | undefined) ?? DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles =\n (rule?.options['allowedRootFiles'] as string[] | undefined) ?? DEFAULT_ALLOWED_ROOT_FILES;\n\n console.log('\\n📁 Validating TypeScript files are in src/ and owned by a project\\n');\n\n const projectRoots = await getProjectRoots(workspaceRoot);\n\n const layerOneViolations = checkLayerOne(projectRoots, workspaceRoot, excludePaths);\n const layerTwoViolations = checkLayerTwo(\n workspaceRoot, projectRoots, excludePaths, allowedRootFiles,\n );\n\n if (layerOneViolations.length === 0 && layerTwoViolations.length === 0) {\n console.log('✅ All .ts files are inside a project\\'s src/ directory\\n');\n return { success: true };\n }\n\n if (layerOneViolations.length > 0) {\n reportLayerOneFailure(layerOneViolations);\n }\n if (layerTwoViolations.length > 0) {\n reportLayerTwoFailure(layerTwoViolations);\n }\n\n console.error('To disable: set rules[\"validate-ts-in-src\"].mode to \"OFF\" in webpieces.config.json\\n');\n return { success: false };\n}\n"]}
@@ -8,12 +8,12 @@
8
8
  "type": "string",
9
9
  "enum": ["ON", "OFF"],
10
10
  "default": "ON",
11
- "description": "ON = enforce both layers, OFF = skip validation"
11
+ "description": "ON = enforce both layers, OFF = skip validation. This is the single on/off switch (the legacy 'enabled' boolean has been removed)."
12
12
  },
13
13
  "excludePaths": {
14
14
  "type": "array",
15
15
  "items": { "type": "string" },
16
- "description": "Top-level workspace directory names to skip entirely when looking for orphan .ts files. Defaults: node_modules, dist, .nx, .git, architecture, tmp, scripts. node_modules_* backup directories are always skipped regardless of this list. Override replaces the defaults — re-list any defaults you want to keep."
16
+ "description": "Holistic exclusion list applied to BOTH layers. Each entry is either a bare directory/segment name matched at any depth (e.g. node_modules, dist, scripts) or a glob matched against the workspace-relative path (e.g. '**/*.d.ts', '**/codegen.ts'). Defaults: node_modules, dist, .nx, .git, architecture, tmp, scripts, **/*.d.ts, **/jest.config.ts. node_modules_* backup directories are always skipped. Override replaces the defaults — re-list any defaults you want to keep."
17
17
  },
18
18
  "allowedRootFiles": {
19
19
  "type": "array",