@webpieces/nx-webpieces-rules 0.0.1 → 0.2.114
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 +4 -4
- package/src/executor-result.d.ts +4 -0
- package/src/executor-result.js +10 -0
- package/src/executor-result.js.map +1 -0
- package/src/executors/generate/executor.d.ts +16 -0
- package/src/executors/generate/{executor.ts → executor.js} +15 -30
- package/src/executors/generate/executor.js.map +1 -0
- package/src/executors/help/executor.d.ts +8 -0
- package/src/executors/help/{executor.ts → executor.js} +5 -12
- package/src/executors/help/executor.js.map +1 -0
- package/src/executors/validate-architecture-unchanged/executor.d.ts +17 -0
- package/src/executors/validate-architecture-unchanged/{executor.ts → executor.js} +24 -46
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -0
- package/src/executors/validate-catch-error-pattern/executor.d.ts +3 -0
- package/src/executors/validate-catch-error-pattern/executor.js +10 -0
- package/src/executors/validate-catch-error-pattern/executor.js.map +1 -0
- package/src/executors/validate-code/executor.d.ts +3 -0
- package/src/executors/validate-code/executor.js +10 -0
- package/src/executors/validate-code/executor.js.map +1 -0
- package/src/executors/validate-dtos/executor.d.ts +3 -0
- package/src/executors/validate-dtos/executor.js +10 -0
- package/src/executors/validate-dtos/executor.js.map +1 -0
- package/src/executors/validate-eslint-sync/executor.d.ts +7 -0
- package/src/executors/validate-eslint-sync/{executor.ts → executor.js} +19 -37
- package/src/executors/validate-eslint-sync/executor.js.map +1 -0
- package/src/executors/validate-modified-files/executor.d.ts +3 -0
- package/src/executors/validate-modified-files/executor.js +10 -0
- package/src/executors/validate-modified-files/executor.js.map +1 -0
- package/src/executors/validate-modified-methods/executor.d.ts +3 -0
- package/src/executors/validate-modified-methods/executor.js +10 -0
- package/src/executors/validate-modified-methods/executor.js.map +1 -0
- package/src/executors/validate-new-methods/executor.d.ts +3 -0
- package/src/executors/validate-new-methods/executor.js +10 -0
- package/src/executors/validate-new-methods/executor.js.map +1 -0
- package/src/executors/validate-no-any-unknown/executor.d.ts +3 -0
- package/src/executors/validate-no-any-unknown/executor.js +10 -0
- package/src/executors/validate-no-any-unknown/executor.js.map +1 -0
- package/src/executors/validate-no-architecture-cycles/executor.d.ts +16 -0
- package/src/executors/validate-no-architecture-cycles/{executor.ts → executor.js} +16 -28
- package/src/executors/validate-no-architecture-cycles/executor.js.map +1 -0
- package/src/executors/validate-no-destructure/executor.d.ts +3 -0
- package/src/executors/validate-no-destructure/executor.js +10 -0
- package/src/executors/validate-no-destructure/executor.js.map +1 -0
- package/src/executors/validate-no-direct-api-resolver/executor.d.ts +3 -0
- package/src/executors/validate-no-direct-api-resolver/executor.js +10 -0
- package/src/executors/validate-no-direct-api-resolver/executor.js.map +1 -0
- package/src/executors/validate-no-implicit-any/executor.d.ts +3 -0
- package/src/executors/validate-no-implicit-any/executor.js +10 -0
- package/src/executors/validate-no-implicit-any/executor.js.map +1 -0
- package/src/executors/validate-no-inline-types/executor.d.ts +3 -0
- package/src/executors/validate-no-inline-types/executor.js +10 -0
- package/src/executors/validate-no-inline-types/executor.js.map +1 -0
- package/src/executors/validate-no-skiplevel-deps/executor.d.ts +19 -0
- package/src/executors/validate-no-skiplevel-deps/{executor.ts → executor.js} +23 -63
- package/src/executors/validate-no-skiplevel-deps/executor.js.map +1 -0
- package/src/executors/validate-no-unmanaged-exceptions/executor.d.ts +3 -0
- package/src/executors/validate-no-unmanaged-exceptions/executor.js +10 -0
- package/src/executors/validate-no-unmanaged-exceptions/executor.js.map +1 -0
- package/src/executors/validate-packagejson/executor.d.ts +16 -0
- package/src/executors/validate-packagejson/{executor.ts → executor.js} +15 -32
- package/src/executors/validate-packagejson/executor.js.map +1 -0
- package/src/executors/validate-prisma-converters/executor.d.ts +3 -0
- package/src/executors/validate-prisma-converters/executor.js +10 -0
- package/src/executors/validate-prisma-converters/executor.js.map +1 -0
- package/src/executors/validate-return-types/executor.d.ts +3 -0
- package/src/executors/validate-return-types/executor.js +10 -0
- package/src/executors/validate-return-types/executor.js.map +1 -0
- package/src/executors/validate-ts-in-src/executor.d.ts +32 -0
- package/src/executors/validate-ts-in-src/{executor.ts → executor.js} +80 -135
- package/src/executors/validate-ts-in-src/executor.js.map +1 -0
- package/src/executors/validate-versions-locked/executor.d.ts +22 -0
- package/src/executors/validate-versions-locked/{executor.ts → executor.js} +49 -116
- package/src/executors/validate-versions-locked/executor.js.map +1 -0
- package/src/executors/visualize/executor.d.ts +17 -0
- package/src/executors/visualize/{executor.ts → executor.js} +16 -30
- package/src/executors/visualize/executor.js.map +1 -0
- package/src/{index.ts → index.d.ts} +5 -1
- package/src/index.js +14 -0
- package/src/index.js.map +1 -0
- package/src/lib/graph-comparator.d.ts +39 -0
- package/src/lib/{graph-comparator.ts → graph-comparator.js} +18 -67
- package/src/lib/graph-comparator.js.map +1 -0
- package/src/lib/graph-generator.d.ts +19 -0
- package/src/lib/{graph-generator.ts → graph-generator.js} +17 -30
- package/src/lib/graph-generator.js.map +1 -0
- package/src/lib/graph-loader.d.ts +31 -0
- package/src/lib/{graph-loader.ts → graph-loader.js} +24 -42
- package/src/lib/graph-loader.js.map +1 -0
- package/src/lib/graph-sorter.d.ts +37 -0
- package/src/lib/{graph-sorter.ts → graph-sorter.js} +26 -53
- package/src/lib/graph-sorter.js.map +1 -0
- package/src/lib/graph-visualizer.d.ts +31 -0
- package/src/lib/{graph-visualizer.ts → graph-visualizer.js} +32 -56
- package/src/lib/graph-visualizer.js.map +1 -0
- package/src/lib/package-validator.d.ts +40 -0
- package/src/lib/package-validator.js +175 -0
- package/src/lib/package-validator.js.map +1 -0
- package/src/plugin.d.ts +86 -0
- package/src/{plugin.ts → plugin.js} +100 -255
- package/src/plugin.js.map +1 -0
- package/src/toError.d.ts +5 -0
- package/src/{toError.ts → toError.js} +7 -6
- package/src/toError.js.map +1 -0
- package/LICENSE +0 -373
- package/src/executor-result.ts +0 -7
- package/src/executors/validate-catch-error-pattern/executor.ts +0 -11
- package/src/executors/validate-code/executor.ts +0 -11
- package/src/executors/validate-dtos/executor.ts +0 -11
- package/src/executors/validate-modified-files/executor.ts +0 -11
- package/src/executors/validate-modified-methods/executor.ts +0 -11
- package/src/executors/validate-new-methods/executor.ts +0 -11
- package/src/executors/validate-no-any-unknown/executor.ts +0 -11
- package/src/executors/validate-no-destructure/executor.ts +0 -11
- package/src/executors/validate-no-direct-api-resolver/executor.ts +0 -11
- package/src/executors/validate-no-implicit-any/executor.ts +0 -11
- package/src/executors/validate-no-inline-types/executor.ts +0 -11
- package/src/executors/validate-no-unmanaged-exceptions/executor.ts +0 -11
- package/src/executors/validate-prisma-converters/executor.ts +0 -11
- package/src/executors/validate-return-types/executor.ts +0 -11
- package/src/lib/package-validator.ts +0 -184
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Validate Versions Locked Executor
|
|
3
4
|
*
|
|
@@ -13,72 +14,52 @@
|
|
|
13
14
|
* Usage:
|
|
14
15
|
* nx run architecture:validate-versions-locked
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export interface ValidateVersionsLockedOptions {
|
|
23
|
-
// No options needed
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ExecutorResult {
|
|
27
|
-
success: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.default = runExecutor;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
21
|
+
const path = tslib_1.__importStar(require("path"));
|
|
22
|
+
const toError_1 = require("../../toError");
|
|
30
23
|
// webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
|
|
31
24
|
// Find all package.json files except node_modules, dist, .nx, .angular
|
|
32
|
-
function findPackageJsonFiles(dir
|
|
33
|
-
const files
|
|
25
|
+
function findPackageJsonFiles(dir, basePath = '') {
|
|
26
|
+
const files = [];
|
|
34
27
|
const items = fs.readdirSync(dir);
|
|
35
|
-
|
|
36
28
|
for (const item of items) {
|
|
37
29
|
const fullPath = path.join(dir, item);
|
|
38
30
|
const relativePath = path.join(basePath, item);
|
|
39
|
-
|
|
40
31
|
// Skip these directories
|
|
41
|
-
if (
|
|
42
|
-
['node_modules', 'dist', '.nx', '.angular', 'tmp', '.git'].includes(
|
|
43
|
-
item,
|
|
44
|
-
)
|
|
45
|
-
) {
|
|
32
|
+
if (['node_modules', 'dist', '.nx', '.angular', 'tmp', '.git'].includes(item)) {
|
|
46
33
|
continue;
|
|
47
34
|
}
|
|
48
|
-
|
|
49
35
|
// Skip platform-specific node_modules backups (node_modules_mac, node_modules_linux, etc.)
|
|
50
36
|
if (item.startsWith('node_modules_')) {
|
|
51
37
|
continue;
|
|
52
38
|
}
|
|
53
|
-
|
|
54
39
|
// Skip all hidden directories (starting with .)
|
|
55
40
|
if (item.startsWith('.')) {
|
|
56
41
|
continue;
|
|
57
42
|
}
|
|
58
|
-
|
|
59
43
|
const stat = fs.statSync(fullPath);
|
|
60
44
|
if (stat.isDirectory()) {
|
|
61
45
|
files.push(...findPackageJsonFiles(fullPath, relativePath));
|
|
62
|
-
}
|
|
46
|
+
}
|
|
47
|
+
else if (item === 'package.json') {
|
|
63
48
|
files.push(fullPath);
|
|
64
49
|
}
|
|
65
50
|
}
|
|
66
|
-
|
|
67
51
|
return files;
|
|
68
52
|
}
|
|
69
|
-
|
|
70
53
|
// Check if a version string uses semver ranges
|
|
71
|
-
function hasSemverRange(version
|
|
54
|
+
function hasSemverRange(version) {
|
|
72
55
|
// Allow workspace protocol
|
|
73
56
|
if (version.startsWith('workspace:')) {
|
|
74
57
|
return false;
|
|
75
58
|
}
|
|
76
|
-
|
|
77
59
|
// Allow file: protocol (for local packages)
|
|
78
60
|
if (version.startsWith('file:')) {
|
|
79
61
|
return false;
|
|
80
62
|
}
|
|
81
|
-
|
|
82
63
|
// Check for common semver range patterns
|
|
83
64
|
const semverPatterns = [
|
|
84
65
|
/^\^/, // ^1.2.3
|
|
@@ -95,19 +76,16 @@ function hasSemverRange(version: string): boolean {
|
|
|
95
76
|
/^latest$/, // latest
|
|
96
77
|
/^next$/, // next
|
|
97
78
|
];
|
|
98
|
-
|
|
99
79
|
return semverPatterns.some((pattern) => pattern.test(version));
|
|
100
80
|
}
|
|
101
|
-
|
|
102
81
|
// webpieces-disable max-lines-new-methods -- Existing method from renamed validate-versions file
|
|
103
82
|
// Validate a single package.json file for semver ranges
|
|
104
|
-
function validatePackageJson(filePath
|
|
83
|
+
function validatePackageJson(filePath) {
|
|
105
84
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
106
85
|
try {
|
|
107
86
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
108
87
|
const pkg = JSON.parse(content);
|
|
109
|
-
const errors
|
|
110
|
-
|
|
88
|
+
const errors = [];
|
|
111
89
|
// Check dependencies
|
|
112
90
|
if (pkg.dependencies) {
|
|
113
91
|
for (const [name, version] of Object.entries(pkg.dependencies)) {
|
|
@@ -115,15 +93,11 @@ function validatePackageJson(filePath: string): string[] {
|
|
|
115
93
|
if (name.startsWith('@webpieces/')) {
|
|
116
94
|
continue;
|
|
117
95
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
errors.push(
|
|
121
|
-
`dependencies.${name}: "${version}" uses semver range (must be locked to exact version)`,
|
|
122
|
-
);
|
|
96
|
+
if (hasSemverRange(version)) {
|
|
97
|
+
errors.push(`dependencies.${name}: "${version}" uses semver range (must be locked to exact version)`);
|
|
123
98
|
}
|
|
124
99
|
}
|
|
125
100
|
}
|
|
126
|
-
|
|
127
101
|
// Check devDependencies
|
|
128
102
|
if (pkg.devDependencies) {
|
|
129
103
|
for (const [name, version] of Object.entries(pkg.devDependencies)) {
|
|
@@ -131,134 +105,108 @@ function validatePackageJson(filePath: string): string[] {
|
|
|
131
105
|
if (name.startsWith('@webpieces/')) {
|
|
132
106
|
continue;
|
|
133
107
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
errors.push(
|
|
137
|
-
`devDependencies.${name}: "${version}" uses semver range (must be locked to exact version)`,
|
|
138
|
-
);
|
|
108
|
+
if (hasSemverRange(version)) {
|
|
109
|
+
errors.push(`devDependencies.${name}: "${version}" uses semver range (must be locked to exact version)`);
|
|
139
110
|
}
|
|
140
111
|
}
|
|
141
112
|
}
|
|
142
|
-
|
|
143
113
|
// Check peerDependencies (these can have ranges for compatibility)
|
|
144
114
|
// We don't validate peerDependencies for semver ranges since they're meant to be flexible
|
|
145
|
-
|
|
146
115
|
return errors;
|
|
147
|
-
}
|
|
148
|
-
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const error = (0, toError_1.toError)(err);
|
|
149
119
|
return [`Failed to parse ${filePath}: ${error.message}`];
|
|
150
120
|
}
|
|
151
121
|
}
|
|
152
|
-
|
|
153
|
-
// Track all dependency versions across the monorepo
|
|
154
|
-
interface DependencyUsage {
|
|
155
|
-
version: string;
|
|
156
|
-
file: string;
|
|
157
|
-
type: 'dependencies' | 'devDependencies';
|
|
158
|
-
}
|
|
159
|
-
|
|
160
122
|
// webpieces-disable max-lines-new-methods -- Collecting dependencies from all package.json files
|
|
161
123
|
// Collect all dependency versions from all package.json files
|
|
162
|
-
function collectAllDependencies(workspaceRoot
|
|
163
|
-
const dependencyMap = new Map
|
|
124
|
+
function collectAllDependencies(workspaceRoot) {
|
|
125
|
+
const dependencyMap = new Map();
|
|
164
126
|
const packageFiles = findPackageJsonFiles(workspaceRoot);
|
|
165
|
-
|
|
166
127
|
for (const filePath of packageFiles) {
|
|
167
128
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
168
129
|
try {
|
|
169
130
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
170
131
|
const pkg = JSON.parse(content);
|
|
171
132
|
const relativePath = path.relative(workspaceRoot, filePath);
|
|
172
|
-
|
|
173
133
|
// Collect dependencies
|
|
174
134
|
if (pkg.dependencies) {
|
|
175
135
|
for (const [name, version] of Object.entries(pkg.dependencies)) {
|
|
176
136
|
// Skip internal workspace packages
|
|
177
|
-
if (name.startsWith('@webpieces/'))
|
|
178
|
-
|
|
179
|
-
const usage
|
|
180
|
-
version: version
|
|
137
|
+
if (name.startsWith('@webpieces/'))
|
|
138
|
+
continue;
|
|
139
|
+
const usage = {
|
|
140
|
+
version: version,
|
|
181
141
|
file: relativePath,
|
|
182
142
|
type: 'dependencies'
|
|
183
143
|
};
|
|
184
|
-
|
|
185
144
|
if (!dependencyMap.has(name)) {
|
|
186
145
|
dependencyMap.set(name, []);
|
|
187
146
|
}
|
|
188
|
-
dependencyMap.get(name)
|
|
147
|
+
dependencyMap.get(name).push(usage);
|
|
189
148
|
}
|
|
190
149
|
}
|
|
191
|
-
|
|
192
150
|
// Collect devDependencies
|
|
193
151
|
if (pkg.devDependencies) {
|
|
194
152
|
for (const [name, version] of Object.entries(pkg.devDependencies)) {
|
|
195
153
|
// Skip internal workspace packages
|
|
196
|
-
if (name.startsWith('@webpieces/'))
|
|
197
|
-
|
|
198
|
-
const usage
|
|
199
|
-
version: version
|
|
154
|
+
if (name.startsWith('@webpieces/'))
|
|
155
|
+
continue;
|
|
156
|
+
const usage = {
|
|
157
|
+
version: version,
|
|
200
158
|
file: relativePath,
|
|
201
159
|
type: 'devDependencies'
|
|
202
160
|
};
|
|
203
|
-
|
|
204
161
|
if (!dependencyMap.has(name)) {
|
|
205
162
|
dependencyMap.set(name, []);
|
|
206
163
|
}
|
|
207
|
-
dependencyMap.get(name)
|
|
164
|
+
dependencyMap.get(name).push(usage);
|
|
208
165
|
}
|
|
209
166
|
}
|
|
210
|
-
}
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
211
169
|
// const error = toError(err);
|
|
212
170
|
// Intentionally skip files that can't be parsed - this is expected for some package.json files
|
|
213
171
|
}
|
|
214
172
|
}
|
|
215
|
-
|
|
216
173
|
return dependencyMap;
|
|
217
174
|
}
|
|
218
|
-
|
|
219
175
|
// webpieces-disable max-lines-new-methods -- Simple iteration logic, splitting would reduce clarity
|
|
220
176
|
// Check for version conflicts across package.json files
|
|
221
|
-
function checkVersionConflicts(workspaceRoot
|
|
177
|
+
function checkVersionConflicts(workspaceRoot) {
|
|
222
178
|
console.log('\n🔍 Checking for version conflicts across package.json files:');
|
|
223
|
-
|
|
224
179
|
const dependencyMap = collectAllDependencies(workspaceRoot);
|
|
225
|
-
const conflicts
|
|
226
|
-
|
|
180
|
+
const conflicts = [];
|
|
227
181
|
for (const [packageName, usages] of dependencyMap.entries()) {
|
|
228
182
|
// Get unique versions (ignoring workspace: and file: protocols)
|
|
229
|
-
const versions = new Set(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.filter(v => !v.startsWith('workspace:') && !v.startsWith('file:'))
|
|
233
|
-
);
|
|
234
|
-
|
|
183
|
+
const versions = new Set(usages
|
|
184
|
+
.map(u => u.version)
|
|
185
|
+
.filter(v => !v.startsWith('workspace:') && !v.startsWith('file:')));
|
|
235
186
|
if (versions.size > 1) {
|
|
236
187
|
const conflictDetails = usages
|
|
237
188
|
.filter(u => !u.version.startsWith('workspace:') && !u.version.startsWith('file:'))
|
|
238
189
|
.map(u => ` ${u.file} (${u.type}): ${u.version}`)
|
|
239
190
|
.join('\n');
|
|
240
|
-
|
|
241
191
|
conflicts.push(` ❌ ${packageName} has ${versions.size} different versions:\n${conflictDetails}`);
|
|
242
192
|
}
|
|
243
193
|
}
|
|
244
|
-
|
|
245
194
|
if (conflicts.length === 0) {
|
|
246
195
|
console.log(' ✅ No version conflicts found');
|
|
247
|
-
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
248
198
|
for (const conflict of conflicts) {
|
|
249
199
|
console.log(conflict);
|
|
250
200
|
}
|
|
251
201
|
}
|
|
252
|
-
|
|
253
202
|
return conflicts;
|
|
254
203
|
}
|
|
255
|
-
|
|
256
204
|
/**
|
|
257
205
|
* Prints the educational message explaining why semver ranges are forbidden.
|
|
258
206
|
* This helps developers understand the rationale behind locked versions.
|
|
259
207
|
*/
|
|
260
208
|
// webpieces-disable max-lines-new-methods -- Educational message template, splitting reduces clarity
|
|
261
|
-
function printSemverRangeEducationalMessage(semverErrors
|
|
209
|
+
function printSemverRangeEducationalMessage(semverErrors) {
|
|
262
210
|
console.log(`
|
|
263
211
|
❌ SEMVER RANGES DETECTED - BUILD FAILED
|
|
264
212
|
|
|
@@ -308,61 +256,46 @@ This way, every library change is:
|
|
|
308
256
|
|
|
309
257
|
`);
|
|
310
258
|
}
|
|
311
|
-
|
|
312
|
-
type SemverRangeResult = { errors: number };
|
|
313
|
-
|
|
314
259
|
// Check semver ranges in all package.json files - FAILS if any found
|
|
315
|
-
function checkSemverRanges(workspaceRoot
|
|
260
|
+
function checkSemverRanges(workspaceRoot) {
|
|
316
261
|
console.log('\n📋 Checking for unlocked versions (semver ranges):');
|
|
317
262
|
const packageFiles = findPackageJsonFiles(workspaceRoot);
|
|
318
263
|
let semverErrors = 0;
|
|
319
|
-
|
|
320
264
|
for (const filePath of packageFiles) {
|
|
321
265
|
const relativePath = path.relative(workspaceRoot, filePath);
|
|
322
266
|
const errors = validatePackageJson(filePath);
|
|
323
|
-
|
|
324
267
|
if (errors.length > 0) {
|
|
325
268
|
console.log(` ❌ ${relativePath}:`);
|
|
326
269
|
for (const error of errors) {
|
|
327
270
|
console.log(` ${error}`);
|
|
328
271
|
}
|
|
329
272
|
semverErrors += errors.length;
|
|
330
|
-
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
331
275
|
console.log(` ✅ ${relativePath}`);
|
|
332
276
|
}
|
|
333
277
|
}
|
|
334
|
-
|
|
335
278
|
return { errors: semverErrors };
|
|
336
279
|
}
|
|
337
|
-
|
|
338
|
-
export default async function runExecutor(
|
|
339
|
-
_options: ValidateVersionsLockedOptions,
|
|
340
|
-
context: ExecutorContext
|
|
341
|
-
): Promise<ExecutorResult> {
|
|
280
|
+
async function runExecutor(_options, context) {
|
|
342
281
|
console.log('\n🔒 Validating Package Versions are LOCKED and CONSISTENT\n');
|
|
343
|
-
|
|
344
282
|
const workspaceRoot = context.root;
|
|
345
|
-
|
|
346
283
|
// Step 1: Check for semver ranges (FAILS if any found)
|
|
347
284
|
const semverResult = checkSemverRanges(workspaceRoot);
|
|
348
285
|
const semverErrors = semverResult.errors;
|
|
349
286
|
const packageFiles = findPackageJsonFiles(workspaceRoot);
|
|
350
|
-
|
|
351
287
|
// Step 2: Check for version conflicts across package.json files
|
|
352
288
|
const versionConflicts = checkVersionConflicts(workspaceRoot);
|
|
353
|
-
|
|
354
289
|
// Summary
|
|
355
290
|
console.log(`\n📊 Summary:`);
|
|
356
291
|
console.log(` Files checked: ${packageFiles.length}`);
|
|
357
292
|
console.log(` Unlocked versions (semver ranges): ${semverErrors}`);
|
|
358
293
|
console.log(` Version conflicts: ${versionConflicts.length}`);
|
|
359
|
-
|
|
360
294
|
// Fail on semver ranges with educational message
|
|
361
295
|
if (semverErrors > 0) {
|
|
362
296
|
printSemverRangeEducationalMessage(semverErrors);
|
|
363
297
|
return { success: false };
|
|
364
298
|
}
|
|
365
|
-
|
|
366
299
|
// Fail on version conflicts
|
|
367
300
|
if (versionConflicts.length > 0) {
|
|
368
301
|
console.log('\n❌ VALIDATION FAILED!');
|
|
@@ -370,7 +303,7 @@ export default async function runExecutor(
|
|
|
370
303
|
console.log(' This prevents "works on my machine" bugs where different projects use different library versions.\n');
|
|
371
304
|
return { success: false };
|
|
372
305
|
}
|
|
373
|
-
|
|
374
306
|
console.log('\n✅ VALIDATION PASSED! All versions are locked and consistent.');
|
|
375
307
|
return { success: true };
|
|
376
308
|
}
|
|
309
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/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"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visualize Executor
|
|
3
|
+
*
|
|
4
|
+
* Generates visual representations of the architecture graph (DOT + HTML)
|
|
5
|
+
* and opens the visualization in a browser.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* nx run architecture:visualize
|
|
9
|
+
*/
|
|
10
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
11
|
+
export interface VisualizeExecutorOptions {
|
|
12
|
+
graphPath?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ExecutorResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
}
|
|
17
|
+
export default function runExecutor(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Visualize Executor
|
|
3
4
|
*
|
|
@@ -7,59 +8,44 @@
|
|
|
7
8
|
* Usage:
|
|
8
9
|
* nx run architecture:visualize
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export interface VisualizeExecutorOptions {
|
|
17
|
-
graphPath?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ExecutorResult {
|
|
21
|
-
success: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default async function runExecutor(
|
|
25
|
-
options: VisualizeExecutorOptions,
|
|
26
|
-
context: ExecutorContext
|
|
27
|
-
): Promise<ExecutorResult> {
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.default = runExecutor;
|
|
13
|
+
const graph_loader_1 = require("../../lib/graph-loader");
|
|
14
|
+
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
15
|
+
const toError_1 = require("../../toError");
|
|
16
|
+
async function runExecutor(options, context) {
|
|
28
17
|
const graphPath = options.graphPath;
|
|
29
18
|
const workspaceRoot = context.root;
|
|
30
|
-
|
|
31
19
|
console.log('\n🎨 Architecture Visualization\n');
|
|
32
|
-
|
|
33
20
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
34
21
|
try {
|
|
35
22
|
// Load the saved graph
|
|
36
23
|
console.log('📂 Loading saved graph...');
|
|
37
|
-
const graph = loadBlessedGraph(workspaceRoot, graphPath);
|
|
38
|
-
|
|
24
|
+
const graph = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
39
25
|
if (!graph) {
|
|
40
26
|
console.error('❌ No saved graph found at architecture/dependencies.json');
|
|
41
27
|
console.error(' Run: nx run architecture:generate first');
|
|
42
28
|
return { success: false };
|
|
43
29
|
}
|
|
44
|
-
|
|
45
30
|
// Generate visualization
|
|
46
31
|
console.log('🎨 Generating visualization...');
|
|
47
|
-
const vizPaths = writeVisualization(graph, workspaceRoot);
|
|
32
|
+
const vizPaths = (0, graph_visualizer_1.writeVisualization)(graph, workspaceRoot);
|
|
48
33
|
console.log(`✅ Generated: ${vizPaths.dotPath}`);
|
|
49
34
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
50
|
-
|
|
51
35
|
// Try to open in browser
|
|
52
36
|
console.log('\n🌐 Opening visualization in browser...');
|
|
53
|
-
if (openVisualization(vizPaths.htmlPath)) {
|
|
37
|
+
if ((0, graph_visualizer_1.openVisualization)(vizPaths.htmlPath)) {
|
|
54
38
|
console.log('✅ Browser opened');
|
|
55
|
-
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
56
41
|
console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);
|
|
57
42
|
}
|
|
58
|
-
|
|
59
43
|
return { success: true };
|
|
60
|
-
}
|
|
61
|
-
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const error = (0, toError_1.toError)(err);
|
|
62
47
|
console.error('❌ Visualization failed:', error.message);
|
|
63
48
|
return { success: false };
|
|
64
49
|
}
|
|
65
50
|
}
|
|
51
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAeH,8BAyCC;AArDD,yDAA0D;AAC1D,iEAAmF;AACnF,2CAAwC;AAUzB,KAAK,UAAU,WAAW,CACrC,OAAiC,EACjC,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAEjD,8DAA8D;IAC9D,IAAI,CAAC;QACD,uBAAuB;QACvB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAEzD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAC1E,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,IAAA,qCAAkB,EAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEjD,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,IAAI,IAAA,oCAAiB,EAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,2CAA2C,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACxD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Visualize Executor\n *\n * Generates visual representations of the architecture graph (DOT + HTML)\n * and opens the visualization in a browser.\n *\n * Usage:\n * nx run architecture:visualize\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\nimport { writeVisualization, openVisualization } from '../../lib/graph-visualizer';\nimport { toError } from '../../toError';\n\nexport interface VisualizeExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n options: VisualizeExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n🎨 Architecture Visualization\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Load the saved graph\n console.log('📂 Loading saved graph...');\n const graph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!graph) {\n console.error('❌ No saved graph found at architecture/dependencies.json');\n console.error(' Run: nx run architecture:generate first');\n return { success: false };\n }\n\n // Generate visualization\n console.log('🎨 Generating visualization...');\n const vizPaths = writeVisualization(graph, workspaceRoot);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n\n // Try to open in browser\n console.log('\\n🌐 Opening visualization in browser...');\n if (openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n console.error('❌ Visualization failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -6,4 +6,8 @@ export * from './lib/graph-loader';
|
|
|
6
6
|
export * from './lib/graph-visualizer';
|
|
7
7
|
import { createNodesV2 } from './plugin';
|
|
8
8
|
export { createNodesV2 };
|
|
9
|
-
|
|
9
|
+
declare const _default: {
|
|
10
|
+
name: string;
|
|
11
|
+
createNodesV2: import("@nx/devkit").CreateNodesV2<import("./plugin").ArchitecturePluginOptions>;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodesV2 = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
tslib_1.__exportStar(require("./lib/graph-generator"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./lib/graph-sorter"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./lib/graph-comparator"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./lib/package-validator"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./lib/graph-loader"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./lib/graph-visualizer"), exports);
|
|
11
|
+
const plugin_1 = require("./plugin");
|
|
12
|
+
Object.defineProperty(exports, "createNodesV2", { enumerable: true, get: function () { return plugin_1.createNodesV2; } });
|
|
13
|
+
exports.default = { name: '@webpieces/nx-webpieces-rules', createNodesV2: plugin_1.createNodesV2 };
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AACtC,6DAAmC;AACnC,iEAAuC;AACvC,kEAAwC;AACxC,6DAAmC;AACnC,iEAAuC;AACvC,qCAAyC;AAChC,8FADA,sBAAa,OACA;AACtB,kBAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,aAAa,EAAb,sBAAa,EAAE,CAAC","sourcesContent":["export * from './lib/graph-generator';\nexport * from './lib/graph-sorter';\nexport * from './lib/graph-comparator';\nexport * from './lib/package-validator';\nexport * from './lib/graph-loader';\nexport * from './lib/graph-visualizer';\nimport { createNodesV2 } from './plugin';\nexport { createNodesV2 };\nexport default { name: '@webpieces/nx-webpieces-rules', createNodesV2 };\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Comparator
|
|
3
|
+
*
|
|
4
|
+
* Compares the current generated graph with the saved (blessed) graph.
|
|
5
|
+
* Used in validate mode to ensure developers have updated the graph file.
|
|
6
|
+
*/
|
|
7
|
+
import type { EnhancedGraph } from './graph-sorter';
|
|
8
|
+
/**
|
|
9
|
+
* Difference between two graphs
|
|
10
|
+
*/
|
|
11
|
+
export interface GraphDiff {
|
|
12
|
+
added: string[];
|
|
13
|
+
removed: string[];
|
|
14
|
+
modified: {
|
|
15
|
+
project: string;
|
|
16
|
+
addedDeps: string[];
|
|
17
|
+
removedDeps: string[];
|
|
18
|
+
levelChanged: {
|
|
19
|
+
from: number;
|
|
20
|
+
to: number;
|
|
21
|
+
} | null;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Comparison result
|
|
26
|
+
*/
|
|
27
|
+
export interface ComparisonResult {
|
|
28
|
+
identical: boolean;
|
|
29
|
+
diff: GraphDiff;
|
|
30
|
+
summary: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compare two graphs and return the differences
|
|
34
|
+
*
|
|
35
|
+
* @param current - Currently generated graph
|
|
36
|
+
* @param saved - Previously saved (blessed) graph
|
|
37
|
+
* @returns Comparison result with detailed diff
|
|
38
|
+
*/
|
|
39
|
+
export declare function compareGraphs(current: EnhancedGraph, saved: EnhancedGraph): ComparisonResult;
|