@webpieces/nx-webpieces-rules 0.2.126 → 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.
|
|
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.
|
|
22
|
-
"@webpieces/code-rules": "0.
|
|
23
|
-
"@webpieces/eslint-rules": "0.
|
|
24
|
-
"@webpieces/rules-config": "0.
|
|
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
|
-
*
|
|
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
|
-
* "
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* "
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
-
|
|
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
|
|
182
|
-
console.error('
|
|
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.
|
|
205
|
-
console.log('\n⏭️ Skipping validate-ts-in-src (
|
|
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"].
|
|
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":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;AA2NH,8BA2CC;;AAnQD,uCAAgG;AAChG,0DAAqD;AACrD,+CAAyB;AACzB,mDAA6B;AAc7B,MAAM,qBAAqB,GAAa;IACpC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IACrC,cAAc,EAAE,KAAK,EAAE,SAAS;CACnC,CAAC;AAEF,MAAM,0BAA0B,GAAa,CAAC,eAAe,CAAC,CAAC;AAE/D,MAAM,iBAAiB;IAInB,YAAY,QAAgB,EAAE,WAAmB;QAC7C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACnC,CAAC;CACJ;AAED,MAAM,iBAAiB;IAGnB,YAAY,QAAgB;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AAED,SAAS,gBAAgB,CAAC,IAAY;IAClC,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAE,YAAsB;IAC/D,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,aAAqB;IAChD,MAAM,YAAY,GAAG,MAAM,IAAA,gCAAuB,GAAE,CAAC;IACrD,MAAM,cAAc,GAAG,IAAA,kDAAyC,EAAC,YAAY,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG;YAAE,SAAS;QAClD,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc;YAAE,SAAS;QAC1C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,qBAAqB,CAAC,UAAkB;IAC7C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAClD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;YAAE,SAAS;QACnC,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QAEpC,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;gBAAE,SAAS;YAC9C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1E,UAAU,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;QAChC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAW;IACvC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAExC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,iBAAiB,CACtB,GAAW,EACX,cAA2B,EAC3B,aAAqB,EACrB,OAAiB;IAEjB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO;IAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IACjD,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO;IAEvC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,iBAAiB,CAAC,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACxE,CAAC;IACL,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,YAAsB,EAAE,aAAqB;IAChE,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAClD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC1D,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,SAAS,aAAa,CAClB,aAAqB,EACrB,YAAsB,EACtB,YAAsB,EACtB,gBAA0B;IAE1B,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,MAAM,cAAc,GAAG,IAAI,GAAG,CAC1B,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAC3D,CAAC;IAEF,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC1C,IAAI,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpD,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACnD,SAAS;QACb,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,IAAI,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC;YAAE,SAAS;QAE9D,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,iBAAiB,CACb,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,EACpC,cAAc,EACd,aAAa,EACb,OAAO,CACV,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACjF,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,SAAS,qBAAqB,CAAC,UAA+B;IAC1D,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACpE,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACpF,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC1D,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAEvC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;IACxE,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,qBAAqB,CAAC,UAA+B;IAC1D,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACpE,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAChF,OAAO,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACnF,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAE9D,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrC,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAChF,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;IACvF,OAAO,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACjG,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC1F,OAAO,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;AAC7F,CAAC;AAEc,KAAK,UAAU,WAAW,CACrC,UAAkC,EAClC,OAAwB;IAExB,oEAAoE;IACpE,mDAAmD;IACnD,MAAM,MAAM,GAAG,IAAA,yBAAU,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAEpD,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IACnC,MAAM,YAAY,GACb,IAAI,EAAE,OAAO,CAAC,cAAc,CAA0B,IAAI,qBAAqB,CAAC;IACrF,MAAM,gBAAgB,GACjB,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAA0B,IAAI,0BAA0B,CAAC;IAE9F,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;IAErF,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,CAAC;IAE1D,MAAM,kBAAkB,GAAG,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IACtE,MAAM,kBAAkB,GAAG,aAAa,CACpC,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,CAC9D,CAAC;IAEF,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;QACxE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,yFAAyF,CAAC,CAAC;IACzG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC","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": "
|
|
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",
|