@solid-stack/mason 1.0.32 → 1.0.34
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/dist/bin.cjs +648 -356
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +646 -354
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +5 -4
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +652 -358
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +649 -357
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +10 -2
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +13 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var fs17 = require('fs');
|
|
4
|
+
var path26 = require('path');
|
|
5
5
|
var p19 = require('@clack/prompts');
|
|
6
6
|
var pc14 = require('picocolors');
|
|
7
7
|
var url = require('url');
|
|
@@ -31,8 +31,8 @@ function _interopNamespace(e) {
|
|
|
31
31
|
return Object.freeze(n);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
var
|
|
35
|
-
var
|
|
34
|
+
var fs17__default = /*#__PURE__*/_interopDefault(fs17);
|
|
35
|
+
var path26__default = /*#__PURE__*/_interopDefault(path26);
|
|
36
36
|
var p19__namespace = /*#__PURE__*/_interopNamespace(p19);
|
|
37
37
|
var pc14__default = /*#__PURE__*/_interopDefault(pc14);
|
|
38
38
|
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
@@ -105,8 +105,8 @@ function isValidIdentifier(str) {
|
|
|
105
105
|
}
|
|
106
106
|
function writeFileSafely(absolutePath, content, options = {}) {
|
|
107
107
|
const projectRoot = options.projectRoot || process.cwd();
|
|
108
|
-
const relPath =
|
|
109
|
-
const existedBefore =
|
|
108
|
+
const relPath = path26__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
|
|
109
|
+
const existedBefore = fs17__default.default.existsSync(absolutePath);
|
|
110
110
|
if (options.dryRun) {
|
|
111
111
|
return {
|
|
112
112
|
relativePath: relPath,
|
|
@@ -125,11 +125,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
|
|
|
125
125
|
existedBefore: true
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
|
-
const dir =
|
|
129
|
-
if (!
|
|
130
|
-
|
|
128
|
+
const dir = path26__default.default.dirname(absolutePath);
|
|
129
|
+
if (!fs17__default.default.existsSync(dir)) {
|
|
130
|
+
fs17__default.default.mkdirSync(dir, { recursive: true });
|
|
131
131
|
}
|
|
132
|
-
|
|
132
|
+
fs17__default.default.writeFileSync(absolutePath, content, "utf-8");
|
|
133
133
|
return {
|
|
134
134
|
relativePath: relPath,
|
|
135
135
|
absolutePath,
|
|
@@ -156,17 +156,17 @@ function printGeneratedFiles(files, isDryRun = false) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
function loadMasonConfig(startDir = process.cwd()) {
|
|
159
|
-
let current =
|
|
159
|
+
let current = path26__default.default.resolve(startDir);
|
|
160
160
|
while (true) {
|
|
161
|
-
const configPath =
|
|
162
|
-
if (
|
|
161
|
+
const configPath = path26__default.default.join(current, "mason.config.json");
|
|
162
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
163
163
|
try {
|
|
164
|
-
return JSON.parse(
|
|
164
|
+
return JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
165
165
|
} catch {
|
|
166
166
|
return null;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
const parent =
|
|
169
|
+
const parent = path26__default.default.dirname(current);
|
|
170
170
|
if (parent === current) break;
|
|
171
171
|
current = parent;
|
|
172
172
|
}
|
|
@@ -176,60 +176,91 @@ function getEffectiveMode(projectRoot, flags) {
|
|
|
176
176
|
if (flags?.registry) return "registry";
|
|
177
177
|
const config = loadMasonConfig(projectRoot);
|
|
178
178
|
if (config?.mode) return config.mode;
|
|
179
|
-
if (
|
|
179
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "index.json")) && (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "features")) || fs17__default.default.existsSync(path26__default.default.join(projectRoot, "shared")) || fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/features")) || fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/shared")))) {
|
|
180
180
|
return "registry";
|
|
181
181
|
}
|
|
182
182
|
return "app";
|
|
183
183
|
}
|
|
184
184
|
function getConfiguredFeaturesDir(projectRoot, flags) {
|
|
185
|
-
if (flags?.target) return
|
|
186
|
-
if (flags?.dest) return
|
|
185
|
+
if (flags?.target) return path26__default.default.resolve(projectRoot, flags.target);
|
|
186
|
+
if (flags?.dest) return path26__default.default.resolve(projectRoot, flags.dest);
|
|
187
187
|
const config = loadMasonConfig(projectRoot);
|
|
188
188
|
if (config?.paths?.features) {
|
|
189
|
-
return
|
|
189
|
+
return path26__default.default.resolve(projectRoot, config.paths.features);
|
|
190
190
|
}
|
|
191
191
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
192
192
|
if (mode === "registry") {
|
|
193
|
-
if (
|
|
194
|
-
return
|
|
193
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/features"))) {
|
|
194
|
+
return path26__default.default.join(projectRoot, "src/features");
|
|
195
195
|
}
|
|
196
|
-
return
|
|
196
|
+
return path26__default.default.join(projectRoot, "features");
|
|
197
197
|
}
|
|
198
|
-
if (
|
|
199
|
-
return
|
|
198
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/features"))) {
|
|
199
|
+
return path26__default.default.join(projectRoot, "src/features");
|
|
200
200
|
}
|
|
201
|
-
return
|
|
201
|
+
return path26__default.default.join(projectRoot, "features");
|
|
202
202
|
}
|
|
203
203
|
function getConfiguredSharedDir(projectRoot, flags) {
|
|
204
|
-
if (flags?.target) return
|
|
205
|
-
if (flags?.dest) return
|
|
204
|
+
if (flags?.target) return path26__default.default.resolve(projectRoot, flags.target);
|
|
205
|
+
if (flags?.dest) return path26__default.default.resolve(projectRoot, flags.dest);
|
|
206
206
|
const config = loadMasonConfig(projectRoot);
|
|
207
207
|
if (config?.paths?.shared) {
|
|
208
|
-
return
|
|
208
|
+
return path26__default.default.resolve(projectRoot, config.paths.shared);
|
|
209
209
|
}
|
|
210
210
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
211
211
|
if (mode === "registry") {
|
|
212
|
-
if (
|
|
213
|
-
return
|
|
212
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/shared"))) {
|
|
213
|
+
return path26__default.default.join(projectRoot, "src/shared");
|
|
214
214
|
}
|
|
215
|
-
return
|
|
215
|
+
return path26__default.default.join(projectRoot, "shared");
|
|
216
216
|
}
|
|
217
|
-
if (
|
|
218
|
-
return
|
|
217
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/shared"))) {
|
|
218
|
+
return path26__default.default.join(projectRoot, "src/shared");
|
|
219
219
|
}
|
|
220
|
-
return
|
|
220
|
+
return path26__default.default.join(projectRoot, "shared");
|
|
221
|
+
}
|
|
222
|
+
function loadLintConfig(projectRoot) {
|
|
223
|
+
const configPath = path26__default.default.join(projectRoot, "mason-lint.config.json");
|
|
224
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
225
|
+
try {
|
|
226
|
+
return JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
function getResolvedLintPaths(projectRoot, config) {
|
|
233
|
+
const masonConfig = loadMasonConfig(projectRoot);
|
|
234
|
+
const configuredFeatures = config.paths?.features ?? masonConfig?.paths?.features;
|
|
235
|
+
const configuredShared = config.paths?.shared ?? masonConfig?.paths?.shared;
|
|
236
|
+
const configuredErrors = config.paths?.errors;
|
|
237
|
+
const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
|
|
238
|
+
if (configuredPath !== void 0) {
|
|
239
|
+
return path26__default.default.resolve(projectRoot, configuredPath);
|
|
240
|
+
}
|
|
241
|
+
const srcPath = path26__default.default.join(projectRoot, defaultSrc);
|
|
242
|
+
if (fs17__default.default.existsSync(srcPath)) return srcPath;
|
|
243
|
+
const rootPath = path26__default.default.join(projectRoot, defaultRoot);
|
|
244
|
+
if (fs17__default.default.existsSync(rootPath)) return rootPath;
|
|
245
|
+
return srcPath;
|
|
246
|
+
};
|
|
247
|
+
return {
|
|
248
|
+
featuresBase: resolveBase(configuredFeatures, "src/features", "features"),
|
|
249
|
+
sharedBase: resolveBase(configuredShared, "src/shared", "shared"),
|
|
250
|
+
errorsBase: resolveBase(configuredErrors, "src/errors", "errors")
|
|
251
|
+
};
|
|
221
252
|
}
|
|
222
253
|
|
|
223
254
|
// src/utils/project.ts
|
|
224
255
|
function findProjectRoot(startDir = process.cwd()) {
|
|
225
|
-
let current =
|
|
256
|
+
let current = path26__default.default.resolve(startDir);
|
|
226
257
|
while (true) {
|
|
227
|
-
if (
|
|
258
|
+
if (fs17__default.default.existsSync(path26__default.default.join(current, "package.json"))) {
|
|
228
259
|
return current;
|
|
229
260
|
}
|
|
230
|
-
const parent =
|
|
261
|
+
const parent = path26__default.default.dirname(current);
|
|
231
262
|
if (parent === current) {
|
|
232
|
-
return
|
|
263
|
+
return path26__default.default.resolve(startDir);
|
|
233
264
|
}
|
|
234
265
|
current = parent;
|
|
235
266
|
}
|
|
@@ -242,9 +273,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
242
273
|
}
|
|
243
274
|
function listExistingShared(projectRoot) {
|
|
244
275
|
const sharedDir = resolveSharedDir(projectRoot);
|
|
245
|
-
if (!
|
|
276
|
+
if (!fs17__default.default.existsSync(sharedDir)) return [];
|
|
246
277
|
try {
|
|
247
|
-
const entries =
|
|
278
|
+
const entries = fs17__default.default.readdirSync(sharedDir, { withFileTypes: true });
|
|
248
279
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
249
280
|
} catch {
|
|
250
281
|
return [];
|
|
@@ -252,9 +283,9 @@ function listExistingShared(projectRoot) {
|
|
|
252
283
|
}
|
|
253
284
|
function listExistingFeatures(projectRoot) {
|
|
254
285
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
255
|
-
if (!
|
|
286
|
+
if (!fs17__default.default.existsSync(featuresDir)) return [];
|
|
256
287
|
try {
|
|
257
|
-
const entries =
|
|
288
|
+
const entries = fs17__default.default.readdirSync(featuresDir, { withFileTypes: true });
|
|
258
289
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
259
290
|
} catch {
|
|
260
291
|
return [];
|
|
@@ -264,14 +295,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
264
295
|
const results = [];
|
|
265
296
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
266
297
|
for (const feat of features) {
|
|
267
|
-
const domainDir =
|
|
268
|
-
if (!
|
|
298
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
299
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
269
300
|
try {
|
|
270
|
-
const files =
|
|
301
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
271
302
|
for (const file of files) {
|
|
272
303
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
273
304
|
const name = file.replace(/\.ts$/, "");
|
|
274
|
-
const fullPath =
|
|
305
|
+
const fullPath = path26__default.default.join(domainDir, file);
|
|
275
306
|
if (/^I[A-Z]/.test(name)) {
|
|
276
307
|
results.push({
|
|
277
308
|
name,
|
|
@@ -280,7 +311,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
280
311
|
});
|
|
281
312
|
} else {
|
|
282
313
|
try {
|
|
283
|
-
const content =
|
|
314
|
+
const content = fs17__default.default.readFileSync(fullPath, "utf-8");
|
|
284
315
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
285
316
|
results.push({
|
|
286
317
|
name,
|
|
@@ -302,17 +333,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
302
333
|
const results = [];
|
|
303
334
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
304
335
|
for (const feat of features) {
|
|
305
|
-
const domainDir =
|
|
306
|
-
if (!
|
|
336
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
337
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
307
338
|
try {
|
|
308
|
-
const files =
|
|
339
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
309
340
|
for (const file of files) {
|
|
310
341
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
311
342
|
const name = file.replace(/\.ts$/, "");
|
|
312
343
|
if (!/^I[A-Z]/.test(name)) {
|
|
313
344
|
results.push({
|
|
314
345
|
name,
|
|
315
|
-
fullPath:
|
|
346
|
+
fullPath: path26__default.default.join(domainDir, file),
|
|
316
347
|
feature: feat
|
|
317
348
|
});
|
|
318
349
|
}
|
|
@@ -327,16 +358,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
327
358
|
const results = [];
|
|
328
359
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
329
360
|
for (const feat of features) {
|
|
330
|
-
const useCasesDir =
|
|
331
|
-
if (!
|
|
361
|
+
const useCasesDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
362
|
+
if (!fs17__default.default.existsSync(useCasesDir)) continue;
|
|
332
363
|
try {
|
|
333
|
-
const files =
|
|
364
|
+
const files = fs17__default.default.readdirSync(useCasesDir);
|
|
334
365
|
for (const file of files) {
|
|
335
366
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
336
367
|
const name = file.replace(/\.ts$/, "");
|
|
337
368
|
results.push({
|
|
338
369
|
name,
|
|
339
|
-
fullPath:
|
|
370
|
+
fullPath: path26__default.default.join(useCasesDir, file),
|
|
340
371
|
feature: feat
|
|
341
372
|
});
|
|
342
373
|
}
|
|
@@ -448,25 +479,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
448
479
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
449
480
|
}
|
|
450
481
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
451
|
-
return
|
|
482
|
+
return path26__default.default.resolve(baseDir, trimmed);
|
|
452
483
|
}
|
|
453
484
|
return trimmed;
|
|
454
485
|
}
|
|
455
486
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
456
|
-
let current =
|
|
487
|
+
let current = path26__default.default.resolve(startDir);
|
|
457
488
|
while (true) {
|
|
458
|
-
const candidate =
|
|
459
|
-
if (
|
|
489
|
+
const candidate = path26__default.default.join(current, "mason-registry");
|
|
490
|
+
if (fs17__default.default.existsSync(candidate) && fs17__default.default.existsSync(path26__default.default.join(candidate, "index.json"))) {
|
|
460
491
|
return candidate;
|
|
461
492
|
}
|
|
462
|
-
const parent =
|
|
493
|
+
const parent = path26__default.default.dirname(current);
|
|
463
494
|
if (parent === current) break;
|
|
464
495
|
current = parent;
|
|
465
496
|
}
|
|
466
497
|
return null;
|
|
467
498
|
}
|
|
468
499
|
function getRegistryBase(projectRoot, override) {
|
|
469
|
-
const rootDir = projectRoot ?
|
|
500
|
+
const rootDir = projectRoot ? path26__default.default.resolve(projectRoot) : process.cwd();
|
|
470
501
|
if (override && override.trim()) {
|
|
471
502
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
472
503
|
}
|
|
@@ -474,30 +505,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
474
505
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
475
506
|
}
|
|
476
507
|
const configLocations = [
|
|
477
|
-
|
|
478
|
-
|
|
508
|
+
path26__default.default.join(rootDir, "mason.config.json"),
|
|
509
|
+
path26__default.default.join(process.cwd(), "mason.config.json")
|
|
479
510
|
];
|
|
480
511
|
for (const configPath of configLocations) {
|
|
481
|
-
if (
|
|
512
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
482
513
|
try {
|
|
483
|
-
const config = JSON.parse(
|
|
514
|
+
const config = JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
484
515
|
if (config.registry && typeof config.registry === "string") {
|
|
485
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
516
|
+
return normalizeRegistryUrl(config.registry.trim(), path26__default.default.dirname(configPath));
|
|
486
517
|
}
|
|
487
518
|
} catch {
|
|
488
519
|
}
|
|
489
520
|
}
|
|
490
521
|
}
|
|
491
522
|
const pkgLocations = [
|
|
492
|
-
|
|
493
|
-
|
|
523
|
+
path26__default.default.join(rootDir, "package.json"),
|
|
524
|
+
path26__default.default.join(process.cwd(), "package.json")
|
|
494
525
|
];
|
|
495
526
|
for (const pkgPath of pkgLocations) {
|
|
496
|
-
if (
|
|
527
|
+
if (fs17__default.default.existsSync(pkgPath)) {
|
|
497
528
|
try {
|
|
498
|
-
const pkg = JSON.parse(
|
|
529
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgPath, "utf-8"));
|
|
499
530
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
500
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
531
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26__default.default.dirname(pkgPath));
|
|
501
532
|
}
|
|
502
533
|
} catch {
|
|
503
534
|
}
|
|
@@ -545,16 +576,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
545
576
|
if (baseDir.startsWith("file://")) {
|
|
546
577
|
baseDir = url.fileURLToPath(baseDir);
|
|
547
578
|
}
|
|
548
|
-
let filePath =
|
|
549
|
-
if (!
|
|
550
|
-
const inSrcPath =
|
|
551
|
-
if (
|
|
579
|
+
let filePath = path26__default.default.resolve(baseDir, cleanRelPath);
|
|
580
|
+
if (!fs17__default.default.existsSync(filePath)) {
|
|
581
|
+
const inSrcPath = path26__default.default.resolve(baseDir, "src", cleanRelPath);
|
|
582
|
+
if (fs17__default.default.existsSync(inSrcPath)) {
|
|
552
583
|
filePath = inSrcPath;
|
|
553
584
|
} else {
|
|
554
585
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
555
586
|
}
|
|
556
587
|
}
|
|
557
|
-
return
|
|
588
|
+
return fs17__default.default.promises.readFile(filePath, "utf-8");
|
|
558
589
|
}
|
|
559
590
|
async function fetchRegistryIndex(registryBase) {
|
|
560
591
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -616,19 +647,19 @@ function findClosestMatch(target, candidates) {
|
|
|
616
647
|
return null;
|
|
617
648
|
}
|
|
618
649
|
function detectPackageManager(projectRoot) {
|
|
619
|
-
if (
|
|
620
|
-
if (
|
|
621
|
-
if (
|
|
650
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
651
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
652
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "bun.lockb")) || fs17__default.default.existsSync(path26__default.default.join(projectRoot, "bun.lock"))) return "bun";
|
|
622
653
|
return "npm";
|
|
623
654
|
}
|
|
624
655
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
625
656
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
626
|
-
const pkgJsonPath =
|
|
627
|
-
if (!
|
|
657
|
+
const pkgJsonPath = path26__default.default.join(projectRoot, "package.json");
|
|
658
|
+
if (!fs17__default.default.existsSync(pkgJsonPath)) {
|
|
628
659
|
return requiredPackages;
|
|
629
660
|
}
|
|
630
661
|
try {
|
|
631
|
-
const pkg = JSON.parse(
|
|
662
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgJsonPath, "utf-8"));
|
|
632
663
|
const installed = /* @__PURE__ */ new Set([
|
|
633
664
|
...Object.keys(pkg.dependencies || {}),
|
|
634
665
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -667,12 +698,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
667
698
|
});
|
|
668
699
|
}
|
|
669
700
|
function getAllFiles(dir, baseDir = dir) {
|
|
670
|
-
if (!
|
|
671
|
-
const entries =
|
|
701
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
702
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
672
703
|
const files = [];
|
|
673
704
|
for (const entry of entries) {
|
|
674
|
-
const fullPath =
|
|
675
|
-
const relPath =
|
|
705
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
706
|
+
const relPath = path26__default.default.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
676
707
|
if (entry.isDirectory()) {
|
|
677
708
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
678
709
|
continue;
|
|
@@ -688,13 +719,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
688
719
|
return files.sort();
|
|
689
720
|
}
|
|
690
721
|
function computeDirectoryHash(dirPath, files) {
|
|
691
|
-
if (!
|
|
722
|
+
if (!fs17__default.default.existsSync(dirPath)) return "";
|
|
692
723
|
const hash = crypto.createHash("sha256");
|
|
693
724
|
const fileList = (files ?? getAllFiles(dirPath)).sort();
|
|
694
725
|
for (const relativePath of fileList) {
|
|
695
|
-
const absolutePath =
|
|
696
|
-
if (!
|
|
697
|
-
const content =
|
|
726
|
+
const absolutePath = path26__default.default.join(dirPath, relativePath);
|
|
727
|
+
if (!fs17__default.default.existsSync(absolutePath)) continue;
|
|
728
|
+
const content = fs17__default.default.readFileSync(absolutePath, "utf8");
|
|
698
729
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
699
730
|
hash.update(relativePath);
|
|
700
731
|
hash.update(normalizedContent);
|
|
@@ -717,33 +748,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
717
748
|
});
|
|
718
749
|
var cachedTemplatesDir = null;
|
|
719
750
|
function getTemplatesDir() {
|
|
720
|
-
if (cachedTemplatesDir &&
|
|
751
|
+
if (cachedTemplatesDir && fs17__default.default.existsSync(cachedTemplatesDir)) {
|
|
721
752
|
return cachedTemplatesDir;
|
|
722
753
|
}
|
|
723
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
724
|
-
const cand1 =
|
|
725
|
-
if (
|
|
754
|
+
const currentDir = typeof __dirname !== "undefined" ? __dirname : path26__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
755
|
+
const cand1 = path26__default.default.resolve(currentDir, "../../templates");
|
|
756
|
+
if (fs17__default.default.existsSync(cand1)) {
|
|
726
757
|
cachedTemplatesDir = cand1;
|
|
727
758
|
return cand1;
|
|
728
759
|
}
|
|
729
|
-
const cand2 =
|
|
730
|
-
if (
|
|
760
|
+
const cand2 = path26__default.default.resolve(currentDir, "../templates");
|
|
761
|
+
if (fs17__default.default.existsSync(cand2)) {
|
|
731
762
|
cachedTemplatesDir = cand2;
|
|
732
763
|
return cand2;
|
|
733
764
|
}
|
|
734
|
-
const cand3 =
|
|
735
|
-
if (
|
|
765
|
+
const cand3 = path26__default.default.resolve(currentDir, "templates");
|
|
766
|
+
if (fs17__default.default.existsSync(cand3)) {
|
|
736
767
|
cachedTemplatesDir = cand3;
|
|
737
768
|
return cand3;
|
|
738
769
|
}
|
|
739
770
|
let curr = currentDir;
|
|
740
771
|
while (true) {
|
|
741
|
-
const candTemplates =
|
|
742
|
-
if (
|
|
772
|
+
const candTemplates = path26__default.default.join(curr, "templates");
|
|
773
|
+
if (fs17__default.default.existsSync(candTemplates)) {
|
|
743
774
|
cachedTemplatesDir = candTemplates;
|
|
744
775
|
return candTemplates;
|
|
745
776
|
}
|
|
746
|
-
const parent =
|
|
777
|
+
const parent = path26__default.default.dirname(curr);
|
|
747
778
|
if (parent === curr) break;
|
|
748
779
|
curr = parent;
|
|
749
780
|
}
|
|
@@ -751,8 +782,8 @@ function getTemplatesDir() {
|
|
|
751
782
|
}
|
|
752
783
|
var templateCache = /* @__PURE__ */ new Map();
|
|
753
784
|
function loadTemplate(relativePath) {
|
|
754
|
-
const fullPath =
|
|
755
|
-
return
|
|
785
|
+
const fullPath = path26__default.default.isAbsolute(relativePath) ? relativePath : path26__default.default.join(getTemplatesDir(), relativePath);
|
|
786
|
+
return fs17__default.default.readFileSync(fullPath, "utf-8");
|
|
756
787
|
}
|
|
757
788
|
function renderTemplateFile(relativePath, context) {
|
|
758
789
|
let compiled = templateCache.get(relativePath);
|
|
@@ -837,8 +868,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
837
868
|
function generateDomain(options) {
|
|
838
869
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
839
870
|
const entityName = toPascalCase(options.name);
|
|
840
|
-
const targetDir = options.targetDir ||
|
|
841
|
-
const filePath =
|
|
871
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
872
|
+
const filePath = path26__default.default.join(targetDir, `${entityName}.ts`);
|
|
842
873
|
const code = generateDomainEntityCode(
|
|
843
874
|
entityName,
|
|
844
875
|
options.kind || "interface",
|
|
@@ -887,8 +918,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
887
918
|
function generateInterface(options) {
|
|
888
919
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
889
920
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
890
|
-
const targetDir = options.targetDir ||
|
|
891
|
-
const filePath =
|
|
921
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
922
|
+
const filePath = path26__default.default.join(targetDir, `${ifaceName}.ts`);
|
|
892
923
|
const code = generateInterfaceCode(
|
|
893
924
|
ifaceName,
|
|
894
925
|
options.kind || "abstractClass",
|
|
@@ -1123,7 +1154,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
1123
1154
|
};
|
|
1124
1155
|
}
|
|
1125
1156
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
1126
|
-
if (customPath &&
|
|
1157
|
+
if (customPath && fs17__default.default.existsSync(customPath)) {
|
|
1127
1158
|
return { filePath: customPath, ifaceName };
|
|
1128
1159
|
}
|
|
1129
1160
|
const candidateNames = [
|
|
@@ -1133,22 +1164,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
1133
1164
|
];
|
|
1134
1165
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
1135
1166
|
for (const feat of features) {
|
|
1136
|
-
const domainDir =
|
|
1137
|
-
if (!
|
|
1167
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
1168
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
1138
1169
|
for (const cand of candidateNames) {
|
|
1139
|
-
const p1 =
|
|
1140
|
-
if (
|
|
1170
|
+
const p1 = path26__default.default.join(domainDir, `${cand}.ts`);
|
|
1171
|
+
if (fs17__default.default.existsSync(p1)) {
|
|
1141
1172
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
1142
1173
|
}
|
|
1143
1174
|
}
|
|
1144
1175
|
}
|
|
1145
1176
|
for (const cand of candidateNames) {
|
|
1146
|
-
const p1 =
|
|
1147
|
-
if (
|
|
1148
|
-
const p22 =
|
|
1149
|
-
if (
|
|
1150
|
-
const p32 =
|
|
1151
|
-
if (
|
|
1177
|
+
const p1 = path26__default.default.join(projectRoot, "domain", `${cand}.ts`);
|
|
1178
|
+
if (fs17__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
|
|
1179
|
+
const p22 = path26__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
|
|
1180
|
+
if (fs17__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
|
|
1181
|
+
const p32 = path26__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
|
|
1182
|
+
if (fs17__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
|
|
1152
1183
|
}
|
|
1153
1184
|
return null;
|
|
1154
1185
|
}
|
|
@@ -1171,7 +1202,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
1171
1202
|
while (currentFile && !visited.has(currentFile)) {
|
|
1172
1203
|
visited.add(currentFile);
|
|
1173
1204
|
try {
|
|
1174
|
-
const content =
|
|
1205
|
+
const content = fs17__default.default.readFileSync(currentFile, "utf-8");
|
|
1175
1206
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
1176
1207
|
for (const m of parsed.methods) {
|
|
1177
1208
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -1247,7 +1278,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
1247
1278
|
const entitiesVar = `${entityVar}s`;
|
|
1248
1279
|
const idKey = `${entityVar}Id`;
|
|
1249
1280
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
1250
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
1281
|
+
const entityImportPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
1251
1282
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
1252
1283
|
for (const imp of domainImports) {
|
|
1253
1284
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -1296,7 +1327,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
1296
1327
|
if (baseEntity) {
|
|
1297
1328
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
1298
1329
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
1299
|
-
const importPath = ifaceImportPath ? `${
|
|
1330
|
+
const importPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
1300
1331
|
finalDomainImports.push({
|
|
1301
1332
|
name: baseEntity,
|
|
1302
1333
|
from: importPath,
|
|
@@ -1341,8 +1372,8 @@ function generateInfrastructure(options) {
|
|
|
1341
1372
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1342
1373
|
const className = toPascalCase(options.name);
|
|
1343
1374
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
1344
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
1345
|
-
const filePath =
|
|
1375
|
+
const targetDir = options.targetDir || (isFeatureScope ? path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path26__default.default.join(projectRoot, "infrastructure"));
|
|
1376
|
+
const filePath = path26__default.default.join(targetDir, `${className}.ts`);
|
|
1346
1377
|
let code;
|
|
1347
1378
|
if (options.implementsInterface) {
|
|
1348
1379
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -1356,7 +1387,7 @@ function generateInfrastructure(options) {
|
|
|
1356
1387
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
1357
1388
|
let ifaceImportPath;
|
|
1358
1389
|
if (ifaceFilePath) {
|
|
1359
|
-
let rel =
|
|
1390
|
+
let rel = path26__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
1360
1391
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1361
1392
|
ifaceImportPath = rel;
|
|
1362
1393
|
} else if (isFeatureScope) {
|
|
@@ -1369,8 +1400,8 @@ function generateInfrastructure(options) {
|
|
|
1369
1400
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
1370
1401
|
let fromPath;
|
|
1371
1402
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
1372
|
-
const absTarget =
|
|
1373
|
-
let rel =
|
|
1403
|
+
const absTarget = path26__default.default.resolve(path26__default.default.dirname(ifaceFilePath), imp.specifier);
|
|
1404
|
+
let rel = path26__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
|
|
1374
1405
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1375
1406
|
fromPath = rel;
|
|
1376
1407
|
} else {
|
|
@@ -1557,8 +1588,8 @@ function generateUseCase(options) {
|
|
|
1557
1588
|
const files = [];
|
|
1558
1589
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1559
1590
|
const ucName = toPascalCase(options.name);
|
|
1560
|
-
const targetDir = options.targetDir ||
|
|
1561
|
-
const filePath =
|
|
1591
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
|
|
1592
|
+
const filePath = path26__default.default.join(targetDir, `${ucName}.ts`);
|
|
1562
1593
|
const code = generateUseCaseCode(
|
|
1563
1594
|
ucName,
|
|
1564
1595
|
options.dependencies || [],
|
|
@@ -1574,7 +1605,7 @@ function generateUseCase(options) {
|
|
|
1574
1605
|
})
|
|
1575
1606
|
);
|
|
1576
1607
|
if (options.includeTest) {
|
|
1577
|
-
const testPath =
|
|
1608
|
+
const testPath = path26__default.default.join(targetDir, `${ucName}.test.ts`);
|
|
1578
1609
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1579
1610
|
files.push(
|
|
1580
1611
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1642,10 +1673,10 @@ function generateHttpHandler(options) {
|
|
|
1642
1673
|
const files = [];
|
|
1643
1674
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1644
1675
|
const className = toPascalCase(options.name);
|
|
1645
|
-
const pxDir =
|
|
1646
|
-
const handlersDir =
|
|
1647
|
-
const pxIndexPath =
|
|
1648
|
-
if (!
|
|
1676
|
+
const pxDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
|
|
1677
|
+
const handlersDir = path26__default.default.join(pxDir, "handlers");
|
|
1678
|
+
const pxIndexPath = path26__default.default.join(pxDir, "index.ts");
|
|
1679
|
+
if (!fs17__default.default.existsSync(pxIndexPath)) {
|
|
1649
1680
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1650
1681
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1651
1682
|
routePath: featurePath
|
|
@@ -1658,7 +1689,7 @@ function generateHttpHandler(options) {
|
|
|
1658
1689
|
})
|
|
1659
1690
|
);
|
|
1660
1691
|
}
|
|
1661
|
-
const handlerFilePath =
|
|
1692
|
+
const handlerFilePath = path26__default.default.join(handlersDir, `${className}.ts`);
|
|
1662
1693
|
const code = generateHttpHandlerCode(
|
|
1663
1694
|
className,
|
|
1664
1695
|
options.method || "get",
|
|
@@ -1679,11 +1710,11 @@ function generateHttpHandler(options) {
|
|
|
1679
1710
|
return files;
|
|
1680
1711
|
}
|
|
1681
1712
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1682
|
-
const providerPath =
|
|
1713
|
+
const providerPath = path26__default.default.join(featureDir, "diProvider.ts");
|
|
1683
1714
|
const pascalFeature = toPascalCase(featureName);
|
|
1684
1715
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1685
|
-
if (
|
|
1686
|
-
let content =
|
|
1716
|
+
if (fs17__default.default.existsSync(providerPath) && !options.overwrite) {
|
|
1717
|
+
let content = fs17__default.default.readFileSync(providerPath, "utf-8");
|
|
1687
1718
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1688
1719
|
for (const b of bindings) {
|
|
1689
1720
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1742,7 +1773,7 @@ function generateFeature(options) {
|
|
|
1742
1773
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1743
1774
|
const rawName = options.name.trim();
|
|
1744
1775
|
const featureName = toCamelCase(rawName);
|
|
1745
|
-
const targetDir = options.targetDir ||
|
|
1776
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1746
1777
|
const routePath = options.routePath || `/${featureName}`;
|
|
1747
1778
|
const rawEntities = [];
|
|
1748
1779
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1763,7 +1794,7 @@ function generateFeature(options) {
|
|
|
1763
1794
|
const domainFile = generateDomain({
|
|
1764
1795
|
name: ent,
|
|
1765
1796
|
feature: featureName,
|
|
1766
|
-
targetDir:
|
|
1797
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1767
1798
|
projectRoot,
|
|
1768
1799
|
dryRun: options.dryRun,
|
|
1769
1800
|
overwrite: options.overwrite,
|
|
@@ -1783,7 +1814,7 @@ function generateFeature(options) {
|
|
|
1783
1814
|
const ifaceFile = generateInterface({
|
|
1784
1815
|
name: ifaceName,
|
|
1785
1816
|
feature: featureName,
|
|
1786
|
-
targetDir:
|
|
1817
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1787
1818
|
entityName: ent,
|
|
1788
1819
|
projectRoot,
|
|
1789
1820
|
dryRun: options.dryRun,
|
|
@@ -1800,7 +1831,7 @@ function generateFeature(options) {
|
|
|
1800
1831
|
const stubFile = generateInfrastructure({
|
|
1801
1832
|
name: stubName,
|
|
1802
1833
|
feature: featureName,
|
|
1803
|
-
targetDir:
|
|
1834
|
+
targetDir: path26__default.default.join(targetDir, "infrastructure"),
|
|
1804
1835
|
kind: "stub",
|
|
1805
1836
|
implementsInterface: ifaceName,
|
|
1806
1837
|
entityName: ent,
|
|
@@ -1842,7 +1873,7 @@ function generateFeature(options) {
|
|
|
1842
1873
|
const ucFiles = generateUseCase({
|
|
1843
1874
|
name: ucName,
|
|
1844
1875
|
feature: featureName,
|
|
1845
|
-
targetDir:
|
|
1876
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1846
1877
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1847
1878
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1848
1879
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1887,7 +1918,7 @@ function generateFeature(options) {
|
|
|
1887
1918
|
const ucFiles = generateUseCase({
|
|
1888
1919
|
name: ucName,
|
|
1889
1920
|
feature: featureName,
|
|
1890
|
-
targetDir:
|
|
1921
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1891
1922
|
dependencies: deps,
|
|
1892
1923
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1893
1924
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1920,7 +1951,7 @@ function generateFeature(options) {
|
|
|
1920
1951
|
const ucFiles = generateUseCase({
|
|
1921
1952
|
name: ucName,
|
|
1922
1953
|
feature: featureName,
|
|
1923
|
-
targetDir:
|
|
1954
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1924
1955
|
dependencies: deps,
|
|
1925
1956
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1926
1957
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1953,7 +1984,7 @@ function generateFeature(options) {
|
|
|
1953
1984
|
const ucFiles = generateUseCase({
|
|
1954
1985
|
name: ucName,
|
|
1955
1986
|
feature: featureName,
|
|
1956
|
-
targetDir:
|
|
1987
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1957
1988
|
dependencies: deps,
|
|
1958
1989
|
inputProperties: [
|
|
1959
1990
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1997,7 +2028,7 @@ function generateFeature(options) {
|
|
|
1997
2028
|
const ucFiles = generateUseCase({
|
|
1998
2029
|
name: ucName,
|
|
1999
2030
|
feature: featureName,
|
|
2000
|
-
targetDir:
|
|
2031
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
2001
2032
|
dependencies: deps,
|
|
2002
2033
|
inputProperties: [{ name: entVar, type: ent }],
|
|
2003
2034
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -2030,7 +2061,7 @@ function generateFeature(options) {
|
|
|
2030
2061
|
const ucFiles = generateUseCase({
|
|
2031
2062
|
name: ucName,
|
|
2032
2063
|
feature: featureName,
|
|
2033
|
-
targetDir:
|
|
2064
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
2034
2065
|
dependencies: deps,
|
|
2035
2066
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
2036
2067
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -2067,7 +2098,7 @@ function generateFeature(options) {
|
|
|
2067
2098
|
const ucFiles = generateUseCase({
|
|
2068
2099
|
name: ucName,
|
|
2069
2100
|
feature: featureName,
|
|
2070
|
-
targetDir:
|
|
2101
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
2071
2102
|
dependencies: customUc.dependencies || [],
|
|
2072
2103
|
inputProperties: customUc.inputProperties || [],
|
|
2073
2104
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -2099,8 +2130,8 @@ function generateFeature(options) {
|
|
|
2099
2130
|
}
|
|
2100
2131
|
}
|
|
2101
2132
|
if (options.includeExpress !== false) {
|
|
2102
|
-
const pxDir =
|
|
2103
|
-
const pxIndexPath =
|
|
2133
|
+
const pxDir = path26__default.default.join(targetDir, "pxExpress");
|
|
2134
|
+
const pxIndexPath = path26__default.default.join(pxDir, "index.ts");
|
|
2104
2135
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
2105
2136
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
2106
2137
|
files.push(
|
|
@@ -2145,7 +2176,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2145
2176
|
}
|
|
2146
2177
|
const normalizedName = toCamelCase(name);
|
|
2147
2178
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
2148
|
-
const defaultDir =
|
|
2179
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseFeaturesDir, normalizedName));
|
|
2149
2180
|
let targetDirRel = defaultDir;
|
|
2150
2181
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2151
2182
|
targetDirRel = handleCancel(
|
|
@@ -2156,7 +2187,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2156
2187
|
})
|
|
2157
2188
|
).trim();
|
|
2158
2189
|
}
|
|
2159
|
-
const targetDir = flags.dest ?
|
|
2190
|
+
const targetDir = flags.dest ? path26__default.default.resolve(projectRoot, flags.dest) : flags.target ? path26__default.default.resolve(projectRoot, flags.target) : path26__default.default.resolve(projectRoot, targetDirRel);
|
|
2160
2191
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
2161
2192
|
let routePath = `/${normalizedName}`;
|
|
2162
2193
|
let includeDomain = true;
|
|
@@ -2398,8 +2429,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2398
2429
|
};
|
|
2399
2430
|
const files = generateFeature(options);
|
|
2400
2431
|
if (isRegistry) {
|
|
2401
|
-
const registryPath =
|
|
2402
|
-
if (!
|
|
2432
|
+
const registryPath = path26__default.default.join(targetDir, "registry.json");
|
|
2433
|
+
if (!fs17__default.default.existsSync(registryPath)) {
|
|
2403
2434
|
const manifest = {
|
|
2404
2435
|
name: toKebabCase(normalizedName),
|
|
2405
2436
|
type: "feature",
|
|
@@ -2425,7 +2456,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2425
2456
|
printGeneratedFiles(files, options.dryRun);
|
|
2426
2457
|
p19__namespace.outro(
|
|
2427
2458
|
pc14__default.default.green(
|
|
2428
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2459
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2429
2460
|
)
|
|
2430
2461
|
);
|
|
2431
2462
|
}
|
|
@@ -2436,7 +2467,7 @@ function generateShared(options) {
|
|
|
2436
2467
|
const pascalName = toPascalCase(rawName);
|
|
2437
2468
|
const camelName = toCamelCase(rawName);
|
|
2438
2469
|
const kebabName = toKebabCase(rawName);
|
|
2439
|
-
const targetDir = options.targetDir ||
|
|
2470
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveSharedDir(projectRoot), kebabName);
|
|
2440
2471
|
const fileDefs = [];
|
|
2441
2472
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2442
2473
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2448,7 +2479,7 @@ export class ${pascalName} {
|
|
|
2448
2479
|
}
|
|
2449
2480
|
`;
|
|
2450
2481
|
fileDefs.push({
|
|
2451
|
-
absolutePath:
|
|
2482
|
+
absolutePath: path26__default.default.join(targetDir, `${pascalName}.ts`),
|
|
2452
2483
|
content: facadeContent
|
|
2453
2484
|
});
|
|
2454
2485
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2456,7 +2487,7 @@ export class ${pascalName} {
|
|
|
2456
2487
|
}
|
|
2457
2488
|
`;
|
|
2458
2489
|
fileDefs.push({
|
|
2459
|
-
absolutePath:
|
|
2490
|
+
absolutePath: path26__default.default.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2460
2491
|
content: portContent
|
|
2461
2492
|
});
|
|
2462
2493
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2468,7 +2499,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2468
2499
|
}
|
|
2469
2500
|
`;
|
|
2470
2501
|
fileDefs.push({
|
|
2471
|
-
absolutePath:
|
|
2502
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2472
2503
|
content: concreteContent
|
|
2473
2504
|
});
|
|
2474
2505
|
if (options.includeStub !== false) {
|
|
@@ -2481,7 +2512,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2481
2512
|
}
|
|
2482
2513
|
`;
|
|
2483
2514
|
fileDefs.push({
|
|
2484
|
-
absolutePath:
|
|
2515
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2485
2516
|
content: stubContent
|
|
2486
2517
|
});
|
|
2487
2518
|
}
|
|
@@ -2496,7 +2527,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2496
2527
|
export default diProvider;
|
|
2497
2528
|
`;
|
|
2498
2529
|
fileDefs.push({
|
|
2499
|
-
absolutePath:
|
|
2530
|
+
absolutePath: path26__default.default.join(targetDir, "diProvider.ts"),
|
|
2500
2531
|
content: providerContent
|
|
2501
2532
|
});
|
|
2502
2533
|
if (options.isRegistry) {
|
|
@@ -2513,7 +2544,7 @@ export default diProvider;
|
|
|
2513
2544
|
files: []
|
|
2514
2545
|
};
|
|
2515
2546
|
fileDefs.push({
|
|
2516
|
-
absolutePath:
|
|
2547
|
+
absolutePath: path26__default.default.join(targetDir, "registry.json"),
|
|
2517
2548
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2518
2549
|
});
|
|
2519
2550
|
}
|
|
@@ -2550,7 +2581,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2550
2581
|
}
|
|
2551
2582
|
const normalizedName = toKebabCase(name);
|
|
2552
2583
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2553
|
-
const defaultDir =
|
|
2584
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseSharedDir, normalizedName));
|
|
2554
2585
|
let targetDirRel = defaultDir;
|
|
2555
2586
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2556
2587
|
targetDirRel = handleCancel(
|
|
@@ -2561,7 +2592,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2561
2592
|
})
|
|
2562
2593
|
).trim();
|
|
2563
2594
|
}
|
|
2564
|
-
const targetDir = flags.dest ?
|
|
2595
|
+
const targetDir = flags.dest ? path26__default.default.resolve(projectRoot, flags.dest) : flags.target ? path26__default.default.resolve(projectRoot, flags.target) : path26__default.default.resolve(projectRoot, targetDirRel);
|
|
2565
2596
|
let description = flags.description || "";
|
|
2566
2597
|
let includeStub = true;
|
|
2567
2598
|
if (!flags.yes) {
|
|
@@ -2595,7 +2626,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2595
2626
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2596
2627
|
p19__namespace.outro(
|
|
2597
2628
|
pc14__default.default.green(
|
|
2598
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2629
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2599
2630
|
)
|
|
2600
2631
|
);
|
|
2601
2632
|
}
|
|
@@ -3022,7 +3053,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
3022
3053
|
const infraFile = generateInfrastructure(options);
|
|
3023
3054
|
files.push(infraFile);
|
|
3024
3055
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
3025
|
-
const featureDir =
|
|
3056
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
3026
3057
|
const providerFile = updateOrGenerateDiProvider(
|
|
3027
3058
|
featureDir,
|
|
3028
3059
|
feature,
|
|
@@ -3184,7 +3215,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3184
3215
|
files.push(stubFile);
|
|
3185
3216
|
}
|
|
3186
3217
|
if (registerInProvider && generateStub) {
|
|
3187
|
-
const featureDir =
|
|
3218
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
3188
3219
|
const providerFile = updateOrGenerateDiProvider(
|
|
3189
3220
|
featureDir,
|
|
3190
3221
|
feature,
|
|
@@ -3372,21 +3403,21 @@ function generateInjectable(options) {
|
|
|
3372
3403
|
const className = toPascalCase(options.name);
|
|
3373
3404
|
let targetDir = "";
|
|
3374
3405
|
if (options.customPath) {
|
|
3375
|
-
targetDir =
|
|
3406
|
+
targetDir = path26__default.default.isAbsolute(options.customPath) ? options.customPath : path26__default.default.join(projectRoot, options.customPath);
|
|
3376
3407
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3377
|
-
targetDir =
|
|
3408
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3378
3409
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3379
|
-
targetDir =
|
|
3410
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3380
3411
|
} else if (options.location === "module") {
|
|
3381
|
-
targetDir =
|
|
3412
|
+
targetDir = path26__default.default.join(projectRoot, "modules", className);
|
|
3382
3413
|
} else {
|
|
3383
|
-
targetDir = options.feature ?
|
|
3414
|
+
targetDir = options.feature ? path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26__default.default.join(projectRoot, "shared", className.toLowerCase());
|
|
3384
3415
|
}
|
|
3385
|
-
const filePath =
|
|
3416
|
+
const filePath = path26__default.default.join(targetDir, `${className}.ts`);
|
|
3386
3417
|
let ifaceName;
|
|
3387
3418
|
if (options.generateInterface) {
|
|
3388
3419
|
ifaceName = `I${className}`;
|
|
3389
|
-
const ifacePath =
|
|
3420
|
+
const ifacePath = path26__default.default.join(targetDir, `${ifaceName}.ts`);
|
|
3390
3421
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3391
3422
|
files.push(
|
|
3392
3423
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3515,7 +3546,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3515
3546
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3516
3547
|
const s = p19__namespace.spinner();
|
|
3517
3548
|
s.start("Scanning for .example environment files...");
|
|
3518
|
-
const files =
|
|
3549
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3519
3550
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3520
3551
|
if (exampleFiles.length === 0) {
|
|
3521
3552
|
s.stop("No .example files found.");
|
|
@@ -3525,11 +3556,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3525
3556
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3526
3557
|
const generatedFiles = [];
|
|
3527
3558
|
for (const exampleFile of exampleFiles) {
|
|
3528
|
-
const sourcePath =
|
|
3559
|
+
const sourcePath = path26__default.default.join(projectRoot, exampleFile);
|
|
3529
3560
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3530
|
-
const targetPath =
|
|
3561
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3531
3562
|
let overwrite = flags.overwrite;
|
|
3532
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3563
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17__default.default.existsSync(targetPath)) {
|
|
3533
3564
|
overwrite = await p19__namespace.confirm({
|
|
3534
3565
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3535
3566
|
initialValue: false
|
|
@@ -3539,7 +3570,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3539
3570
|
process.exit(0);
|
|
3540
3571
|
}
|
|
3541
3572
|
}
|
|
3542
|
-
const content =
|
|
3573
|
+
const content = fs17__default.default.readFileSync(sourcePath, "utf-8");
|
|
3543
3574
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3544
3575
|
projectRoot,
|
|
3545
3576
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3558,7 +3589,7 @@ var EnvEditor = class {
|
|
|
3558
3589
|
filePath;
|
|
3559
3590
|
constructor(filePath) {
|
|
3560
3591
|
this.filePath = filePath;
|
|
3561
|
-
this.lines =
|
|
3592
|
+
this.lines = fs17__default.default.existsSync(filePath) ? fs17__default.default.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3562
3593
|
}
|
|
3563
3594
|
getLines() {
|
|
3564
3595
|
return [...this.lines];
|
|
@@ -3622,18 +3653,18 @@ var EnvEditor = class {
|
|
|
3622
3653
|
});
|
|
3623
3654
|
}
|
|
3624
3655
|
save() {
|
|
3625
|
-
const dir =
|
|
3626
|
-
if (!
|
|
3627
|
-
|
|
3656
|
+
const dir = path26__default.default.dirname(this.filePath);
|
|
3657
|
+
if (!fs17__default.default.existsSync(dir)) {
|
|
3658
|
+
fs17__default.default.mkdirSync(dir, { recursive: true });
|
|
3628
3659
|
}
|
|
3629
|
-
|
|
3660
|
+
fs17__default.default.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3630
3661
|
}
|
|
3631
3662
|
};
|
|
3632
3663
|
function discoverEnvironments(projectRoot) {
|
|
3633
|
-
if (!
|
|
3664
|
+
if (!fs17__default.default.existsSync(projectRoot)) {
|
|
3634
3665
|
return ["local", "dev", "prod"];
|
|
3635
3666
|
}
|
|
3636
|
-
const files =
|
|
3667
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3637
3668
|
const envs = /* @__PURE__ */ new Set();
|
|
3638
3669
|
for (const file of files) {
|
|
3639
3670
|
if (file.startsWith(".env")) {
|
|
@@ -3658,7 +3689,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3658
3689
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3659
3690
|
const s = p19__namespace.spinner();
|
|
3660
3691
|
s.start("Scanning for local environment files...");
|
|
3661
|
-
const files =
|
|
3692
|
+
const files = fs17__default.default.readdirSync(projectRoot, { withFileTypes: true });
|
|
3662
3693
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3663
3694
|
if (envFiles.length === 0) {
|
|
3664
3695
|
s.stop("No local .env files found.");
|
|
@@ -3668,11 +3699,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3668
3699
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3669
3700
|
const generatedFiles = [];
|
|
3670
3701
|
for (const envFile of envFiles) {
|
|
3671
|
-
const sourcePath =
|
|
3702
|
+
const sourcePath = path26__default.default.join(projectRoot, envFile);
|
|
3672
3703
|
const targetFile = `${envFile}.example`;
|
|
3673
|
-
const targetPath =
|
|
3704
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3674
3705
|
let overwrite = flags.overwrite;
|
|
3675
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3706
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17__default.default.existsSync(targetPath)) {
|
|
3676
3707
|
overwrite = await p19__namespace.confirm({
|
|
3677
3708
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3678
3709
|
initialValue: true
|
|
@@ -3706,12 +3737,12 @@ function isInteractive(flags) {
|
|
|
3706
3737
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3707
3738
|
const keys = /* @__PURE__ */ new Set();
|
|
3708
3739
|
for (const env of envs) {
|
|
3709
|
-
const localPath =
|
|
3710
|
-
const examplePath =
|
|
3711
|
-
if (
|
|
3740
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3741
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3742
|
+
if (fs17__default.default.existsSync(localPath)) {
|
|
3712
3743
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3713
3744
|
}
|
|
3714
|
-
if (
|
|
3745
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3715
3746
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3716
3747
|
}
|
|
3717
3748
|
}
|
|
@@ -3783,8 +3814,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3783
3814
|
})
|
|
3784
3815
|
);
|
|
3785
3816
|
}
|
|
3786
|
-
const localPath =
|
|
3787
|
-
const examplePath =
|
|
3817
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3818
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3788
3819
|
if (flags.dryRun) {
|
|
3789
3820
|
p19__namespace.log.info(
|
|
3790
3821
|
pc14__default.default.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3831,8 +3862,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3831
3862
|
);
|
|
3832
3863
|
}
|
|
3833
3864
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3834
|
-
const localPath =
|
|
3835
|
-
return
|
|
3865
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3866
|
+
return fs17__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3836
3867
|
});
|
|
3837
3868
|
let targetEnvs = [];
|
|
3838
3869
|
if (flags.env) {
|
|
@@ -3858,7 +3889,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3858
3889
|
return { key: targetKey, environments: [] };
|
|
3859
3890
|
}
|
|
3860
3891
|
for (const env of targetEnvs) {
|
|
3861
|
-
const localPath =
|
|
3892
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3862
3893
|
const localEditor = new EnvEditor(localPath);
|
|
3863
3894
|
const currentVal = localEditor.getValue(targetKey);
|
|
3864
3895
|
let newVal = value;
|
|
@@ -3888,7 +3919,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3888
3919
|
} else {
|
|
3889
3920
|
localEditor.set(targetKey, newVal);
|
|
3890
3921
|
localEditor.save();
|
|
3891
|
-
const examplePath =
|
|
3922
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3892
3923
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3893
3924
|
if (!exampleEditor.has(targetKey)) {
|
|
3894
3925
|
exampleEditor.set(targetKey, "");
|
|
@@ -3924,9 +3955,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3924
3955
|
);
|
|
3925
3956
|
}
|
|
3926
3957
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3927
|
-
const localPath =
|
|
3928
|
-
const examplePath =
|
|
3929
|
-
return
|
|
3958
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3959
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3960
|
+
return fs17__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs17__default.default.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
|
|
3930
3961
|
});
|
|
3931
3962
|
let targetEnvs = [];
|
|
3932
3963
|
if (flags.env) {
|
|
@@ -3964,19 +3995,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3964
3995
|
}
|
|
3965
3996
|
}
|
|
3966
3997
|
for (const env of targetEnvs) {
|
|
3967
|
-
const localPath =
|
|
3968
|
-
const examplePath =
|
|
3998
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3999
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3969
4000
|
if (flags.dryRun) {
|
|
3970
4001
|
p19__namespace.log.info(
|
|
3971
4002
|
pc14__default.default.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3972
4003
|
);
|
|
3973
4004
|
} else {
|
|
3974
|
-
if (
|
|
4005
|
+
if (fs17__default.default.existsSync(localPath)) {
|
|
3975
4006
|
const localEditor = new EnvEditor(localPath);
|
|
3976
4007
|
localEditor.delete(targetKey);
|
|
3977
4008
|
localEditor.save();
|
|
3978
4009
|
}
|
|
3979
|
-
if (
|
|
4010
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3980
4011
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3981
4012
|
exampleEditor.delete(targetKey);
|
|
3982
4013
|
exampleEditor.save();
|
|
@@ -4001,10 +4032,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
4001
4032
|
for (const env of envs) {
|
|
4002
4033
|
const localFile = resolveEnvFilename(env, false);
|
|
4003
4034
|
const exampleFile = resolveEnvFilename(env, true);
|
|
4004
|
-
const localPath =
|
|
4005
|
-
const examplePath =
|
|
4006
|
-
const localExists =
|
|
4007
|
-
const exampleExists =
|
|
4035
|
+
const localPath = path26__default.default.join(projectRoot, localFile);
|
|
4036
|
+
const examplePath = path26__default.default.join(projectRoot, exampleFile);
|
|
4037
|
+
const localExists = fs17__default.default.existsSync(localPath);
|
|
4038
|
+
const exampleExists = fs17__default.default.existsSync(examplePath);
|
|
4008
4039
|
const localEditor = new EnvEditor(localPath);
|
|
4009
4040
|
const exampleEditor = new EnvEditor(examplePath);
|
|
4010
4041
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4343,17 +4374,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4343
4374
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4344
4375
|
}
|
|
4345
4376
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4346
|
-
return
|
|
4377
|
+
return path26__default.default.join(projectRoot, "src", folder);
|
|
4347
4378
|
}
|
|
4348
4379
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4349
4380
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4350
4381
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4351
|
-
const inConfigured =
|
|
4352
|
-
if (
|
|
4353
|
-
const inSrc =
|
|
4354
|
-
if (
|
|
4355
|
-
const inRoot =
|
|
4356
|
-
if (
|
|
4382
|
+
const inConfigured = path26__default.default.join(configuredDir, modName);
|
|
4383
|
+
if (fs17__default.default.existsSync(inConfigured)) return inConfigured;
|
|
4384
|
+
const inSrc = path26__default.default.join(projectRoot, "src", folder, modName);
|
|
4385
|
+
if (fs17__default.default.existsSync(inSrc)) return inSrc;
|
|
4386
|
+
const inRoot = path26__default.default.join(projectRoot, folder, modName);
|
|
4387
|
+
if (fs17__default.default.existsSync(inRoot)) return inRoot;
|
|
4357
4388
|
return null;
|
|
4358
4389
|
}
|
|
4359
4390
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4364,14 +4395,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4364
4395
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4365
4396
|
const dirs = [
|
|
4366
4397
|
configuredDir,
|
|
4367
|
-
|
|
4368
|
-
|
|
4398
|
+
path26__default.default.join(projectRoot, "src", folder),
|
|
4399
|
+
path26__default.default.join(projectRoot, folder)
|
|
4369
4400
|
];
|
|
4370
4401
|
const set = /* @__PURE__ */ new Set();
|
|
4371
4402
|
for (const dir of dirs) {
|
|
4372
|
-
if (
|
|
4403
|
+
if (fs17__default.default.existsSync(dir)) {
|
|
4373
4404
|
try {
|
|
4374
|
-
const entries =
|
|
4405
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4375
4406
|
for (const e of entries) {
|
|
4376
4407
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4377
4408
|
set.add(e.name);
|
|
@@ -4573,7 +4604,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4573
4604
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4574
4605
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4575
4606
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4576
|
-
const displayDest =
|
|
4607
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, programmaticAlias));
|
|
4577
4608
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4578
4609
|
resolutions.set(key, {
|
|
4579
4610
|
type: modType,
|
|
@@ -4761,7 +4792,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4761
4792
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4762
4793
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4763
4794
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4764
|
-
const displayDest =
|
|
4795
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, newName));
|
|
4765
4796
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4766
4797
|
resolutions.set(key, {
|
|
4767
4798
|
type: modType,
|
|
@@ -4813,7 +4844,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4813
4844
|
continue;
|
|
4814
4845
|
}
|
|
4815
4846
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4816
|
-
const targetDir =
|
|
4847
|
+
const targetDir = path26__default.default.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4817
4848
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4818
4849
|
if (key !== rootKey) {
|
|
4819
4850
|
p19__namespace.log.step(
|
|
@@ -4823,30 +4854,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4823
4854
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4824
4855
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4825
4856
|
if (existingDir && existingDir !== targetDir) {
|
|
4826
|
-
|
|
4857
|
+
fs17__default.default.rmSync(existingDir, { recursive: true, force: true });
|
|
4827
4858
|
}
|
|
4828
|
-
if (
|
|
4829
|
-
|
|
4859
|
+
if (fs17__default.default.existsSync(targetDir)) {
|
|
4860
|
+
fs17__default.default.rmSync(targetDir, { recursive: true, force: true });
|
|
4830
4861
|
}
|
|
4831
4862
|
}
|
|
4832
4863
|
for (const file of res.manifest.files || []) {
|
|
4833
4864
|
if (file === "registry.json") continue;
|
|
4834
4865
|
if (!includeTests && isTestFile(file)) continue;
|
|
4835
4866
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4836
|
-
const localFilePath =
|
|
4837
|
-
if (
|
|
4867
|
+
const localFilePath = path26__default.default.join(targetDir, file);
|
|
4868
|
+
if (fs17__default.default.existsSync(localFilePath) && !isOverwrite) {
|
|
4838
4869
|
p19__namespace.log.warn(
|
|
4839
|
-
` \u21B3 Skipping existing file: ${pc14__default.default.dim(
|
|
4870
|
+
` \u21B3 Skipping existing file: ${pc14__default.default.dim(path26__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4840
4871
|
);
|
|
4841
4872
|
continue;
|
|
4842
4873
|
}
|
|
4843
4874
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4844
4875
|
content = rewriteImports(content, globalAliasMap);
|
|
4845
4876
|
if (!flags.dryRun) {
|
|
4846
|
-
|
|
4847
|
-
|
|
4877
|
+
fs17__default.default.mkdirSync(path26__default.default.dirname(localFilePath), { recursive: true });
|
|
4878
|
+
fs17__default.default.writeFileSync(localFilePath, content, "utf-8");
|
|
4848
4879
|
}
|
|
4849
|
-
const displayPath =
|
|
4880
|
+
const displayPath = path26__default.default.relative(projectRoot, localFilePath);
|
|
4850
4881
|
totalWrittenFiles.push(displayPath);
|
|
4851
4882
|
p19__namespace.log.message(` ${pc14__default.default.green("+")} ${displayPath}`);
|
|
4852
4883
|
}
|
|
@@ -4872,7 +4903,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4872
4903
|
} else {
|
|
4873
4904
|
const rootRes = resolutions.get(rootKey);
|
|
4874
4905
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4875
|
-
const displayTarget =
|
|
4906
|
+
const displayTarget = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, rootRes.targetName));
|
|
4876
4907
|
p19__namespace.log.success(
|
|
4877
4908
|
pc14__default.default.green(
|
|
4878
4909
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4891,19 +4922,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4891
4922
|
}
|
|
4892
4923
|
};
|
|
4893
4924
|
function isTestFile2(filePath, projectRoot) {
|
|
4894
|
-
const norm = (projectRoot ?
|
|
4895
|
-
const base =
|
|
4925
|
+
const norm = (projectRoot ? path26__default.default.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4926
|
+
const base = path26__default.default.basename(norm);
|
|
4896
4927
|
return base.endsWith(".test.ts") || base.endsWith(".test.js") || base.endsWith(".spec.ts") || base.endsWith(".spec.js") || base.endsWith(".d.ts") || norm.includes("/__tests__/") || norm.startsWith("__tests__/") || /(?:^|\/)tests?\//.test(norm);
|
|
4897
4928
|
}
|
|
4898
4929
|
function scanDirFiles(dir, projectRoot) {
|
|
4899
|
-
if (!
|
|
4900
|
-
const entries =
|
|
4930
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
4931
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4901
4932
|
const files = [];
|
|
4902
4933
|
for (const entry of entries) {
|
|
4903
4934
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4904
4935
|
continue;
|
|
4905
4936
|
}
|
|
4906
|
-
const fullPath =
|
|
4937
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
4907
4938
|
if (entry.isDirectory()) {
|
|
4908
4939
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4909
4940
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4912,101 +4943,194 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4912
4943
|
}
|
|
4913
4944
|
return files;
|
|
4914
4945
|
}
|
|
4915
|
-
function classifyFile(
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
}
|
|
4935
|
-
const
|
|
4936
|
-
if (
|
|
4937
|
-
const
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
layer = "
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4946
|
+
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4947
|
+
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4948
|
+
const paths = getResolvedLintPaths(featuresBaseAbs, loadLintConfig(featuresBaseAbs));
|
|
4949
|
+
featuresBaseAbs = paths.featuresBase;
|
|
4950
|
+
sharedBaseAbs = paths.sharedBase;
|
|
4951
|
+
errorsBaseAbs = paths.errorsBase;
|
|
4952
|
+
}
|
|
4953
|
+
const normFullPath = path26__default.default.resolve(fullPathAbs);
|
|
4954
|
+
const normErrorsBase = path26__default.default.resolve(errorsBaseAbs);
|
|
4955
|
+
const normSharedBase = path26__default.default.resolve(sharedBaseAbs);
|
|
4956
|
+
const normFeaturesBase = path26__default.default.resolve(featuresBaseAbs);
|
|
4957
|
+
const relErrors = path26__default.default.relative(normErrorsBase, normFullPath).replace(/\\/g, "/");
|
|
4958
|
+
if (!relErrors.startsWith("..") && !path26__default.default.isAbsolute(relErrors)) {
|
|
4959
|
+
return {
|
|
4960
|
+
moduleType: "error",
|
|
4961
|
+
moduleName: "global-errors",
|
|
4962
|
+
feature: "global-errors",
|
|
4963
|
+
layer: "domain"
|
|
4964
|
+
};
|
|
4965
|
+
}
|
|
4966
|
+
const relShared = path26__default.default.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4967
|
+
if (!relShared.startsWith("..") && !path26__default.default.isAbsolute(relShared)) {
|
|
4968
|
+
const parts = relShared.split("/").filter(Boolean);
|
|
4969
|
+
if (parts.length >= 1) {
|
|
4970
|
+
const moduleName = parts[0];
|
|
4971
|
+
const rest = parts.slice(1).join("/");
|
|
4972
|
+
let layer = "root";
|
|
4973
|
+
if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
|
|
4974
|
+
layer = "domain";
|
|
4975
|
+
} else if (rest.startsWith("ports/") || rest === "ports") {
|
|
4976
|
+
layer = "ports";
|
|
4977
|
+
} else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
|
|
4978
|
+
layer = "infrastructure";
|
|
4979
|
+
}
|
|
4980
|
+
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
const relFeatures = path26__default.default.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4984
|
+
if (!relFeatures.startsWith("..") && !path26__default.default.isAbsolute(relFeatures)) {
|
|
4985
|
+
const parts = relFeatures.split("/").filter(Boolean);
|
|
4986
|
+
if (parts.length >= 2) {
|
|
4987
|
+
const moduleName = parts[0];
|
|
4988
|
+
const subfolder = parts[1];
|
|
4989
|
+
let layer;
|
|
4990
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4991
|
+
layer = "domain";
|
|
4992
|
+
} else if (subfolder === "useCases" || subfolder === "usecases") {
|
|
4993
|
+
layer = "useCases";
|
|
4994
|
+
} else if (subfolder === "services") {
|
|
4995
|
+
layer = "services";
|
|
4996
|
+
} else if (subfolder === "infrastructure") {
|
|
4997
|
+
layer = "infrastructure";
|
|
4998
|
+
} else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
|
|
4999
|
+
layer = "presentation";
|
|
5000
|
+
}
|
|
5001
|
+
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
5002
|
+
}
|
|
4948
5003
|
}
|
|
4949
5004
|
return {};
|
|
4950
5005
|
}
|
|
4951
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
5006
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4952
5007
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4953
5008
|
return { isExternal: true, isDiFramework: true };
|
|
4954
5009
|
}
|
|
4955
|
-
if (specifier.startsWith("node:") || [
|
|
5010
|
+
if (specifier.startsWith("node:") || [
|
|
5011
|
+
"crypto",
|
|
5012
|
+
"fs",
|
|
5013
|
+
"path",
|
|
5014
|
+
"http",
|
|
5015
|
+
"https",
|
|
5016
|
+
"url",
|
|
5017
|
+
"os",
|
|
5018
|
+
"child_process",
|
|
5019
|
+
"stream",
|
|
5020
|
+
"express",
|
|
5021
|
+
"cors",
|
|
5022
|
+
"dotenv"
|
|
5023
|
+
].includes(specifier)) {
|
|
4956
5024
|
return { isExternal: true, isDiFramework: false };
|
|
4957
5025
|
}
|
|
4958
|
-
|
|
5026
|
+
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
5027
|
+
const paths = getResolvedLintPaths(projectRoot, loadLintConfig(projectRoot));
|
|
5028
|
+
featuresBaseAbs = featuresBaseAbs || paths.featuresBase;
|
|
5029
|
+
sharedBaseAbs = sharedBaseAbs || paths.sharedBase;
|
|
5030
|
+
errorsBaseAbs = errorsBaseAbs || paths.errorsBase;
|
|
5031
|
+
}
|
|
5032
|
+
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
5033
|
+
let targetAbs = null;
|
|
4959
5034
|
if (specifier.startsWith(".")) {
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
5035
|
+
targetAbs = path26__default.default.resolve(path26__default.default.dirname(sourceFileAbs), specifier);
|
|
5036
|
+
} else {
|
|
5037
|
+
const featRel = path26__default.default.relative(projectRoot, featuresBaseAbs).replace(/\\/g, "/");
|
|
5038
|
+
const sharedRel = path26__default.default.relative(projectRoot, sharedBaseAbs).replace(/\\/g, "/");
|
|
5039
|
+
const errorsRel = path26__default.default.relative(projectRoot, errorsBaseAbs).replace(/\\/g, "/");
|
|
5040
|
+
const featPrefixes = ["features"];
|
|
5041
|
+
if (featRel && featRel !== "." && !featRel.startsWith("..")) {
|
|
5042
|
+
featPrefixes.push(featRel);
|
|
5043
|
+
const b = path26__default.default.basename(featRel);
|
|
5044
|
+
if (!featPrefixes.includes(b)) featPrefixes.push(b);
|
|
5045
|
+
}
|
|
5046
|
+
const sharedPrefixes = ["shared"];
|
|
5047
|
+
if (sharedRel && sharedRel !== "." && !sharedRel.startsWith("..")) {
|
|
5048
|
+
sharedPrefixes.push(sharedRel);
|
|
5049
|
+
const b = path26__default.default.basename(sharedRel);
|
|
5050
|
+
if (!sharedPrefixes.includes(b)) sharedPrefixes.push(b);
|
|
5051
|
+
}
|
|
5052
|
+
const errorsPrefixes = ["errors"];
|
|
5053
|
+
if (errorsRel && errorsRel !== "." && !errorsRel.startsWith("..")) {
|
|
5054
|
+
errorsPrefixes.push(errorsRel);
|
|
5055
|
+
const b = path26__default.default.basename(errorsRel);
|
|
5056
|
+
if (!errorsPrefixes.includes(b)) errorsPrefixes.push(b);
|
|
5057
|
+
}
|
|
5058
|
+
for (const prefix of featPrefixes) {
|
|
5059
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5060
|
+
const m = specifier.match(aliasRegex);
|
|
5061
|
+
if (m && m[2]) {
|
|
5062
|
+
targetAbs = path26__default.default.resolve(featuresBaseAbs, m[2]);
|
|
5063
|
+
break;
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
if (!targetAbs) {
|
|
5067
|
+
for (const prefix of sharedPrefixes) {
|
|
5068
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5069
|
+
const m = specifier.match(aliasRegex);
|
|
5070
|
+
if (m && m[2]) {
|
|
5071
|
+
targetAbs = path26__default.default.resolve(sharedBaseAbs, m[2]);
|
|
5072
|
+
break;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
if (!targetAbs) {
|
|
5077
|
+
for (const prefix of errorsPrefixes) {
|
|
5078
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}(?:\\/(.*))?$`);
|
|
5079
|
+
const m = specifier.match(aliasRegex);
|
|
5080
|
+
if (m) {
|
|
5081
|
+
targetAbs = path26__default.default.resolve(errorsBaseAbs, m[2] || "");
|
|
5082
|
+
break;
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
if (!targetAbs && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
|
|
5087
|
+
const sub = specifier.slice(2);
|
|
5088
|
+
const srcCandidate = path26__default.default.resolve(projectRoot, "src", sub);
|
|
5089
|
+
const rootCandidate = path26__default.default.resolve(projectRoot, sub);
|
|
5090
|
+
if (fs17__default.default.existsSync(srcCandidate)) {
|
|
5091
|
+
targetAbs = srcCandidate;
|
|
5092
|
+
} else if (fs17__default.default.existsSync(rootCandidate)) {
|
|
5093
|
+
targetAbs = rootCandidate;
|
|
5094
|
+
} else {
|
|
5095
|
+
targetAbs = srcCandidate;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
if (!targetAbs) {
|
|
5099
|
+
const featCandidate = path26__default.default.resolve(featuresBaseAbs, specifier);
|
|
5100
|
+
if (fs17__default.default.existsSync(featCandidate) || fs17__default.default.existsSync(featCandidate + ".ts") || fs17__default.default.existsSync(featCandidate + ".js")) {
|
|
5101
|
+
targetAbs = featCandidate;
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
5105
|
+
if (!targetAbs) {
|
|
5106
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5107
|
+
}
|
|
5108
|
+
const normTarget = path26__default.default.resolve(targetAbs);
|
|
5109
|
+
const srcConfig = path26__default.default.resolve(projectRoot, "src/configs");
|
|
5110
|
+
const rootConfig = path26__default.default.resolve(projectRoot, "configs");
|
|
5111
|
+
const isTargetConfig = isGlobalConfig || normTarget === srcConfig || normTarget.startsWith(srcConfig + path26__default.default.sep) || normTarget === rootConfig || normTarget.startsWith(rootConfig + path26__default.default.sep);
|
|
5112
|
+
const classification = classifyFile(targetAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs);
|
|
5113
|
+
if (classification.moduleType === "error") {
|
|
5114
|
+
return {
|
|
5115
|
+
isExternal: false,
|
|
5116
|
+
isDiFramework: false,
|
|
5117
|
+
isGlobalError: true,
|
|
5118
|
+
isTargetError: true
|
|
5119
|
+
};
|
|
4981
5120
|
}
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
const
|
|
4986
|
-
const
|
|
4987
|
-
const rest = featMatch[3] || "";
|
|
4988
|
-
let targetLayer;
|
|
4989
|
-
if (targetSubfolder === "domain" || targetSubfolder === "errors") {
|
|
4990
|
-
targetLayer = "domain";
|
|
4991
|
-
} else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
|
|
4992
|
-
targetLayer = "useCases";
|
|
4993
|
-
} else if (targetSubfolder === "services") {
|
|
4994
|
-
targetLayer = "services";
|
|
4995
|
-
} else if (targetSubfolder === "infrastructure") {
|
|
4996
|
-
targetLayer = "infrastructure";
|
|
4997
|
-
} else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
|
|
4998
|
-
targetLayer = "presentation";
|
|
4999
|
-
}
|
|
5000
|
-
const basePart = rest ? path2__default.default.basename(rest) : targetSubfolder;
|
|
5001
|
-
const targetFileName = basePart.replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5121
|
+
if (classification.moduleType === "feature") {
|
|
5122
|
+
const rel = path26__default.default.relative(featuresBaseAbs, targetAbs).replace(/\\/g, "/");
|
|
5123
|
+
const parts = rel.split("/").filter(Boolean);
|
|
5124
|
+
const targetSubfolder = parts[1];
|
|
5125
|
+
const targetFileName = path26__default.default.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5002
5126
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
5003
5127
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
5004
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5128
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
5005
5129
|
return {
|
|
5006
5130
|
isExternal: false,
|
|
5007
5131
|
isDiFramework: false,
|
|
5008
|
-
targetFeature,
|
|
5009
|
-
targetLayer,
|
|
5132
|
+
targetFeature: classification.moduleName,
|
|
5133
|
+
targetLayer: classification.layer,
|
|
5010
5134
|
targetSubfolder,
|
|
5011
5135
|
targetFileName,
|
|
5012
5136
|
isTargetPort,
|
|
@@ -5014,25 +5138,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5014
5138
|
isTargetError
|
|
5015
5139
|
};
|
|
5016
5140
|
}
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
const
|
|
5020
|
-
const sharedRest = sharedMatch[2] || "";
|
|
5021
|
-
const isSharedRoot = !sharedRest.includes("/");
|
|
5022
|
-
const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
|
|
5023
|
-
const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
|
|
5024
|
-
const isSharedDomain = sharedRest.startsWith("domain/") || sharedRest === "domain";
|
|
5141
|
+
if (classification.moduleType === "shared") {
|
|
5142
|
+
const targetFileName = path26__default.default.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5143
|
+
const isTargetError = classification.layer === "domain" && (targetFileName.endsWith("Error") || targetFileName.endsWith("Errors") || targetAbs.includes("/errors/"));
|
|
5025
5144
|
return {
|
|
5026
5145
|
isExternal: false,
|
|
5027
5146
|
isDiFramework: false,
|
|
5028
|
-
targetSharedModule,
|
|
5029
|
-
isSharedRoot,
|
|
5030
|
-
isSharedPorts,
|
|
5031
|
-
isSharedInfra,
|
|
5032
|
-
isSharedDomain
|
|
5147
|
+
targetSharedModule: classification.moduleName,
|
|
5148
|
+
isSharedRoot: classification.layer === "root",
|
|
5149
|
+
isSharedPorts: classification.layer === "ports",
|
|
5150
|
+
isSharedInfra: classification.layer === "infrastructure",
|
|
5151
|
+
isSharedDomain: classification.layer === "domain",
|
|
5152
|
+
isTargetError
|
|
5033
5153
|
};
|
|
5034
5154
|
}
|
|
5035
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5155
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
5036
5156
|
}
|
|
5037
5157
|
function extractImportStatements(content) {
|
|
5038
5158
|
const statements = [];
|
|
@@ -5154,22 +5274,50 @@ var FeatureDependencyGraph = class {
|
|
|
5154
5274
|
return dfs(start);
|
|
5155
5275
|
}
|
|
5156
5276
|
};
|
|
5157
|
-
function discoverAllFeatures(
|
|
5158
|
-
const
|
|
5159
|
-
if (!
|
|
5160
|
-
const
|
|
5161
|
-
|
|
5277
|
+
function discoverAllFeatures(featuresBaseAbs, excludeDirs = []) {
|
|
5278
|
+
const base = featuresBaseAbs;
|
|
5279
|
+
if (!fs17__default.default.existsSync(base)) return [];
|
|
5280
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26__default.default.resolve(d)));
|
|
5281
|
+
const entries = fs17__default.default.readdirSync(base, { withFileTypes: true });
|
|
5282
|
+
return entries.filter((e) => {
|
|
5283
|
+
if (!e.isDirectory()) return false;
|
|
5284
|
+
if (e.name.startsWith(".")) return false;
|
|
5285
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5286
|
+
return false;
|
|
5287
|
+
}
|
|
5288
|
+
const resolved = path26__default.default.resolve(base, e.name);
|
|
5289
|
+
for (const ex of excludeSet) {
|
|
5290
|
+
if (ex === resolved || ex.startsWith(resolved + path26__default.default.sep) || resolved.startsWith(ex + path26__default.default.sep)) {
|
|
5291
|
+
return false;
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
return true;
|
|
5295
|
+
}).map((e) => ({
|
|
5162
5296
|
name: e.name,
|
|
5163
|
-
dir:
|
|
5297
|
+
dir: path26__default.default.join(base, e.name)
|
|
5164
5298
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5165
5299
|
}
|
|
5166
|
-
function discoverAllShared(
|
|
5167
|
-
const
|
|
5168
|
-
if (!
|
|
5169
|
-
const
|
|
5170
|
-
|
|
5300
|
+
function discoverAllShared(sharedBaseAbs, excludeDirs = []) {
|
|
5301
|
+
const base = sharedBaseAbs;
|
|
5302
|
+
if (!fs17__default.default.existsSync(base)) return [];
|
|
5303
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26__default.default.resolve(d)));
|
|
5304
|
+
const entries = fs17__default.default.readdirSync(base, { withFileTypes: true });
|
|
5305
|
+
return entries.filter((e) => {
|
|
5306
|
+
if (!e.isDirectory()) return false;
|
|
5307
|
+
if (e.name.startsWith(".")) return false;
|
|
5308
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5309
|
+
return false;
|
|
5310
|
+
}
|
|
5311
|
+
const resolved = path26__default.default.resolve(base, e.name);
|
|
5312
|
+
for (const ex of excludeSet) {
|
|
5313
|
+
if (ex === resolved || ex.startsWith(resolved + path26__default.default.sep) || resolved.startsWith(ex + path26__default.default.sep)) {
|
|
5314
|
+
return false;
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
return true;
|
|
5318
|
+
}).map((e) => ({
|
|
5171
5319
|
name: e.name,
|
|
5172
|
-
dir:
|
|
5320
|
+
dir: path26__default.default.join(base, e.name)
|
|
5173
5321
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5174
5322
|
}
|
|
5175
5323
|
function extractClassMethods(content, className) {
|
|
@@ -5325,7 +5473,7 @@ function extractClassMethods(content, className) {
|
|
|
5325
5473
|
return methods;
|
|
5326
5474
|
}
|
|
5327
5475
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5328
|
-
const fileName =
|
|
5476
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5329
5477
|
const lines = content.split(/\r?\n/);
|
|
5330
5478
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5331
5479
|
if (!classMatch) {
|
|
@@ -5421,7 +5569,7 @@ private ${m.name}(...) { ... }`
|
|
|
5421
5569
|
}
|
|
5422
5570
|
}
|
|
5423
5571
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5424
|
-
const fileName =
|
|
5572
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5425
5573
|
const lines = content.split(/\r?\n/);
|
|
5426
5574
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5427
5575
|
if (!classMatch) {
|
|
@@ -5520,11 +5668,40 @@ function lintCodebase(options = {}) {
|
|
|
5520
5668
|
const startTime = Date.now();
|
|
5521
5669
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5522
5670
|
const failFast = !options.complete;
|
|
5523
|
-
const
|
|
5524
|
-
const
|
|
5671
|
+
const lintConfig = loadLintConfig(projectRoot);
|
|
5672
|
+
const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
|
|
5673
|
+
const missingFoundations = [];
|
|
5674
|
+
if (!fs17__default.default.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5675
|
+
if (!fs17__default.default.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5676
|
+
if (!fs17__default.default.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5677
|
+
if (missingFoundations.length > 0) {
|
|
5678
|
+
throw new DependencyRuleError(
|
|
5679
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5680
|
+
[],
|
|
5681
|
+
true
|
|
5682
|
+
);
|
|
5683
|
+
}
|
|
5684
|
+
const baseErrorRelative = lintConfig.baseErrorPath || path26__default.default.relative(projectRoot, path26__default.default.join(errorsBase, "AppError.ts")).replace(/\\/g, "/");
|
|
5685
|
+
const baseErrorAbs = path26__default.default.resolve(projectRoot, baseErrorRelative);
|
|
5686
|
+
if (!fs17__default.default.existsSync(baseErrorAbs)) {
|
|
5687
|
+
throw new DependencyRuleError(
|
|
5688
|
+
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5689
|
+
[],
|
|
5690
|
+
true
|
|
5691
|
+
);
|
|
5692
|
+
}
|
|
5693
|
+
const baseErrorContent = fs17__default.default.readFileSync(baseErrorAbs, "utf-8");
|
|
5694
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5695
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5696
|
+
);
|
|
5697
|
+
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5698
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5699
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5700
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5525
5701
|
const allModules = [
|
|
5526
5702
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5527
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5703
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" })),
|
|
5704
|
+
errorModule
|
|
5528
5705
|
];
|
|
5529
5706
|
const graph = new FeatureDependencyGraph();
|
|
5530
5707
|
const allViolations = [];
|
|
@@ -5554,7 +5731,7 @@ Code: ${v.code}`;
|
|
|
5554
5731
|
scannedFilesCount: 0,
|
|
5555
5732
|
passed: true,
|
|
5556
5733
|
violations: [],
|
|
5557
|
-
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : ["domain", "ports", "infrastructure", "root"]
|
|
5734
|
+
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : mod.type === "shared" ? ["domain", "ports", "infrastructure", "root"] : ["domain"]
|
|
5558
5735
|
});
|
|
5559
5736
|
}
|
|
5560
5737
|
for (const mod of allModules) {
|
|
@@ -5562,13 +5739,128 @@ Code: ${v.code}`;
|
|
|
5562
5739
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5563
5740
|
modResult.scannedFilesCount = allFiles.length;
|
|
5564
5741
|
for (const file of allFiles) {
|
|
5565
|
-
const relPath =
|
|
5566
|
-
const { moduleType, moduleName, layer } = classifyFile(file,
|
|
5742
|
+
const relPath = path26__default.default.relative(projectRoot, file).replace(/\\/g, "/");
|
|
5743
|
+
const { moduleType, moduleName, layer } = classifyFile(file, featuresBase, sharedBase, errorsBase);
|
|
5567
5744
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5568
|
-
const content =
|
|
5745
|
+
const content = fs17__default.default.readFileSync(file, "utf-8");
|
|
5569
5746
|
const statements = extractImportStatements(content);
|
|
5570
|
-
const baseName =
|
|
5747
|
+
const baseName = path26__default.default.basename(file);
|
|
5571
5748
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5749
|
+
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5750
|
+
let throwMatch;
|
|
5751
|
+
while ((throwMatch = throwErrorRegex.exec(content)) !== null) {
|
|
5752
|
+
const charIndex = throwMatch.index;
|
|
5753
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5754
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || throwMatch[0];
|
|
5755
|
+
if (lineCode.startsWith("//")) continue;
|
|
5756
|
+
addViolation({
|
|
5757
|
+
feature: moduleName,
|
|
5758
|
+
layer,
|
|
5759
|
+
file,
|
|
5760
|
+
relativePath: relPath,
|
|
5761
|
+
line: lineNum,
|
|
5762
|
+
category: "Plain Error Throw Violation",
|
|
5763
|
+
code: lineCode,
|
|
5764
|
+
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5765
|
+
severity: "error",
|
|
5766
|
+
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a domain-specific error class that inherits from it.`,
|
|
5767
|
+
fixExample: {
|
|
5768
|
+
bad: lineCode,
|
|
5769
|
+
good: `throw new ${baseErrorClassName}("Something went wrong");`
|
|
5770
|
+
}
|
|
5771
|
+
});
|
|
5772
|
+
}
|
|
5773
|
+
const isErrorFile = relPath.includes("/errors/") || relPath.startsWith("errors/") || relPath.startsWith("src/errors/") || moduleType === "error" || baseName.endsWith("Error.ts") || baseName.endsWith("Errors.ts") || baseName.endsWith("Error.js") || baseName.endsWith("Errors.js");
|
|
5774
|
+
const isBaseErrorFile = path26__default.default.resolve(file) === path26__default.default.resolve(baseErrorAbs);
|
|
5775
|
+
if (isErrorFile && !isBaseErrorFile) {
|
|
5776
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5777
|
+
let classMatch;
|
|
5778
|
+
while ((classMatch = classRegex.exec(content)) !== null) {
|
|
5779
|
+
const className = classMatch[1];
|
|
5780
|
+
const extendsName = classMatch[2];
|
|
5781
|
+
const charIndex = classMatch.index;
|
|
5782
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5783
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || classMatch[0];
|
|
5784
|
+
if (lineCode.startsWith("//")) continue;
|
|
5785
|
+
if (!extendsName) {
|
|
5786
|
+
addViolation({
|
|
5787
|
+
feature: moduleName,
|
|
5788
|
+
layer,
|
|
5789
|
+
file,
|
|
5790
|
+
relativePath: relPath,
|
|
5791
|
+
line: lineNum,
|
|
5792
|
+
category: "Error Inheritance Violation",
|
|
5793
|
+
code: lineCode,
|
|
5794
|
+
ruleId: "E4_MISSING_INHERITANCE",
|
|
5795
|
+
severity: "error",
|
|
5796
|
+
advice: `Error class '${className}' must extend '${baseErrorClassName}' (or another class that derives from it).`,
|
|
5797
|
+
fixExample: {
|
|
5798
|
+
bad: lineCode,
|
|
5799
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5800
|
+
}
|
|
5801
|
+
});
|
|
5802
|
+
} else if (extendsName === "Error") {
|
|
5803
|
+
addViolation({
|
|
5804
|
+
feature: moduleName,
|
|
5805
|
+
layer,
|
|
5806
|
+
file,
|
|
5807
|
+
relativePath: relPath,
|
|
5808
|
+
line: lineNum,
|
|
5809
|
+
category: "Error Inheritance Violation",
|
|
5810
|
+
code: lineCode,
|
|
5811
|
+
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5812
|
+
severity: "error",
|
|
5813
|
+
advice: `Error class '${className}' extends the native 'Error'. It must extend your base error class ('${baseErrorClassName}').`,
|
|
5814
|
+
fixExample: {
|
|
5815
|
+
bad: lineCode,
|
|
5816
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5817
|
+
}
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
if (moduleType === "error") {
|
|
5823
|
+
for (const stmt of statements) {
|
|
5824
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5825
|
+
if (target.targetFeature) {
|
|
5826
|
+
addViolation({
|
|
5827
|
+
feature: "global-errors",
|
|
5828
|
+
layer: "domain",
|
|
5829
|
+
file,
|
|
5830
|
+
relativePath: relPath,
|
|
5831
|
+
line: stmt.line,
|
|
5832
|
+
category: "Global Error Purity Violation",
|
|
5833
|
+
code: stmt.rawCode,
|
|
5834
|
+
ruleId: "E1_FEATURE_DEPENDENCY",
|
|
5835
|
+
severity: "error",
|
|
5836
|
+
advice: `Global errors must not depend on features ('${target.targetFeature}'). Global errors represent pure domain or system states.`,
|
|
5837
|
+
fixExample: {
|
|
5838
|
+
bad: stmt.rawCode,
|
|
5839
|
+
good: "// Global errors must not reference specific features. Keep error types pure."
|
|
5840
|
+
}
|
|
5841
|
+
});
|
|
5842
|
+
}
|
|
5843
|
+
if (target.targetSharedModule && !target.isSharedDomain) {
|
|
5844
|
+
addViolation({
|
|
5845
|
+
feature: "global-errors",
|
|
5846
|
+
layer: "domain",
|
|
5847
|
+
file,
|
|
5848
|
+
relativePath: relPath,
|
|
5849
|
+
line: stmt.line,
|
|
5850
|
+
category: "Global Error Purity Violation",
|
|
5851
|
+
code: stmt.rawCode,
|
|
5852
|
+
ruleId: "E2_SHARED_NON_DOMAIN",
|
|
5853
|
+
severity: "error",
|
|
5854
|
+
advice: `Global errors can only depend on shared domain elements (e.g., value objects). They cannot depend on shared ports or infrastructure.`,
|
|
5855
|
+
fixExample: {
|
|
5856
|
+
bad: stmt.rawCode,
|
|
5857
|
+
good: "// Depend only on pure domain types from shared modules."
|
|
5858
|
+
}
|
|
5859
|
+
});
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
continue;
|
|
5863
|
+
}
|
|
5572
5864
|
if (moduleType === "shared") {
|
|
5573
5865
|
if (layer === "domain") {
|
|
5574
5866
|
const lines = content.split(/\r?\n/);
|
|
@@ -5595,7 +5887,7 @@ Code: ${v.code}`;
|
|
|
5595
5887
|
}
|
|
5596
5888
|
}
|
|
5597
5889
|
for (const stmt of statements) {
|
|
5598
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5890
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5599
5891
|
if (target.targetFeature) {
|
|
5600
5892
|
addViolation({
|
|
5601
5893
|
feature: moduleName,
|
|
@@ -5725,7 +6017,7 @@ Code: ${v.code}`;
|
|
|
5725
6017
|
}
|
|
5726
6018
|
}
|
|
5727
6019
|
for (const stmt of statements) {
|
|
5728
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
6020
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5729
6021
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5730
6022
|
const evidence = {
|
|
5731
6023
|
fromFeature: feat.name,
|
|
@@ -6636,6 +6928,7 @@ exports.getConfiguredSharedDir = getConfiguredSharedDir;
|
|
|
6636
6928
|
exports.getEffectiveMode = getEffectiveMode;
|
|
6637
6929
|
exports.getMissingNpmPackages = getMissingNpmPackages;
|
|
6638
6930
|
exports.getRegistryBase = getRegistryBase;
|
|
6931
|
+
exports.getResolvedLintPaths = getResolvedLintPaths;
|
|
6639
6932
|
exports.getTemplatesDir = getTemplatesDir;
|
|
6640
6933
|
exports.handleCancel = handleCancel;
|
|
6641
6934
|
exports.handleCreate = handleCreate;
|
|
@@ -6658,6 +6951,7 @@ exports.listExistingFeatures = listExistingFeatures;
|
|
|
6658
6951
|
exports.listExistingShared = listExistingShared;
|
|
6659
6952
|
exports.listUseCases = listUseCases;
|
|
6660
6953
|
exports.loadInterfaceHierarchy = loadInterfaceHierarchy;
|
|
6954
|
+
exports.loadLintConfig = loadLintConfig;
|
|
6661
6955
|
exports.loadMasonConfig = loadMasonConfig;
|
|
6662
6956
|
exports.loadTemplate = loadTemplate;
|
|
6663
6957
|
exports.normalizeRegistryUrl = normalizeRegistryUrl;
|