@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/bin.cjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
var cac = require('cac');
|
|
5
5
|
var p19 = require('@clack/prompts');
|
|
6
6
|
var pc13 = require('picocolors');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
7
|
+
var fs17 = require('fs');
|
|
8
|
+
var path26 = require('path');
|
|
9
9
|
var url = require('url');
|
|
10
10
|
var Handlebars = require('handlebars');
|
|
11
11
|
var child_process = require('child_process');
|
|
@@ -34,14 +34,14 @@ function _interopNamespace(e) {
|
|
|
34
34
|
|
|
35
35
|
var p19__namespace = /*#__PURE__*/_interopNamespace(p19);
|
|
36
36
|
var pc13__default = /*#__PURE__*/_interopDefault(pc13);
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var fs17__default = /*#__PURE__*/_interopDefault(fs17);
|
|
38
|
+
var path26__default = /*#__PURE__*/_interopDefault(path26);
|
|
39
39
|
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
40
40
|
|
|
41
41
|
function writeFileSafely(absolutePath, content, options = {}) {
|
|
42
42
|
const projectRoot = options.projectRoot || process.cwd();
|
|
43
|
-
const relPath =
|
|
44
|
-
const existedBefore =
|
|
43
|
+
const relPath = path26__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
|
|
44
|
+
const existedBefore = fs17__default.default.existsSync(absolutePath);
|
|
45
45
|
if (options.dryRun) {
|
|
46
46
|
return {
|
|
47
47
|
relativePath: relPath,
|
|
@@ -60,11 +60,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
|
|
|
60
60
|
existedBefore: true
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
const dir =
|
|
64
|
-
if (!
|
|
65
|
-
|
|
63
|
+
const dir = path26__default.default.dirname(absolutePath);
|
|
64
|
+
if (!fs17__default.default.existsSync(dir)) {
|
|
65
|
+
fs17__default.default.mkdirSync(dir, { recursive: true });
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
fs17__default.default.writeFileSync(absolutePath, content, "utf-8");
|
|
68
68
|
return {
|
|
69
69
|
relativePath: relPath,
|
|
70
70
|
absolutePath,
|
|
@@ -155,17 +155,17 @@ function toStubName(ifaceName) {
|
|
|
155
155
|
return `Stub${base}`;
|
|
156
156
|
}
|
|
157
157
|
function loadMasonConfig(startDir = process.cwd()) {
|
|
158
|
-
let current =
|
|
158
|
+
let current = path26__default.default.resolve(startDir);
|
|
159
159
|
while (true) {
|
|
160
|
-
const configPath =
|
|
161
|
-
if (
|
|
160
|
+
const configPath = path26__default.default.join(current, "mason.config.json");
|
|
161
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
162
162
|
try {
|
|
163
|
-
return JSON.parse(
|
|
163
|
+
return JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
164
164
|
} catch {
|
|
165
165
|
return null;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
const parent =
|
|
168
|
+
const parent = path26__default.default.dirname(current);
|
|
169
169
|
if (parent === current) break;
|
|
170
170
|
current = parent;
|
|
171
171
|
}
|
|
@@ -175,60 +175,91 @@ function getEffectiveMode(projectRoot, flags) {
|
|
|
175
175
|
if (flags?.registry) return "registry";
|
|
176
176
|
const config = loadMasonConfig(projectRoot);
|
|
177
177
|
if (config?.mode) return config.mode;
|
|
178
|
-
if (
|
|
178
|
+
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")))) {
|
|
179
179
|
return "registry";
|
|
180
180
|
}
|
|
181
181
|
return "app";
|
|
182
182
|
}
|
|
183
183
|
function getConfiguredFeaturesDir(projectRoot, flags) {
|
|
184
|
-
if (flags?.target) return
|
|
185
|
-
if (flags?.dest) return
|
|
184
|
+
if (flags?.target) return path26__default.default.resolve(projectRoot, flags.target);
|
|
185
|
+
if (flags?.dest) return path26__default.default.resolve(projectRoot, flags.dest);
|
|
186
186
|
const config = loadMasonConfig(projectRoot);
|
|
187
187
|
if (config?.paths?.features) {
|
|
188
|
-
return
|
|
188
|
+
return path26__default.default.resolve(projectRoot, config.paths.features);
|
|
189
189
|
}
|
|
190
190
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
191
191
|
if (mode === "registry") {
|
|
192
|
-
if (
|
|
193
|
-
return
|
|
192
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/features"))) {
|
|
193
|
+
return path26__default.default.join(projectRoot, "src/features");
|
|
194
194
|
}
|
|
195
|
-
return
|
|
195
|
+
return path26__default.default.join(projectRoot, "features");
|
|
196
196
|
}
|
|
197
|
-
if (
|
|
198
|
-
return
|
|
197
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/features"))) {
|
|
198
|
+
return path26__default.default.join(projectRoot, "src/features");
|
|
199
199
|
}
|
|
200
|
-
return
|
|
200
|
+
return path26__default.default.join(projectRoot, "features");
|
|
201
201
|
}
|
|
202
202
|
function getConfiguredSharedDir(projectRoot, flags) {
|
|
203
|
-
if (flags?.target) return
|
|
204
|
-
if (flags?.dest) return
|
|
203
|
+
if (flags?.target) return path26__default.default.resolve(projectRoot, flags.target);
|
|
204
|
+
if (flags?.dest) return path26__default.default.resolve(projectRoot, flags.dest);
|
|
205
205
|
const config = loadMasonConfig(projectRoot);
|
|
206
206
|
if (config?.paths?.shared) {
|
|
207
|
-
return
|
|
207
|
+
return path26__default.default.resolve(projectRoot, config.paths.shared);
|
|
208
208
|
}
|
|
209
209
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
210
210
|
if (mode === "registry") {
|
|
211
|
-
if (
|
|
212
|
-
return
|
|
211
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/shared"))) {
|
|
212
|
+
return path26__default.default.join(projectRoot, "src/shared");
|
|
213
213
|
}
|
|
214
|
-
return
|
|
214
|
+
return path26__default.default.join(projectRoot, "shared");
|
|
215
215
|
}
|
|
216
|
-
if (
|
|
217
|
-
return
|
|
216
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "src/shared"))) {
|
|
217
|
+
return path26__default.default.join(projectRoot, "src/shared");
|
|
218
218
|
}
|
|
219
|
-
return
|
|
219
|
+
return path26__default.default.join(projectRoot, "shared");
|
|
220
|
+
}
|
|
221
|
+
function loadLintConfig(projectRoot) {
|
|
222
|
+
const configPath = path26__default.default.join(projectRoot, "mason-lint.config.json");
|
|
223
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
function getResolvedLintPaths(projectRoot, config) {
|
|
232
|
+
const masonConfig = loadMasonConfig(projectRoot);
|
|
233
|
+
const configuredFeatures = config.paths?.features ?? masonConfig?.paths?.features;
|
|
234
|
+
const configuredShared = config.paths?.shared ?? masonConfig?.paths?.shared;
|
|
235
|
+
const configuredErrors = config.paths?.errors;
|
|
236
|
+
const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
|
|
237
|
+
if (configuredPath !== void 0) {
|
|
238
|
+
return path26__default.default.resolve(projectRoot, configuredPath);
|
|
239
|
+
}
|
|
240
|
+
const srcPath = path26__default.default.join(projectRoot, defaultSrc);
|
|
241
|
+
if (fs17__default.default.existsSync(srcPath)) return srcPath;
|
|
242
|
+
const rootPath = path26__default.default.join(projectRoot, defaultRoot);
|
|
243
|
+
if (fs17__default.default.existsSync(rootPath)) return rootPath;
|
|
244
|
+
return srcPath;
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
featuresBase: resolveBase(configuredFeatures, "src/features", "features"),
|
|
248
|
+
sharedBase: resolveBase(configuredShared, "src/shared", "shared"),
|
|
249
|
+
errorsBase: resolveBase(configuredErrors, "src/errors", "errors")
|
|
250
|
+
};
|
|
220
251
|
}
|
|
221
252
|
|
|
222
253
|
// src/utils/project.ts
|
|
223
254
|
function findProjectRoot(startDir = process.cwd()) {
|
|
224
|
-
let current =
|
|
255
|
+
let current = path26__default.default.resolve(startDir);
|
|
225
256
|
while (true) {
|
|
226
|
-
if (
|
|
257
|
+
if (fs17__default.default.existsSync(path26__default.default.join(current, "package.json"))) {
|
|
227
258
|
return current;
|
|
228
259
|
}
|
|
229
|
-
const parent =
|
|
260
|
+
const parent = path26__default.default.dirname(current);
|
|
230
261
|
if (parent === current) {
|
|
231
|
-
return
|
|
262
|
+
return path26__default.default.resolve(startDir);
|
|
232
263
|
}
|
|
233
264
|
current = parent;
|
|
234
265
|
}
|
|
@@ -241,9 +272,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
241
272
|
}
|
|
242
273
|
function listExistingFeatures(projectRoot) {
|
|
243
274
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
244
|
-
if (!
|
|
275
|
+
if (!fs17__default.default.existsSync(featuresDir)) return [];
|
|
245
276
|
try {
|
|
246
|
-
const entries =
|
|
277
|
+
const entries = fs17__default.default.readdirSync(featuresDir, { withFileTypes: true });
|
|
247
278
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
248
279
|
} catch {
|
|
249
280
|
return [];
|
|
@@ -253,14 +284,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
253
284
|
const results = [];
|
|
254
285
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
255
286
|
for (const feat of features) {
|
|
256
|
-
const domainDir =
|
|
257
|
-
if (!
|
|
287
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
288
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
258
289
|
try {
|
|
259
|
-
const files =
|
|
290
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
260
291
|
for (const file of files) {
|
|
261
292
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
262
293
|
const name = file.replace(/\.ts$/, "");
|
|
263
|
-
const fullPath =
|
|
294
|
+
const fullPath = path26__default.default.join(domainDir, file);
|
|
264
295
|
if (/^I[A-Z]/.test(name)) {
|
|
265
296
|
results.push({
|
|
266
297
|
name,
|
|
@@ -269,7 +300,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
269
300
|
});
|
|
270
301
|
} else {
|
|
271
302
|
try {
|
|
272
|
-
const content =
|
|
303
|
+
const content = fs17__default.default.readFileSync(fullPath, "utf-8");
|
|
273
304
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
274
305
|
results.push({
|
|
275
306
|
name,
|
|
@@ -291,17 +322,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
291
322
|
const results = [];
|
|
292
323
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
293
324
|
for (const feat of features) {
|
|
294
|
-
const domainDir =
|
|
295
|
-
if (!
|
|
325
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
326
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
296
327
|
try {
|
|
297
|
-
const files =
|
|
328
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
298
329
|
for (const file of files) {
|
|
299
330
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
300
331
|
const name = file.replace(/\.ts$/, "");
|
|
301
332
|
if (!/^I[A-Z]/.test(name)) {
|
|
302
333
|
results.push({
|
|
303
334
|
name,
|
|
304
|
-
fullPath:
|
|
335
|
+
fullPath: path26__default.default.join(domainDir, file),
|
|
305
336
|
feature: feat
|
|
306
337
|
});
|
|
307
338
|
}
|
|
@@ -316,16 +347,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
316
347
|
const results = [];
|
|
317
348
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
318
349
|
for (const feat of features) {
|
|
319
|
-
const useCasesDir =
|
|
320
|
-
if (!
|
|
350
|
+
const useCasesDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
351
|
+
if (!fs17__default.default.existsSync(useCasesDir)) continue;
|
|
321
352
|
try {
|
|
322
|
-
const files =
|
|
353
|
+
const files = fs17__default.default.readdirSync(useCasesDir);
|
|
323
354
|
for (const file of files) {
|
|
324
355
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
325
356
|
const name = file.replace(/\.ts$/, "");
|
|
326
357
|
results.push({
|
|
327
358
|
name,
|
|
328
|
-
fullPath:
|
|
359
|
+
fullPath: path26__default.default.join(useCasesDir, file),
|
|
329
360
|
feature: feat
|
|
330
361
|
});
|
|
331
362
|
}
|
|
@@ -351,33 +382,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
351
382
|
});
|
|
352
383
|
var cachedTemplatesDir = null;
|
|
353
384
|
function getTemplatesDir() {
|
|
354
|
-
if (cachedTemplatesDir &&
|
|
385
|
+
if (cachedTemplatesDir && fs17__default.default.existsSync(cachedTemplatesDir)) {
|
|
355
386
|
return cachedTemplatesDir;
|
|
356
387
|
}
|
|
357
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
358
|
-
const cand1 =
|
|
359
|
-
if (
|
|
388
|
+
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('bin.cjs', document.baseURI).href))));
|
|
389
|
+
const cand1 = path26__default.default.resolve(currentDir, "../../templates");
|
|
390
|
+
if (fs17__default.default.existsSync(cand1)) {
|
|
360
391
|
cachedTemplatesDir = cand1;
|
|
361
392
|
return cand1;
|
|
362
393
|
}
|
|
363
|
-
const cand2 =
|
|
364
|
-
if (
|
|
394
|
+
const cand2 = path26__default.default.resolve(currentDir, "../templates");
|
|
395
|
+
if (fs17__default.default.existsSync(cand2)) {
|
|
365
396
|
cachedTemplatesDir = cand2;
|
|
366
397
|
return cand2;
|
|
367
398
|
}
|
|
368
|
-
const cand3 =
|
|
369
|
-
if (
|
|
399
|
+
const cand3 = path26__default.default.resolve(currentDir, "templates");
|
|
400
|
+
if (fs17__default.default.existsSync(cand3)) {
|
|
370
401
|
cachedTemplatesDir = cand3;
|
|
371
402
|
return cand3;
|
|
372
403
|
}
|
|
373
404
|
let curr = currentDir;
|
|
374
405
|
while (true) {
|
|
375
|
-
const candTemplates =
|
|
376
|
-
if (
|
|
406
|
+
const candTemplates = path26__default.default.join(curr, "templates");
|
|
407
|
+
if (fs17__default.default.existsSync(candTemplates)) {
|
|
377
408
|
cachedTemplatesDir = candTemplates;
|
|
378
409
|
return candTemplates;
|
|
379
410
|
}
|
|
380
|
-
const parent =
|
|
411
|
+
const parent = path26__default.default.dirname(curr);
|
|
381
412
|
if (parent === curr) break;
|
|
382
413
|
curr = parent;
|
|
383
414
|
}
|
|
@@ -385,8 +416,8 @@ function getTemplatesDir() {
|
|
|
385
416
|
}
|
|
386
417
|
var templateCache = /* @__PURE__ */ new Map();
|
|
387
418
|
function loadTemplate(relativePath) {
|
|
388
|
-
const fullPath =
|
|
389
|
-
return
|
|
419
|
+
const fullPath = path26__default.default.isAbsolute(relativePath) ? relativePath : path26__default.default.join(getTemplatesDir(), relativePath);
|
|
420
|
+
return fs17__default.default.readFileSync(fullPath, "utf-8");
|
|
390
421
|
}
|
|
391
422
|
function renderTemplateFile(relativePath, context) {
|
|
392
423
|
let compiled = templateCache.get(relativePath);
|
|
@@ -473,8 +504,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
473
504
|
function generateDomain(options) {
|
|
474
505
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
475
506
|
const entityName = toPascalCase(options.name);
|
|
476
|
-
const targetDir = options.targetDir ||
|
|
477
|
-
const filePath =
|
|
507
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
508
|
+
const filePath = path26__default.default.join(targetDir, `${entityName}.ts`);
|
|
478
509
|
const code = generateDomainEntityCode(
|
|
479
510
|
entityName,
|
|
480
511
|
options.kind || "interface",
|
|
@@ -523,8 +554,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
523
554
|
function generateInterface(options) {
|
|
524
555
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
525
556
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
526
|
-
const targetDir = options.targetDir ||
|
|
527
|
-
const filePath =
|
|
557
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
558
|
+
const filePath = path26__default.default.join(targetDir, `${ifaceName}.ts`);
|
|
528
559
|
const code = generateInterfaceCode(
|
|
529
560
|
ifaceName,
|
|
530
561
|
options.kind || "abstractClass",
|
|
@@ -759,7 +790,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
759
790
|
};
|
|
760
791
|
}
|
|
761
792
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
762
|
-
if (customPath &&
|
|
793
|
+
if (customPath && fs17__default.default.existsSync(customPath)) {
|
|
763
794
|
return { filePath: customPath, ifaceName };
|
|
764
795
|
}
|
|
765
796
|
const candidateNames = [
|
|
@@ -769,22 +800,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
769
800
|
];
|
|
770
801
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
771
802
|
for (const feat of features) {
|
|
772
|
-
const domainDir =
|
|
773
|
-
if (!
|
|
803
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
804
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
774
805
|
for (const cand of candidateNames) {
|
|
775
|
-
const p1 =
|
|
776
|
-
if (
|
|
806
|
+
const p1 = path26__default.default.join(domainDir, `${cand}.ts`);
|
|
807
|
+
if (fs17__default.default.existsSync(p1)) {
|
|
777
808
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
778
809
|
}
|
|
779
810
|
}
|
|
780
811
|
}
|
|
781
812
|
for (const cand of candidateNames) {
|
|
782
|
-
const p1 =
|
|
783
|
-
if (
|
|
784
|
-
const p22 =
|
|
785
|
-
if (
|
|
786
|
-
const p32 =
|
|
787
|
-
if (
|
|
813
|
+
const p1 = path26__default.default.join(projectRoot, "domain", `${cand}.ts`);
|
|
814
|
+
if (fs17__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
|
|
815
|
+
const p22 = path26__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
|
|
816
|
+
if (fs17__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
|
|
817
|
+
const p32 = path26__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
|
|
818
|
+
if (fs17__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
|
|
788
819
|
}
|
|
789
820
|
return null;
|
|
790
821
|
}
|
|
@@ -807,7 +838,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
807
838
|
while (currentFile && !visited.has(currentFile)) {
|
|
808
839
|
visited.add(currentFile);
|
|
809
840
|
try {
|
|
810
|
-
const content =
|
|
841
|
+
const content = fs17__default.default.readFileSync(currentFile, "utf-8");
|
|
811
842
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
812
843
|
for (const m of parsed.methods) {
|
|
813
844
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -883,7 +914,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
883
914
|
const entitiesVar = `${entityVar}s`;
|
|
884
915
|
const idKey = `${entityVar}Id`;
|
|
885
916
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
886
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
917
|
+
const entityImportPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
887
918
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
888
919
|
for (const imp of domainImports) {
|
|
889
920
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -932,7 +963,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
932
963
|
if (baseEntity) {
|
|
933
964
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
934
965
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
935
|
-
const importPath = ifaceImportPath ? `${
|
|
966
|
+
const importPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
936
967
|
finalDomainImports.push({
|
|
937
968
|
name: baseEntity,
|
|
938
969
|
from: importPath,
|
|
@@ -977,8 +1008,8 @@ function generateInfrastructure(options) {
|
|
|
977
1008
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
978
1009
|
const className = toPascalCase(options.name);
|
|
979
1010
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
980
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
981
|
-
const filePath =
|
|
1011
|
+
const targetDir = options.targetDir || (isFeatureScope ? path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path26__default.default.join(projectRoot, "infrastructure"));
|
|
1012
|
+
const filePath = path26__default.default.join(targetDir, `${className}.ts`);
|
|
982
1013
|
let code;
|
|
983
1014
|
if (options.implementsInterface) {
|
|
984
1015
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -992,7 +1023,7 @@ function generateInfrastructure(options) {
|
|
|
992
1023
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
993
1024
|
let ifaceImportPath;
|
|
994
1025
|
if (ifaceFilePath) {
|
|
995
|
-
let rel =
|
|
1026
|
+
let rel = path26__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
996
1027
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
997
1028
|
ifaceImportPath = rel;
|
|
998
1029
|
} else if (isFeatureScope) {
|
|
@@ -1005,8 +1036,8 @@ function generateInfrastructure(options) {
|
|
|
1005
1036
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
1006
1037
|
let fromPath;
|
|
1007
1038
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
1008
|
-
const absTarget =
|
|
1009
|
-
let rel =
|
|
1039
|
+
const absTarget = path26__default.default.resolve(path26__default.default.dirname(ifaceFilePath), imp.specifier);
|
|
1040
|
+
let rel = path26__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
|
|
1010
1041
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1011
1042
|
fromPath = rel;
|
|
1012
1043
|
} else {
|
|
@@ -1193,8 +1224,8 @@ function generateUseCase(options) {
|
|
|
1193
1224
|
const files = [];
|
|
1194
1225
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1195
1226
|
const ucName = toPascalCase(options.name);
|
|
1196
|
-
const targetDir = options.targetDir ||
|
|
1197
|
-
const filePath =
|
|
1227
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
|
|
1228
|
+
const filePath = path26__default.default.join(targetDir, `${ucName}.ts`);
|
|
1198
1229
|
const code = generateUseCaseCode(
|
|
1199
1230
|
ucName,
|
|
1200
1231
|
options.dependencies || [],
|
|
@@ -1210,7 +1241,7 @@ function generateUseCase(options) {
|
|
|
1210
1241
|
})
|
|
1211
1242
|
);
|
|
1212
1243
|
if (options.includeTest) {
|
|
1213
|
-
const testPath =
|
|
1244
|
+
const testPath = path26__default.default.join(targetDir, `${ucName}.test.ts`);
|
|
1214
1245
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1215
1246
|
files.push(
|
|
1216
1247
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1278,10 +1309,10 @@ function generateHttpHandler(options) {
|
|
|
1278
1309
|
const files = [];
|
|
1279
1310
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1280
1311
|
const className = toPascalCase(options.name);
|
|
1281
|
-
const pxDir =
|
|
1282
|
-
const handlersDir =
|
|
1283
|
-
const pxIndexPath =
|
|
1284
|
-
if (!
|
|
1312
|
+
const pxDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
|
|
1313
|
+
const handlersDir = path26__default.default.join(pxDir, "handlers");
|
|
1314
|
+
const pxIndexPath = path26__default.default.join(pxDir, "index.ts");
|
|
1315
|
+
if (!fs17__default.default.existsSync(pxIndexPath)) {
|
|
1285
1316
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1286
1317
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1287
1318
|
routePath: featurePath
|
|
@@ -1294,7 +1325,7 @@ function generateHttpHandler(options) {
|
|
|
1294
1325
|
})
|
|
1295
1326
|
);
|
|
1296
1327
|
}
|
|
1297
|
-
const handlerFilePath =
|
|
1328
|
+
const handlerFilePath = path26__default.default.join(handlersDir, `${className}.ts`);
|
|
1298
1329
|
const code = generateHttpHandlerCode(
|
|
1299
1330
|
className,
|
|
1300
1331
|
options.method || "get",
|
|
@@ -1315,11 +1346,11 @@ function generateHttpHandler(options) {
|
|
|
1315
1346
|
return files;
|
|
1316
1347
|
}
|
|
1317
1348
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1318
|
-
const providerPath =
|
|
1349
|
+
const providerPath = path26__default.default.join(featureDir, "diProvider.ts");
|
|
1319
1350
|
const pascalFeature = toPascalCase(featureName);
|
|
1320
1351
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1321
|
-
if (
|
|
1322
|
-
let content =
|
|
1352
|
+
if (fs17__default.default.existsSync(providerPath) && !options.overwrite) {
|
|
1353
|
+
let content = fs17__default.default.readFileSync(providerPath, "utf-8");
|
|
1323
1354
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1324
1355
|
for (const b of bindings) {
|
|
1325
1356
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1378,7 +1409,7 @@ function generateFeature(options) {
|
|
|
1378
1409
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1379
1410
|
const rawName = options.name.trim();
|
|
1380
1411
|
const featureName = toCamelCase(rawName);
|
|
1381
|
-
const targetDir = options.targetDir ||
|
|
1412
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1382
1413
|
const routePath = options.routePath || `/${featureName}`;
|
|
1383
1414
|
const rawEntities = [];
|
|
1384
1415
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1399,7 +1430,7 @@ function generateFeature(options) {
|
|
|
1399
1430
|
const domainFile = generateDomain({
|
|
1400
1431
|
name: ent,
|
|
1401
1432
|
feature: featureName,
|
|
1402
|
-
targetDir:
|
|
1433
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1403
1434
|
projectRoot,
|
|
1404
1435
|
dryRun: options.dryRun,
|
|
1405
1436
|
overwrite: options.overwrite,
|
|
@@ -1419,7 +1450,7 @@ function generateFeature(options) {
|
|
|
1419
1450
|
const ifaceFile = generateInterface({
|
|
1420
1451
|
name: ifaceName,
|
|
1421
1452
|
feature: featureName,
|
|
1422
|
-
targetDir:
|
|
1453
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1423
1454
|
entityName: ent,
|
|
1424
1455
|
projectRoot,
|
|
1425
1456
|
dryRun: options.dryRun,
|
|
@@ -1436,7 +1467,7 @@ function generateFeature(options) {
|
|
|
1436
1467
|
const stubFile = generateInfrastructure({
|
|
1437
1468
|
name: stubName,
|
|
1438
1469
|
feature: featureName,
|
|
1439
|
-
targetDir:
|
|
1470
|
+
targetDir: path26__default.default.join(targetDir, "infrastructure"),
|
|
1440
1471
|
kind: "stub",
|
|
1441
1472
|
implementsInterface: ifaceName,
|
|
1442
1473
|
entityName: ent,
|
|
@@ -1478,7 +1509,7 @@ function generateFeature(options) {
|
|
|
1478
1509
|
const ucFiles = generateUseCase({
|
|
1479
1510
|
name: ucName,
|
|
1480
1511
|
feature: featureName,
|
|
1481
|
-
targetDir:
|
|
1512
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1482
1513
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1483
1514
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1484
1515
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1523,7 +1554,7 @@ function generateFeature(options) {
|
|
|
1523
1554
|
const ucFiles = generateUseCase({
|
|
1524
1555
|
name: ucName,
|
|
1525
1556
|
feature: featureName,
|
|
1526
|
-
targetDir:
|
|
1557
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1527
1558
|
dependencies: deps,
|
|
1528
1559
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1529
1560
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1556,7 +1587,7 @@ function generateFeature(options) {
|
|
|
1556
1587
|
const ucFiles = generateUseCase({
|
|
1557
1588
|
name: ucName,
|
|
1558
1589
|
feature: featureName,
|
|
1559
|
-
targetDir:
|
|
1590
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1560
1591
|
dependencies: deps,
|
|
1561
1592
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1562
1593
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1589,7 +1620,7 @@ function generateFeature(options) {
|
|
|
1589
1620
|
const ucFiles = generateUseCase({
|
|
1590
1621
|
name: ucName,
|
|
1591
1622
|
feature: featureName,
|
|
1592
|
-
targetDir:
|
|
1623
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1593
1624
|
dependencies: deps,
|
|
1594
1625
|
inputProperties: [
|
|
1595
1626
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1633,7 +1664,7 @@ function generateFeature(options) {
|
|
|
1633
1664
|
const ucFiles = generateUseCase({
|
|
1634
1665
|
name: ucName,
|
|
1635
1666
|
feature: featureName,
|
|
1636
|
-
targetDir:
|
|
1667
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1637
1668
|
dependencies: deps,
|
|
1638
1669
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1639
1670
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1666,7 +1697,7 @@ function generateFeature(options) {
|
|
|
1666
1697
|
const ucFiles = generateUseCase({
|
|
1667
1698
|
name: ucName,
|
|
1668
1699
|
feature: featureName,
|
|
1669
|
-
targetDir:
|
|
1700
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1670
1701
|
dependencies: deps,
|
|
1671
1702
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1672
1703
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -1703,7 +1734,7 @@ function generateFeature(options) {
|
|
|
1703
1734
|
const ucFiles = generateUseCase({
|
|
1704
1735
|
name: ucName,
|
|
1705
1736
|
feature: featureName,
|
|
1706
|
-
targetDir:
|
|
1737
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1707
1738
|
dependencies: customUc.dependencies || [],
|
|
1708
1739
|
inputProperties: customUc.inputProperties || [],
|
|
1709
1740
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -1735,8 +1766,8 @@ function generateFeature(options) {
|
|
|
1735
1766
|
}
|
|
1736
1767
|
}
|
|
1737
1768
|
if (options.includeExpress !== false) {
|
|
1738
|
-
const pxDir =
|
|
1739
|
-
const pxIndexPath =
|
|
1769
|
+
const pxDir = path26__default.default.join(targetDir, "pxExpress");
|
|
1770
|
+
const pxIndexPath = path26__default.default.join(pxDir, "index.ts");
|
|
1740
1771
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
1741
1772
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
1742
1773
|
files.push(
|
|
@@ -1866,7 +1897,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1866
1897
|
}
|
|
1867
1898
|
const normalizedName = toCamelCase(name);
|
|
1868
1899
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
1869
|
-
const defaultDir =
|
|
1900
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseFeaturesDir, normalizedName));
|
|
1870
1901
|
let targetDirRel = defaultDir;
|
|
1871
1902
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
1872
1903
|
targetDirRel = handleCancel(
|
|
@@ -1877,7 +1908,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1877
1908
|
})
|
|
1878
1909
|
).trim();
|
|
1879
1910
|
}
|
|
1880
|
-
const targetDir = flags.dest ?
|
|
1911
|
+
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);
|
|
1881
1912
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
1882
1913
|
let routePath = `/${normalizedName}`;
|
|
1883
1914
|
let includeDomain = true;
|
|
@@ -2119,8 +2150,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2119
2150
|
};
|
|
2120
2151
|
const files = generateFeature(options);
|
|
2121
2152
|
if (isRegistry) {
|
|
2122
|
-
const registryPath =
|
|
2123
|
-
if (!
|
|
2153
|
+
const registryPath = path26__default.default.join(targetDir, "registry.json");
|
|
2154
|
+
if (!fs17__default.default.existsSync(registryPath)) {
|
|
2124
2155
|
const manifest = {
|
|
2125
2156
|
name: toKebabCase(normalizedName),
|
|
2126
2157
|
type: "feature",
|
|
@@ -2146,7 +2177,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2146
2177
|
printGeneratedFiles(files, options.dryRun);
|
|
2147
2178
|
p19__namespace.outro(
|
|
2148
2179
|
pc13__default.default.green(
|
|
2149
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2180
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2150
2181
|
)
|
|
2151
2182
|
);
|
|
2152
2183
|
}
|
|
@@ -2157,7 +2188,7 @@ function generateShared(options) {
|
|
|
2157
2188
|
const pascalName = toPascalCase(rawName);
|
|
2158
2189
|
const camelName = toCamelCase(rawName);
|
|
2159
2190
|
const kebabName = toKebabCase(rawName);
|
|
2160
|
-
const targetDir = options.targetDir ||
|
|
2191
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveSharedDir(projectRoot), kebabName);
|
|
2161
2192
|
const fileDefs = [];
|
|
2162
2193
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2163
2194
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2169,7 +2200,7 @@ export class ${pascalName} {
|
|
|
2169
2200
|
}
|
|
2170
2201
|
`;
|
|
2171
2202
|
fileDefs.push({
|
|
2172
|
-
absolutePath:
|
|
2203
|
+
absolutePath: path26__default.default.join(targetDir, `${pascalName}.ts`),
|
|
2173
2204
|
content: facadeContent
|
|
2174
2205
|
});
|
|
2175
2206
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2177,7 +2208,7 @@ export class ${pascalName} {
|
|
|
2177
2208
|
}
|
|
2178
2209
|
`;
|
|
2179
2210
|
fileDefs.push({
|
|
2180
|
-
absolutePath:
|
|
2211
|
+
absolutePath: path26__default.default.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2181
2212
|
content: portContent
|
|
2182
2213
|
});
|
|
2183
2214
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2189,7 +2220,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2189
2220
|
}
|
|
2190
2221
|
`;
|
|
2191
2222
|
fileDefs.push({
|
|
2192
|
-
absolutePath:
|
|
2223
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2193
2224
|
content: concreteContent
|
|
2194
2225
|
});
|
|
2195
2226
|
if (options.includeStub !== false) {
|
|
@@ -2202,7 +2233,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2202
2233
|
}
|
|
2203
2234
|
`;
|
|
2204
2235
|
fileDefs.push({
|
|
2205
|
-
absolutePath:
|
|
2236
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2206
2237
|
content: stubContent
|
|
2207
2238
|
});
|
|
2208
2239
|
}
|
|
@@ -2217,7 +2248,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2217
2248
|
export default diProvider;
|
|
2218
2249
|
`;
|
|
2219
2250
|
fileDefs.push({
|
|
2220
|
-
absolutePath:
|
|
2251
|
+
absolutePath: path26__default.default.join(targetDir, "diProvider.ts"),
|
|
2221
2252
|
content: providerContent
|
|
2222
2253
|
});
|
|
2223
2254
|
if (options.isRegistry) {
|
|
@@ -2234,7 +2265,7 @@ export default diProvider;
|
|
|
2234
2265
|
files: []
|
|
2235
2266
|
};
|
|
2236
2267
|
fileDefs.push({
|
|
2237
|
-
absolutePath:
|
|
2268
|
+
absolutePath: path26__default.default.join(targetDir, "registry.json"),
|
|
2238
2269
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2239
2270
|
});
|
|
2240
2271
|
}
|
|
@@ -2271,7 +2302,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2271
2302
|
}
|
|
2272
2303
|
const normalizedName = toKebabCase(name);
|
|
2273
2304
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2274
|
-
const defaultDir =
|
|
2305
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseSharedDir, normalizedName));
|
|
2275
2306
|
let targetDirRel = defaultDir;
|
|
2276
2307
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2277
2308
|
targetDirRel = handleCancel(
|
|
@@ -2282,7 +2313,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2282
2313
|
})
|
|
2283
2314
|
).trim();
|
|
2284
2315
|
}
|
|
2285
|
-
const targetDir = flags.dest ?
|
|
2316
|
+
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);
|
|
2286
2317
|
let description = flags.description || "";
|
|
2287
2318
|
let includeStub = true;
|
|
2288
2319
|
if (!flags.yes) {
|
|
@@ -2316,7 +2347,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2316
2347
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2317
2348
|
p19__namespace.outro(
|
|
2318
2349
|
pc13__default.default.green(
|
|
2319
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2350
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2320
2351
|
)
|
|
2321
2352
|
);
|
|
2322
2353
|
}
|
|
@@ -2743,7 +2774,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2743
2774
|
const infraFile = generateInfrastructure(options);
|
|
2744
2775
|
files.push(infraFile);
|
|
2745
2776
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
2746
|
-
const featureDir =
|
|
2777
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
2747
2778
|
const providerFile = updateOrGenerateDiProvider(
|
|
2748
2779
|
featureDir,
|
|
2749
2780
|
feature,
|
|
@@ -2905,7 +2936,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
2905
2936
|
files.push(stubFile);
|
|
2906
2937
|
}
|
|
2907
2938
|
if (registerInProvider && generateStub) {
|
|
2908
|
-
const featureDir =
|
|
2939
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
2909
2940
|
const providerFile = updateOrGenerateDiProvider(
|
|
2910
2941
|
featureDir,
|
|
2911
2942
|
feature,
|
|
@@ -3093,21 +3124,21 @@ function generateInjectable(options) {
|
|
|
3093
3124
|
const className = toPascalCase(options.name);
|
|
3094
3125
|
let targetDir = "";
|
|
3095
3126
|
if (options.customPath) {
|
|
3096
|
-
targetDir =
|
|
3127
|
+
targetDir = path26__default.default.isAbsolute(options.customPath) ? options.customPath : path26__default.default.join(projectRoot, options.customPath);
|
|
3097
3128
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3098
|
-
targetDir =
|
|
3129
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3099
3130
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3100
|
-
targetDir =
|
|
3131
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3101
3132
|
} else if (options.location === "module") {
|
|
3102
|
-
targetDir =
|
|
3133
|
+
targetDir = path26__default.default.join(projectRoot, "modules", className);
|
|
3103
3134
|
} else {
|
|
3104
|
-
targetDir = options.feature ?
|
|
3135
|
+
targetDir = options.feature ? path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26__default.default.join(projectRoot, "shared", className.toLowerCase());
|
|
3105
3136
|
}
|
|
3106
|
-
const filePath =
|
|
3137
|
+
const filePath = path26__default.default.join(targetDir, `${className}.ts`);
|
|
3107
3138
|
let ifaceName;
|
|
3108
3139
|
if (options.generateInterface) {
|
|
3109
3140
|
ifaceName = `I${className}`;
|
|
3110
|
-
const ifacePath =
|
|
3141
|
+
const ifacePath = path26__default.default.join(targetDir, `${ifaceName}.ts`);
|
|
3111
3142
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3112
3143
|
files.push(
|
|
3113
3144
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3236,7 +3267,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3236
3267
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3237
3268
|
const s = p19__namespace.spinner();
|
|
3238
3269
|
s.start("Scanning for .example environment files...");
|
|
3239
|
-
const files =
|
|
3270
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3240
3271
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3241
3272
|
if (exampleFiles.length === 0) {
|
|
3242
3273
|
s.stop("No .example files found.");
|
|
@@ -3246,11 +3277,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3246
3277
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3247
3278
|
const generatedFiles = [];
|
|
3248
3279
|
for (const exampleFile of exampleFiles) {
|
|
3249
|
-
const sourcePath =
|
|
3280
|
+
const sourcePath = path26__default.default.join(projectRoot, exampleFile);
|
|
3250
3281
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3251
|
-
const targetPath =
|
|
3282
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3252
3283
|
let overwrite = flags.overwrite;
|
|
3253
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3284
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17__default.default.existsSync(targetPath)) {
|
|
3254
3285
|
overwrite = await p19__namespace.confirm({
|
|
3255
3286
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3256
3287
|
initialValue: false
|
|
@@ -3260,7 +3291,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3260
3291
|
process.exit(0);
|
|
3261
3292
|
}
|
|
3262
3293
|
}
|
|
3263
|
-
const content =
|
|
3294
|
+
const content = fs17__default.default.readFileSync(sourcePath, "utf-8");
|
|
3264
3295
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3265
3296
|
projectRoot,
|
|
3266
3297
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3278,7 +3309,7 @@ var EnvEditor = class {
|
|
|
3278
3309
|
filePath;
|
|
3279
3310
|
constructor(filePath) {
|
|
3280
3311
|
this.filePath = filePath;
|
|
3281
|
-
this.lines =
|
|
3312
|
+
this.lines = fs17__default.default.existsSync(filePath) ? fs17__default.default.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3282
3313
|
}
|
|
3283
3314
|
getLines() {
|
|
3284
3315
|
return [...this.lines];
|
|
@@ -3342,18 +3373,18 @@ var EnvEditor = class {
|
|
|
3342
3373
|
});
|
|
3343
3374
|
}
|
|
3344
3375
|
save() {
|
|
3345
|
-
const dir =
|
|
3346
|
-
if (!
|
|
3347
|
-
|
|
3376
|
+
const dir = path26__default.default.dirname(this.filePath);
|
|
3377
|
+
if (!fs17__default.default.existsSync(dir)) {
|
|
3378
|
+
fs17__default.default.mkdirSync(dir, { recursive: true });
|
|
3348
3379
|
}
|
|
3349
|
-
|
|
3380
|
+
fs17__default.default.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3350
3381
|
}
|
|
3351
3382
|
};
|
|
3352
3383
|
function discoverEnvironments(projectRoot) {
|
|
3353
|
-
if (!
|
|
3384
|
+
if (!fs17__default.default.existsSync(projectRoot)) {
|
|
3354
3385
|
return ["local", "dev", "prod"];
|
|
3355
3386
|
}
|
|
3356
|
-
const files =
|
|
3387
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3357
3388
|
const envs = /* @__PURE__ */ new Set();
|
|
3358
3389
|
for (const file of files) {
|
|
3359
3390
|
if (file.startsWith(".env")) {
|
|
@@ -3378,7 +3409,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3378
3409
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3379
3410
|
const s = p19__namespace.spinner();
|
|
3380
3411
|
s.start("Scanning for local environment files...");
|
|
3381
|
-
const files =
|
|
3412
|
+
const files = fs17__default.default.readdirSync(projectRoot, { withFileTypes: true });
|
|
3382
3413
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3383
3414
|
if (envFiles.length === 0) {
|
|
3384
3415
|
s.stop("No local .env files found.");
|
|
@@ -3388,11 +3419,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3388
3419
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3389
3420
|
const generatedFiles = [];
|
|
3390
3421
|
for (const envFile of envFiles) {
|
|
3391
|
-
const sourcePath =
|
|
3422
|
+
const sourcePath = path26__default.default.join(projectRoot, envFile);
|
|
3392
3423
|
const targetFile = `${envFile}.example`;
|
|
3393
|
-
const targetPath =
|
|
3424
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3394
3425
|
let overwrite = flags.overwrite;
|
|
3395
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3426
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17__default.default.existsSync(targetPath)) {
|
|
3396
3427
|
overwrite = await p19__namespace.confirm({
|
|
3397
3428
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3398
3429
|
initialValue: true
|
|
@@ -3425,12 +3456,12 @@ function isInteractive(flags) {
|
|
|
3425
3456
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3426
3457
|
const keys = /* @__PURE__ */ new Set();
|
|
3427
3458
|
for (const env of envs) {
|
|
3428
|
-
const localPath =
|
|
3429
|
-
const examplePath =
|
|
3430
|
-
if (
|
|
3459
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3460
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3461
|
+
if (fs17__default.default.existsSync(localPath)) {
|
|
3431
3462
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3432
3463
|
}
|
|
3433
|
-
if (
|
|
3464
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3434
3465
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3435
3466
|
}
|
|
3436
3467
|
}
|
|
@@ -3502,8 +3533,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3502
3533
|
})
|
|
3503
3534
|
);
|
|
3504
3535
|
}
|
|
3505
|
-
const localPath =
|
|
3506
|
-
const examplePath =
|
|
3536
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3537
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3507
3538
|
if (flags.dryRun) {
|
|
3508
3539
|
p19__namespace.log.info(
|
|
3509
3540
|
pc13__default.default.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3550,8 +3581,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3550
3581
|
);
|
|
3551
3582
|
}
|
|
3552
3583
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3553
|
-
const localPath =
|
|
3554
|
-
return
|
|
3584
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3585
|
+
return fs17__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3555
3586
|
});
|
|
3556
3587
|
let targetEnvs = [];
|
|
3557
3588
|
if (flags.env) {
|
|
@@ -3577,7 +3608,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3577
3608
|
return { key: targetKey, environments: [] };
|
|
3578
3609
|
}
|
|
3579
3610
|
for (const env of targetEnvs) {
|
|
3580
|
-
const localPath =
|
|
3611
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3581
3612
|
const localEditor = new EnvEditor(localPath);
|
|
3582
3613
|
const currentVal = localEditor.getValue(targetKey);
|
|
3583
3614
|
let newVal = value;
|
|
@@ -3607,7 +3638,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3607
3638
|
} else {
|
|
3608
3639
|
localEditor.set(targetKey, newVal);
|
|
3609
3640
|
localEditor.save();
|
|
3610
|
-
const examplePath =
|
|
3641
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3611
3642
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3612
3643
|
if (!exampleEditor.has(targetKey)) {
|
|
3613
3644
|
exampleEditor.set(targetKey, "");
|
|
@@ -3643,9 +3674,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3643
3674
|
);
|
|
3644
3675
|
}
|
|
3645
3676
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3646
|
-
const localPath =
|
|
3647
|
-
const examplePath =
|
|
3648
|
-
return
|
|
3677
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3678
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3679
|
+
return fs17__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs17__default.default.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
|
|
3649
3680
|
});
|
|
3650
3681
|
let targetEnvs = [];
|
|
3651
3682
|
if (flags.env) {
|
|
@@ -3683,19 +3714,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3683
3714
|
}
|
|
3684
3715
|
}
|
|
3685
3716
|
for (const env of targetEnvs) {
|
|
3686
|
-
const localPath =
|
|
3687
|
-
const examplePath =
|
|
3717
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3718
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3688
3719
|
if (flags.dryRun) {
|
|
3689
3720
|
p19__namespace.log.info(
|
|
3690
3721
|
pc13__default.default.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3691
3722
|
);
|
|
3692
3723
|
} else {
|
|
3693
|
-
if (
|
|
3724
|
+
if (fs17__default.default.existsSync(localPath)) {
|
|
3694
3725
|
const localEditor = new EnvEditor(localPath);
|
|
3695
3726
|
localEditor.delete(targetKey);
|
|
3696
3727
|
localEditor.save();
|
|
3697
3728
|
}
|
|
3698
|
-
if (
|
|
3729
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3699
3730
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3700
3731
|
exampleEditor.delete(targetKey);
|
|
3701
3732
|
exampleEditor.save();
|
|
@@ -3720,10 +3751,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
3720
3751
|
for (const env of envs) {
|
|
3721
3752
|
const localFile = resolveEnvFilename(env, false);
|
|
3722
3753
|
const exampleFile = resolveEnvFilename(env, true);
|
|
3723
|
-
const localPath =
|
|
3724
|
-
const examplePath =
|
|
3725
|
-
const localExists =
|
|
3726
|
-
const exampleExists =
|
|
3754
|
+
const localPath = path26__default.default.join(projectRoot, localFile);
|
|
3755
|
+
const examplePath = path26__default.default.join(projectRoot, exampleFile);
|
|
3756
|
+
const localExists = fs17__default.default.existsSync(localPath);
|
|
3757
|
+
const exampleExists = fs17__default.default.existsSync(examplePath);
|
|
3727
3758
|
const localEditor = new EnvEditor(localPath);
|
|
3728
3759
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3729
3760
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4050,25 +4081,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
4050
4081
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
4051
4082
|
}
|
|
4052
4083
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
4053
|
-
return
|
|
4084
|
+
return path26__default.default.resolve(baseDir, trimmed);
|
|
4054
4085
|
}
|
|
4055
4086
|
return trimmed;
|
|
4056
4087
|
}
|
|
4057
4088
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
4058
|
-
let current =
|
|
4089
|
+
let current = path26__default.default.resolve(startDir);
|
|
4059
4090
|
while (true) {
|
|
4060
|
-
const candidate =
|
|
4061
|
-
if (
|
|
4091
|
+
const candidate = path26__default.default.join(current, "mason-registry");
|
|
4092
|
+
if (fs17__default.default.existsSync(candidate) && fs17__default.default.existsSync(path26__default.default.join(candidate, "index.json"))) {
|
|
4062
4093
|
return candidate;
|
|
4063
4094
|
}
|
|
4064
|
-
const parent =
|
|
4095
|
+
const parent = path26__default.default.dirname(current);
|
|
4065
4096
|
if (parent === current) break;
|
|
4066
4097
|
current = parent;
|
|
4067
4098
|
}
|
|
4068
4099
|
return null;
|
|
4069
4100
|
}
|
|
4070
4101
|
function getRegistryBase(projectRoot, override) {
|
|
4071
|
-
const rootDir = projectRoot ?
|
|
4102
|
+
const rootDir = projectRoot ? path26__default.default.resolve(projectRoot) : process.cwd();
|
|
4072
4103
|
if (override && override.trim()) {
|
|
4073
4104
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
4074
4105
|
}
|
|
@@ -4076,30 +4107,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
4076
4107
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
4077
4108
|
}
|
|
4078
4109
|
const configLocations = [
|
|
4079
|
-
|
|
4080
|
-
|
|
4110
|
+
path26__default.default.join(rootDir, "mason.config.json"),
|
|
4111
|
+
path26__default.default.join(process.cwd(), "mason.config.json")
|
|
4081
4112
|
];
|
|
4082
4113
|
for (const configPath of configLocations) {
|
|
4083
|
-
if (
|
|
4114
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
4084
4115
|
try {
|
|
4085
|
-
const config = JSON.parse(
|
|
4116
|
+
const config = JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
4086
4117
|
if (config.registry && typeof config.registry === "string") {
|
|
4087
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
4118
|
+
return normalizeRegistryUrl(config.registry.trim(), path26__default.default.dirname(configPath));
|
|
4088
4119
|
}
|
|
4089
4120
|
} catch {
|
|
4090
4121
|
}
|
|
4091
4122
|
}
|
|
4092
4123
|
}
|
|
4093
4124
|
const pkgLocations = [
|
|
4094
|
-
|
|
4095
|
-
|
|
4125
|
+
path26__default.default.join(rootDir, "package.json"),
|
|
4126
|
+
path26__default.default.join(process.cwd(), "package.json")
|
|
4096
4127
|
];
|
|
4097
4128
|
for (const pkgPath of pkgLocations) {
|
|
4098
|
-
if (
|
|
4129
|
+
if (fs17__default.default.existsSync(pkgPath)) {
|
|
4099
4130
|
try {
|
|
4100
|
-
const pkg = JSON.parse(
|
|
4131
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgPath, "utf-8"));
|
|
4101
4132
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
4102
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
4133
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26__default.default.dirname(pkgPath));
|
|
4103
4134
|
}
|
|
4104
4135
|
} catch {
|
|
4105
4136
|
}
|
|
@@ -4147,16 +4178,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
4147
4178
|
if (baseDir.startsWith("file://")) {
|
|
4148
4179
|
baseDir = url.fileURLToPath(baseDir);
|
|
4149
4180
|
}
|
|
4150
|
-
let filePath =
|
|
4151
|
-
if (!
|
|
4152
|
-
const inSrcPath =
|
|
4153
|
-
if (
|
|
4181
|
+
let filePath = path26__default.default.resolve(baseDir, cleanRelPath);
|
|
4182
|
+
if (!fs17__default.default.existsSync(filePath)) {
|
|
4183
|
+
const inSrcPath = path26__default.default.resolve(baseDir, "src", cleanRelPath);
|
|
4184
|
+
if (fs17__default.default.existsSync(inSrcPath)) {
|
|
4154
4185
|
filePath = inSrcPath;
|
|
4155
4186
|
} else {
|
|
4156
4187
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
4157
4188
|
}
|
|
4158
4189
|
}
|
|
4159
|
-
return
|
|
4190
|
+
return fs17__default.default.promises.readFile(filePath, "utf-8");
|
|
4160
4191
|
}
|
|
4161
4192
|
async function fetchRegistryIndex(registryBase) {
|
|
4162
4193
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -4218,19 +4249,19 @@ function findClosestMatch(target, candidates) {
|
|
|
4218
4249
|
return null;
|
|
4219
4250
|
}
|
|
4220
4251
|
function detectPackageManager(projectRoot) {
|
|
4221
|
-
if (
|
|
4222
|
-
if (
|
|
4223
|
-
if (
|
|
4252
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
4253
|
+
if (fs17__default.default.existsSync(path26__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
4254
|
+
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";
|
|
4224
4255
|
return "npm";
|
|
4225
4256
|
}
|
|
4226
4257
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
4227
4258
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
4228
|
-
const pkgJsonPath =
|
|
4229
|
-
if (!
|
|
4259
|
+
const pkgJsonPath = path26__default.default.join(projectRoot, "package.json");
|
|
4260
|
+
if (!fs17__default.default.existsSync(pkgJsonPath)) {
|
|
4230
4261
|
return requiredPackages;
|
|
4231
4262
|
}
|
|
4232
4263
|
try {
|
|
4233
|
-
const pkg = JSON.parse(
|
|
4264
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgJsonPath, "utf-8"));
|
|
4234
4265
|
const installed = /* @__PURE__ */ new Set([
|
|
4235
4266
|
...Object.keys(pkg.dependencies || {}),
|
|
4236
4267
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -4269,12 +4300,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
4269
4300
|
});
|
|
4270
4301
|
}
|
|
4271
4302
|
function getAllFiles(dir, baseDir = dir) {
|
|
4272
|
-
if (!
|
|
4273
|
-
const entries =
|
|
4303
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
4304
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4274
4305
|
const files = [];
|
|
4275
4306
|
for (const entry of entries) {
|
|
4276
|
-
const fullPath =
|
|
4277
|
-
const relPath =
|
|
4307
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
4308
|
+
const relPath = path26__default.default.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
4278
4309
|
if (entry.isDirectory()) {
|
|
4279
4310
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4280
4311
|
continue;
|
|
@@ -4290,13 +4321,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
4290
4321
|
return files.sort();
|
|
4291
4322
|
}
|
|
4292
4323
|
function computeDirectoryHash(dirPath, files) {
|
|
4293
|
-
if (!
|
|
4324
|
+
if (!fs17__default.default.existsSync(dirPath)) return "";
|
|
4294
4325
|
const hash = crypto.createHash("sha256");
|
|
4295
4326
|
const fileList = (getAllFiles(dirPath)).sort();
|
|
4296
4327
|
for (const relativePath of fileList) {
|
|
4297
|
-
const absolutePath =
|
|
4298
|
-
if (!
|
|
4299
|
-
const content =
|
|
4328
|
+
const absolutePath = path26__default.default.join(dirPath, relativePath);
|
|
4329
|
+
if (!fs17__default.default.existsSync(absolutePath)) continue;
|
|
4330
|
+
const content = fs17__default.default.readFileSync(absolutePath, "utf8");
|
|
4300
4331
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
4301
4332
|
hash.update(relativePath);
|
|
4302
4333
|
hash.update(normalizedContent);
|
|
@@ -4334,17 +4365,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4334
4365
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4335
4366
|
}
|
|
4336
4367
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4337
|
-
return
|
|
4368
|
+
return path26__default.default.join(projectRoot, "src", folder);
|
|
4338
4369
|
}
|
|
4339
4370
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4340
4371
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4341
4372
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4342
|
-
const inConfigured =
|
|
4343
|
-
if (
|
|
4344
|
-
const inSrc =
|
|
4345
|
-
if (
|
|
4346
|
-
const inRoot =
|
|
4347
|
-
if (
|
|
4373
|
+
const inConfigured = path26__default.default.join(configuredDir, modName);
|
|
4374
|
+
if (fs17__default.default.existsSync(inConfigured)) return inConfigured;
|
|
4375
|
+
const inSrc = path26__default.default.join(projectRoot, "src", folder, modName);
|
|
4376
|
+
if (fs17__default.default.existsSync(inSrc)) return inSrc;
|
|
4377
|
+
const inRoot = path26__default.default.join(projectRoot, folder, modName);
|
|
4378
|
+
if (fs17__default.default.existsSync(inRoot)) return inRoot;
|
|
4348
4379
|
return null;
|
|
4349
4380
|
}
|
|
4350
4381
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4355,14 +4386,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4355
4386
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4356
4387
|
const dirs = [
|
|
4357
4388
|
configuredDir,
|
|
4358
|
-
|
|
4359
|
-
|
|
4389
|
+
path26__default.default.join(projectRoot, "src", folder),
|
|
4390
|
+
path26__default.default.join(projectRoot, folder)
|
|
4360
4391
|
];
|
|
4361
4392
|
const set = /* @__PURE__ */ new Set();
|
|
4362
4393
|
for (const dir of dirs) {
|
|
4363
|
-
if (
|
|
4394
|
+
if (fs17__default.default.existsSync(dir)) {
|
|
4364
4395
|
try {
|
|
4365
|
-
const entries =
|
|
4396
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4366
4397
|
for (const e of entries) {
|
|
4367
4398
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4368
4399
|
set.add(e.name);
|
|
@@ -4564,7 +4595,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4564
4595
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4565
4596
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4566
4597
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4567
|
-
const displayDest =
|
|
4598
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, programmaticAlias));
|
|
4568
4599
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4569
4600
|
resolutions.set(key, {
|
|
4570
4601
|
type: modType,
|
|
@@ -4752,7 +4783,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4752
4783
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4753
4784
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4754
4785
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4755
|
-
const displayDest =
|
|
4786
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, newName));
|
|
4756
4787
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4757
4788
|
resolutions.set(key, {
|
|
4758
4789
|
type: modType,
|
|
@@ -4804,7 +4835,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4804
4835
|
continue;
|
|
4805
4836
|
}
|
|
4806
4837
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4807
|
-
const targetDir =
|
|
4838
|
+
const targetDir = path26__default.default.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4808
4839
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4809
4840
|
if (key !== rootKey) {
|
|
4810
4841
|
p19__namespace.log.step(
|
|
@@ -4814,30 +4845,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4814
4845
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4815
4846
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4816
4847
|
if (existingDir && existingDir !== targetDir) {
|
|
4817
|
-
|
|
4848
|
+
fs17__default.default.rmSync(existingDir, { recursive: true, force: true });
|
|
4818
4849
|
}
|
|
4819
|
-
if (
|
|
4820
|
-
|
|
4850
|
+
if (fs17__default.default.existsSync(targetDir)) {
|
|
4851
|
+
fs17__default.default.rmSync(targetDir, { recursive: true, force: true });
|
|
4821
4852
|
}
|
|
4822
4853
|
}
|
|
4823
4854
|
for (const file of res.manifest.files || []) {
|
|
4824
4855
|
if (file === "registry.json") continue;
|
|
4825
4856
|
if (!includeTests && isTestFile(file)) continue;
|
|
4826
4857
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4827
|
-
const localFilePath =
|
|
4828
|
-
if (
|
|
4858
|
+
const localFilePath = path26__default.default.join(targetDir, file);
|
|
4859
|
+
if (fs17__default.default.existsSync(localFilePath) && !isOverwrite) {
|
|
4829
4860
|
p19__namespace.log.warn(
|
|
4830
|
-
` \u21B3 Skipping existing file: ${pc13__default.default.dim(
|
|
4861
|
+
` \u21B3 Skipping existing file: ${pc13__default.default.dim(path26__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4831
4862
|
);
|
|
4832
4863
|
continue;
|
|
4833
4864
|
}
|
|
4834
4865
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4835
4866
|
content = rewriteImports(content, globalAliasMap);
|
|
4836
4867
|
if (!flags.dryRun) {
|
|
4837
|
-
|
|
4838
|
-
|
|
4868
|
+
fs17__default.default.mkdirSync(path26__default.default.dirname(localFilePath), { recursive: true });
|
|
4869
|
+
fs17__default.default.writeFileSync(localFilePath, content, "utf-8");
|
|
4839
4870
|
}
|
|
4840
|
-
const displayPath =
|
|
4871
|
+
const displayPath = path26__default.default.relative(projectRoot, localFilePath);
|
|
4841
4872
|
totalWrittenFiles.push(displayPath);
|
|
4842
4873
|
p19__namespace.log.message(` ${pc13__default.default.green("+")} ${displayPath}`);
|
|
4843
4874
|
}
|
|
@@ -4863,7 +4894,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4863
4894
|
} else {
|
|
4864
4895
|
const rootRes = resolutions.get(rootKey);
|
|
4865
4896
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4866
|
-
const displayTarget =
|
|
4897
|
+
const displayTarget = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, rootRes.targetName));
|
|
4867
4898
|
p19__namespace.log.success(
|
|
4868
4899
|
pc13__default.default.green(
|
|
4869
4900
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4882,19 +4913,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4882
4913
|
}
|
|
4883
4914
|
};
|
|
4884
4915
|
function isTestFile2(filePath, projectRoot) {
|
|
4885
|
-
const norm = (projectRoot ?
|
|
4886
|
-
const base =
|
|
4916
|
+
const norm = (projectRoot ? path26__default.default.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4917
|
+
const base = path26__default.default.basename(norm);
|
|
4887
4918
|
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);
|
|
4888
4919
|
}
|
|
4889
4920
|
function scanDirFiles(dir, projectRoot) {
|
|
4890
|
-
if (!
|
|
4891
|
-
const entries =
|
|
4921
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
4922
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4892
4923
|
const files = [];
|
|
4893
4924
|
for (const entry of entries) {
|
|
4894
4925
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4895
4926
|
continue;
|
|
4896
4927
|
}
|
|
4897
|
-
const fullPath =
|
|
4928
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
4898
4929
|
if (entry.isDirectory()) {
|
|
4899
4930
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4900
4931
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4903,101 +4934,194 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4903
4934
|
}
|
|
4904
4935
|
return files;
|
|
4905
4936
|
}
|
|
4906
|
-
function classifyFile(
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
}
|
|
4926
|
-
const
|
|
4927
|
-
if (
|
|
4928
|
-
const
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
layer = "
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4937
|
+
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4938
|
+
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4939
|
+
const paths = getResolvedLintPaths(featuresBaseAbs, loadLintConfig(featuresBaseAbs));
|
|
4940
|
+
featuresBaseAbs = paths.featuresBase;
|
|
4941
|
+
sharedBaseAbs = paths.sharedBase;
|
|
4942
|
+
errorsBaseAbs = paths.errorsBase;
|
|
4943
|
+
}
|
|
4944
|
+
const normFullPath = path26__default.default.resolve(fullPathAbs);
|
|
4945
|
+
const normErrorsBase = path26__default.default.resolve(errorsBaseAbs);
|
|
4946
|
+
const normSharedBase = path26__default.default.resolve(sharedBaseAbs);
|
|
4947
|
+
const normFeaturesBase = path26__default.default.resolve(featuresBaseAbs);
|
|
4948
|
+
const relErrors = path26__default.default.relative(normErrorsBase, normFullPath).replace(/\\/g, "/");
|
|
4949
|
+
if (!relErrors.startsWith("..") && !path26__default.default.isAbsolute(relErrors)) {
|
|
4950
|
+
return {
|
|
4951
|
+
moduleType: "error",
|
|
4952
|
+
moduleName: "global-errors",
|
|
4953
|
+
feature: "global-errors",
|
|
4954
|
+
layer: "domain"
|
|
4955
|
+
};
|
|
4956
|
+
}
|
|
4957
|
+
const relShared = path26__default.default.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4958
|
+
if (!relShared.startsWith("..") && !path26__default.default.isAbsolute(relShared)) {
|
|
4959
|
+
const parts = relShared.split("/").filter(Boolean);
|
|
4960
|
+
if (parts.length >= 1) {
|
|
4961
|
+
const moduleName = parts[0];
|
|
4962
|
+
const rest = parts.slice(1).join("/");
|
|
4963
|
+
let layer = "root";
|
|
4964
|
+
if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
|
|
4965
|
+
layer = "domain";
|
|
4966
|
+
} else if (rest.startsWith("ports/") || rest === "ports") {
|
|
4967
|
+
layer = "ports";
|
|
4968
|
+
} else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
|
|
4969
|
+
layer = "infrastructure";
|
|
4970
|
+
}
|
|
4971
|
+
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
const relFeatures = path26__default.default.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4975
|
+
if (!relFeatures.startsWith("..") && !path26__default.default.isAbsolute(relFeatures)) {
|
|
4976
|
+
const parts = relFeatures.split("/").filter(Boolean);
|
|
4977
|
+
if (parts.length >= 2) {
|
|
4978
|
+
const moduleName = parts[0];
|
|
4979
|
+
const subfolder = parts[1];
|
|
4980
|
+
let layer;
|
|
4981
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4982
|
+
layer = "domain";
|
|
4983
|
+
} else if (subfolder === "useCases" || subfolder === "usecases") {
|
|
4984
|
+
layer = "useCases";
|
|
4985
|
+
} else if (subfolder === "services") {
|
|
4986
|
+
layer = "services";
|
|
4987
|
+
} else if (subfolder === "infrastructure") {
|
|
4988
|
+
layer = "infrastructure";
|
|
4989
|
+
} else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
|
|
4990
|
+
layer = "presentation";
|
|
4991
|
+
}
|
|
4992
|
+
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
4993
|
+
}
|
|
4939
4994
|
}
|
|
4940
4995
|
return {};
|
|
4941
4996
|
}
|
|
4942
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
4997
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4943
4998
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4944
4999
|
return { isExternal: true, isDiFramework: true };
|
|
4945
5000
|
}
|
|
4946
|
-
if (specifier.startsWith("node:") || [
|
|
5001
|
+
if (specifier.startsWith("node:") || [
|
|
5002
|
+
"crypto",
|
|
5003
|
+
"fs",
|
|
5004
|
+
"path",
|
|
5005
|
+
"http",
|
|
5006
|
+
"https",
|
|
5007
|
+
"url",
|
|
5008
|
+
"os",
|
|
5009
|
+
"child_process",
|
|
5010
|
+
"stream",
|
|
5011
|
+
"express",
|
|
5012
|
+
"cors",
|
|
5013
|
+
"dotenv"
|
|
5014
|
+
].includes(specifier)) {
|
|
4947
5015
|
return { isExternal: true, isDiFramework: false };
|
|
4948
5016
|
}
|
|
4949
|
-
|
|
5017
|
+
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
5018
|
+
const paths = getResolvedLintPaths(projectRoot, loadLintConfig(projectRoot));
|
|
5019
|
+
featuresBaseAbs = featuresBaseAbs || paths.featuresBase;
|
|
5020
|
+
sharedBaseAbs = sharedBaseAbs || paths.sharedBase;
|
|
5021
|
+
errorsBaseAbs = errorsBaseAbs || paths.errorsBase;
|
|
5022
|
+
}
|
|
5023
|
+
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
5024
|
+
let targetAbs = null;
|
|
4950
5025
|
if (specifier.startsWith(".")) {
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
5026
|
+
targetAbs = path26__default.default.resolve(path26__default.default.dirname(sourceFileAbs), specifier);
|
|
5027
|
+
} else {
|
|
5028
|
+
const featRel = path26__default.default.relative(projectRoot, featuresBaseAbs).replace(/\\/g, "/");
|
|
5029
|
+
const sharedRel = path26__default.default.relative(projectRoot, sharedBaseAbs).replace(/\\/g, "/");
|
|
5030
|
+
const errorsRel = path26__default.default.relative(projectRoot, errorsBaseAbs).replace(/\\/g, "/");
|
|
5031
|
+
const featPrefixes = ["features"];
|
|
5032
|
+
if (featRel && featRel !== "." && !featRel.startsWith("..")) {
|
|
5033
|
+
featPrefixes.push(featRel);
|
|
5034
|
+
const b = path26__default.default.basename(featRel);
|
|
5035
|
+
if (!featPrefixes.includes(b)) featPrefixes.push(b);
|
|
5036
|
+
}
|
|
5037
|
+
const sharedPrefixes = ["shared"];
|
|
5038
|
+
if (sharedRel && sharedRel !== "." && !sharedRel.startsWith("..")) {
|
|
5039
|
+
sharedPrefixes.push(sharedRel);
|
|
5040
|
+
const b = path26__default.default.basename(sharedRel);
|
|
5041
|
+
if (!sharedPrefixes.includes(b)) sharedPrefixes.push(b);
|
|
5042
|
+
}
|
|
5043
|
+
const errorsPrefixes = ["errors"];
|
|
5044
|
+
if (errorsRel && errorsRel !== "." && !errorsRel.startsWith("..")) {
|
|
5045
|
+
errorsPrefixes.push(errorsRel);
|
|
5046
|
+
const b = path26__default.default.basename(errorsRel);
|
|
5047
|
+
if (!errorsPrefixes.includes(b)) errorsPrefixes.push(b);
|
|
5048
|
+
}
|
|
5049
|
+
for (const prefix of featPrefixes) {
|
|
5050
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5051
|
+
const m = specifier.match(aliasRegex);
|
|
5052
|
+
if (m && m[2]) {
|
|
5053
|
+
targetAbs = path26__default.default.resolve(featuresBaseAbs, m[2]);
|
|
5054
|
+
break;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
if (!targetAbs) {
|
|
5058
|
+
for (const prefix of sharedPrefixes) {
|
|
5059
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5060
|
+
const m = specifier.match(aliasRegex);
|
|
5061
|
+
if (m && m[2]) {
|
|
5062
|
+
targetAbs = path26__default.default.resolve(sharedBaseAbs, m[2]);
|
|
5063
|
+
break;
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (!targetAbs) {
|
|
5068
|
+
for (const prefix of errorsPrefixes) {
|
|
5069
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}(?:\\/(.*))?$`);
|
|
5070
|
+
const m = specifier.match(aliasRegex);
|
|
5071
|
+
if (m) {
|
|
5072
|
+
targetAbs = path26__default.default.resolve(errorsBaseAbs, m[2] || "");
|
|
5073
|
+
break;
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
if (!targetAbs && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
|
|
5078
|
+
const sub = specifier.slice(2);
|
|
5079
|
+
const srcCandidate = path26__default.default.resolve(projectRoot, "src", sub);
|
|
5080
|
+
const rootCandidate = path26__default.default.resolve(projectRoot, sub);
|
|
5081
|
+
if (fs17__default.default.existsSync(srcCandidate)) {
|
|
5082
|
+
targetAbs = srcCandidate;
|
|
5083
|
+
} else if (fs17__default.default.existsSync(rootCandidate)) {
|
|
5084
|
+
targetAbs = rootCandidate;
|
|
5085
|
+
} else {
|
|
5086
|
+
targetAbs = srcCandidate;
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
if (!targetAbs) {
|
|
5090
|
+
const featCandidate = path26__default.default.resolve(featuresBaseAbs, specifier);
|
|
5091
|
+
if (fs17__default.default.existsSync(featCandidate) || fs17__default.default.existsSync(featCandidate + ".ts") || fs17__default.default.existsSync(featCandidate + ".js")) {
|
|
5092
|
+
targetAbs = featCandidate;
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
if (!targetAbs) {
|
|
5097
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5098
|
+
}
|
|
5099
|
+
const normTarget = path26__default.default.resolve(targetAbs);
|
|
5100
|
+
const srcConfig = path26__default.default.resolve(projectRoot, "src/configs");
|
|
5101
|
+
const rootConfig = path26__default.default.resolve(projectRoot, "configs");
|
|
5102
|
+
const isTargetConfig = isGlobalConfig || normTarget === srcConfig || normTarget.startsWith(srcConfig + path26__default.default.sep) || normTarget === rootConfig || normTarget.startsWith(rootConfig + path26__default.default.sep);
|
|
5103
|
+
const classification = classifyFile(targetAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs);
|
|
5104
|
+
if (classification.moduleType === "error") {
|
|
5105
|
+
return {
|
|
5106
|
+
isExternal: false,
|
|
5107
|
+
isDiFramework: false,
|
|
5108
|
+
isGlobalError: true,
|
|
5109
|
+
isTargetError: true
|
|
5110
|
+
};
|
|
4972
5111
|
}
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
const
|
|
4977
|
-
const
|
|
4978
|
-
const rest = featMatch[3] || "";
|
|
4979
|
-
let targetLayer;
|
|
4980
|
-
if (targetSubfolder === "domain" || targetSubfolder === "errors") {
|
|
4981
|
-
targetLayer = "domain";
|
|
4982
|
-
} else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
|
|
4983
|
-
targetLayer = "useCases";
|
|
4984
|
-
} else if (targetSubfolder === "services") {
|
|
4985
|
-
targetLayer = "services";
|
|
4986
|
-
} else if (targetSubfolder === "infrastructure") {
|
|
4987
|
-
targetLayer = "infrastructure";
|
|
4988
|
-
} else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
|
|
4989
|
-
targetLayer = "presentation";
|
|
4990
|
-
}
|
|
4991
|
-
const basePart = rest ? path2__default.default.basename(rest) : targetSubfolder;
|
|
4992
|
-
const targetFileName = basePart.replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5112
|
+
if (classification.moduleType === "feature") {
|
|
5113
|
+
const rel = path26__default.default.relative(featuresBaseAbs, targetAbs).replace(/\\/g, "/");
|
|
5114
|
+
const parts = rel.split("/").filter(Boolean);
|
|
5115
|
+
const targetSubfolder = parts[1];
|
|
5116
|
+
const targetFileName = path26__default.default.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
4993
5117
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
4994
5118
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
4995
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5119
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
4996
5120
|
return {
|
|
4997
5121
|
isExternal: false,
|
|
4998
5122
|
isDiFramework: false,
|
|
4999
|
-
targetFeature,
|
|
5000
|
-
targetLayer,
|
|
5123
|
+
targetFeature: classification.moduleName,
|
|
5124
|
+
targetLayer: classification.layer,
|
|
5001
5125
|
targetSubfolder,
|
|
5002
5126
|
targetFileName,
|
|
5003
5127
|
isTargetPort,
|
|
@@ -5005,25 +5129,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5005
5129
|
isTargetError
|
|
5006
5130
|
};
|
|
5007
5131
|
}
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
const
|
|
5011
|
-
const sharedRest = sharedMatch[2] || "";
|
|
5012
|
-
const isSharedRoot = !sharedRest.includes("/");
|
|
5013
|
-
const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
|
|
5014
|
-
const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
|
|
5015
|
-
const isSharedDomain = sharedRest.startsWith("domain/") || sharedRest === "domain";
|
|
5132
|
+
if (classification.moduleType === "shared") {
|
|
5133
|
+
const targetFileName = path26__default.default.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5134
|
+
const isTargetError = classification.layer === "domain" && (targetFileName.endsWith("Error") || targetFileName.endsWith("Errors") || targetAbs.includes("/errors/"));
|
|
5016
5135
|
return {
|
|
5017
5136
|
isExternal: false,
|
|
5018
5137
|
isDiFramework: false,
|
|
5019
|
-
targetSharedModule,
|
|
5020
|
-
isSharedRoot,
|
|
5021
|
-
isSharedPorts,
|
|
5022
|
-
isSharedInfra,
|
|
5023
|
-
isSharedDomain
|
|
5138
|
+
targetSharedModule: classification.moduleName,
|
|
5139
|
+
isSharedRoot: classification.layer === "root",
|
|
5140
|
+
isSharedPorts: classification.layer === "ports",
|
|
5141
|
+
isSharedInfra: classification.layer === "infrastructure",
|
|
5142
|
+
isSharedDomain: classification.layer === "domain",
|
|
5143
|
+
isTargetError
|
|
5024
5144
|
};
|
|
5025
5145
|
}
|
|
5026
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5146
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
5027
5147
|
}
|
|
5028
5148
|
function extractImportStatements(content) {
|
|
5029
5149
|
const statements = [];
|
|
@@ -5145,22 +5265,50 @@ var FeatureDependencyGraph = class {
|
|
|
5145
5265
|
return dfs(start);
|
|
5146
5266
|
}
|
|
5147
5267
|
};
|
|
5148
|
-
function discoverAllFeatures(
|
|
5149
|
-
const
|
|
5150
|
-
if (!
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5268
|
+
function discoverAllFeatures(featuresBaseAbs, excludeDirs = []) {
|
|
5269
|
+
const base = featuresBaseAbs;
|
|
5270
|
+
if (!fs17__default.default.existsSync(base)) return [];
|
|
5271
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26__default.default.resolve(d)));
|
|
5272
|
+
const entries = fs17__default.default.readdirSync(base, { withFileTypes: true });
|
|
5273
|
+
return entries.filter((e) => {
|
|
5274
|
+
if (!e.isDirectory()) return false;
|
|
5275
|
+
if (e.name.startsWith(".")) return false;
|
|
5276
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5277
|
+
return false;
|
|
5278
|
+
}
|
|
5279
|
+
const resolved = path26__default.default.resolve(base, e.name);
|
|
5280
|
+
for (const ex of excludeSet) {
|
|
5281
|
+
if (ex === resolved || ex.startsWith(resolved + path26__default.default.sep) || resolved.startsWith(ex + path26__default.default.sep)) {
|
|
5282
|
+
return false;
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
return true;
|
|
5286
|
+
}).map((e) => ({
|
|
5153
5287
|
name: e.name,
|
|
5154
|
-
dir:
|
|
5288
|
+
dir: path26__default.default.join(base, e.name)
|
|
5155
5289
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5156
5290
|
}
|
|
5157
|
-
function discoverAllShared(
|
|
5158
|
-
const
|
|
5159
|
-
if (!
|
|
5160
|
-
const
|
|
5161
|
-
|
|
5291
|
+
function discoverAllShared(sharedBaseAbs, excludeDirs = []) {
|
|
5292
|
+
const base = sharedBaseAbs;
|
|
5293
|
+
if (!fs17__default.default.existsSync(base)) return [];
|
|
5294
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26__default.default.resolve(d)));
|
|
5295
|
+
const entries = fs17__default.default.readdirSync(base, { withFileTypes: true });
|
|
5296
|
+
return entries.filter((e) => {
|
|
5297
|
+
if (!e.isDirectory()) return false;
|
|
5298
|
+
if (e.name.startsWith(".")) return false;
|
|
5299
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5300
|
+
return false;
|
|
5301
|
+
}
|
|
5302
|
+
const resolved = path26__default.default.resolve(base, e.name);
|
|
5303
|
+
for (const ex of excludeSet) {
|
|
5304
|
+
if (ex === resolved || ex.startsWith(resolved + path26__default.default.sep) || resolved.startsWith(ex + path26__default.default.sep)) {
|
|
5305
|
+
return false;
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
return true;
|
|
5309
|
+
}).map((e) => ({
|
|
5162
5310
|
name: e.name,
|
|
5163
|
-
dir:
|
|
5311
|
+
dir: path26__default.default.join(base, e.name)
|
|
5164
5312
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5165
5313
|
}
|
|
5166
5314
|
function extractClassMethods(content, className) {
|
|
@@ -5316,7 +5464,7 @@ function extractClassMethods(content, className) {
|
|
|
5316
5464
|
return methods;
|
|
5317
5465
|
}
|
|
5318
5466
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5319
|
-
const fileName =
|
|
5467
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5320
5468
|
const lines = content.split(/\r?\n/);
|
|
5321
5469
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5322
5470
|
if (!classMatch) {
|
|
@@ -5412,7 +5560,7 @@ private ${m.name}(...) { ... }`
|
|
|
5412
5560
|
}
|
|
5413
5561
|
}
|
|
5414
5562
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5415
|
-
const fileName =
|
|
5563
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5416
5564
|
const lines = content.split(/\r?\n/);
|
|
5417
5565
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5418
5566
|
if (!classMatch) {
|
|
@@ -5511,11 +5659,40 @@ function lintCodebase(options = {}) {
|
|
|
5511
5659
|
const startTime = Date.now();
|
|
5512
5660
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5513
5661
|
const failFast = !options.complete;
|
|
5514
|
-
const
|
|
5515
|
-
const
|
|
5662
|
+
const lintConfig = loadLintConfig(projectRoot);
|
|
5663
|
+
const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
|
|
5664
|
+
const missingFoundations = [];
|
|
5665
|
+
if (!fs17__default.default.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5666
|
+
if (!fs17__default.default.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5667
|
+
if (!fs17__default.default.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5668
|
+
if (missingFoundations.length > 0) {
|
|
5669
|
+
throw new DependencyRuleError(
|
|
5670
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5671
|
+
[],
|
|
5672
|
+
true
|
|
5673
|
+
);
|
|
5674
|
+
}
|
|
5675
|
+
const baseErrorRelative = lintConfig.baseErrorPath || path26__default.default.relative(projectRoot, path26__default.default.join(errorsBase, "AppError.ts")).replace(/\\/g, "/");
|
|
5676
|
+
const baseErrorAbs = path26__default.default.resolve(projectRoot, baseErrorRelative);
|
|
5677
|
+
if (!fs17__default.default.existsSync(baseErrorAbs)) {
|
|
5678
|
+
throw new DependencyRuleError(
|
|
5679
|
+
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5680
|
+
[],
|
|
5681
|
+
true
|
|
5682
|
+
);
|
|
5683
|
+
}
|
|
5684
|
+
const baseErrorContent = fs17__default.default.readFileSync(baseErrorAbs, "utf-8");
|
|
5685
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5686
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5687
|
+
);
|
|
5688
|
+
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5689
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5690
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5691
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5516
5692
|
const allModules = [
|
|
5517
5693
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5518
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5694
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" })),
|
|
5695
|
+
errorModule
|
|
5519
5696
|
];
|
|
5520
5697
|
const graph = new FeatureDependencyGraph();
|
|
5521
5698
|
const allViolations = [];
|
|
@@ -5545,7 +5722,7 @@ Code: ${v.code}`;
|
|
|
5545
5722
|
scannedFilesCount: 0,
|
|
5546
5723
|
passed: true,
|
|
5547
5724
|
violations: [],
|
|
5548
|
-
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : ["domain", "ports", "infrastructure", "root"]
|
|
5725
|
+
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : mod.type === "shared" ? ["domain", "ports", "infrastructure", "root"] : ["domain"]
|
|
5549
5726
|
});
|
|
5550
5727
|
}
|
|
5551
5728
|
for (const mod of allModules) {
|
|
@@ -5553,13 +5730,128 @@ Code: ${v.code}`;
|
|
|
5553
5730
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5554
5731
|
modResult.scannedFilesCount = allFiles.length;
|
|
5555
5732
|
for (const file of allFiles) {
|
|
5556
|
-
const relPath =
|
|
5557
|
-
const { moduleType, moduleName, layer } = classifyFile(file,
|
|
5733
|
+
const relPath = path26__default.default.relative(projectRoot, file).replace(/\\/g, "/");
|
|
5734
|
+
const { moduleType, moduleName, layer } = classifyFile(file, featuresBase, sharedBase, errorsBase);
|
|
5558
5735
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5559
|
-
const content =
|
|
5736
|
+
const content = fs17__default.default.readFileSync(file, "utf-8");
|
|
5560
5737
|
const statements = extractImportStatements(content);
|
|
5561
|
-
const baseName =
|
|
5738
|
+
const baseName = path26__default.default.basename(file);
|
|
5562
5739
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5740
|
+
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5741
|
+
let throwMatch;
|
|
5742
|
+
while ((throwMatch = throwErrorRegex.exec(content)) !== null) {
|
|
5743
|
+
const charIndex = throwMatch.index;
|
|
5744
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5745
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || throwMatch[0];
|
|
5746
|
+
if (lineCode.startsWith("//")) continue;
|
|
5747
|
+
addViolation({
|
|
5748
|
+
feature: moduleName,
|
|
5749
|
+
layer,
|
|
5750
|
+
file,
|
|
5751
|
+
relativePath: relPath,
|
|
5752
|
+
line: lineNum,
|
|
5753
|
+
category: "Plain Error Throw Violation",
|
|
5754
|
+
code: lineCode,
|
|
5755
|
+
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5756
|
+
severity: "error",
|
|
5757
|
+
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a domain-specific error class that inherits from it.`,
|
|
5758
|
+
fixExample: {
|
|
5759
|
+
bad: lineCode,
|
|
5760
|
+
good: `throw new ${baseErrorClassName}("Something went wrong");`
|
|
5761
|
+
}
|
|
5762
|
+
});
|
|
5763
|
+
}
|
|
5764
|
+
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");
|
|
5765
|
+
const isBaseErrorFile = path26__default.default.resolve(file) === path26__default.default.resolve(baseErrorAbs);
|
|
5766
|
+
if (isErrorFile && !isBaseErrorFile) {
|
|
5767
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5768
|
+
let classMatch;
|
|
5769
|
+
while ((classMatch = classRegex.exec(content)) !== null) {
|
|
5770
|
+
const className = classMatch[1];
|
|
5771
|
+
const extendsName = classMatch[2];
|
|
5772
|
+
const charIndex = classMatch.index;
|
|
5773
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5774
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || classMatch[0];
|
|
5775
|
+
if (lineCode.startsWith("//")) continue;
|
|
5776
|
+
if (!extendsName) {
|
|
5777
|
+
addViolation({
|
|
5778
|
+
feature: moduleName,
|
|
5779
|
+
layer,
|
|
5780
|
+
file,
|
|
5781
|
+
relativePath: relPath,
|
|
5782
|
+
line: lineNum,
|
|
5783
|
+
category: "Error Inheritance Violation",
|
|
5784
|
+
code: lineCode,
|
|
5785
|
+
ruleId: "E4_MISSING_INHERITANCE",
|
|
5786
|
+
severity: "error",
|
|
5787
|
+
advice: `Error class '${className}' must extend '${baseErrorClassName}' (or another class that derives from it).`,
|
|
5788
|
+
fixExample: {
|
|
5789
|
+
bad: lineCode,
|
|
5790
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5791
|
+
}
|
|
5792
|
+
});
|
|
5793
|
+
} else if (extendsName === "Error") {
|
|
5794
|
+
addViolation({
|
|
5795
|
+
feature: moduleName,
|
|
5796
|
+
layer,
|
|
5797
|
+
file,
|
|
5798
|
+
relativePath: relPath,
|
|
5799
|
+
line: lineNum,
|
|
5800
|
+
category: "Error Inheritance Violation",
|
|
5801
|
+
code: lineCode,
|
|
5802
|
+
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5803
|
+
severity: "error",
|
|
5804
|
+
advice: `Error class '${className}' extends the native 'Error'. It must extend your base error class ('${baseErrorClassName}').`,
|
|
5805
|
+
fixExample: {
|
|
5806
|
+
bad: lineCode,
|
|
5807
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5808
|
+
}
|
|
5809
|
+
});
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
if (moduleType === "error") {
|
|
5814
|
+
for (const stmt of statements) {
|
|
5815
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5816
|
+
if (target.targetFeature) {
|
|
5817
|
+
addViolation({
|
|
5818
|
+
feature: "global-errors",
|
|
5819
|
+
layer: "domain",
|
|
5820
|
+
file,
|
|
5821
|
+
relativePath: relPath,
|
|
5822
|
+
line: stmt.line,
|
|
5823
|
+
category: "Global Error Purity Violation",
|
|
5824
|
+
code: stmt.rawCode,
|
|
5825
|
+
ruleId: "E1_FEATURE_DEPENDENCY",
|
|
5826
|
+
severity: "error",
|
|
5827
|
+
advice: `Global errors must not depend on features ('${target.targetFeature}'). Global errors represent pure domain or system states.`,
|
|
5828
|
+
fixExample: {
|
|
5829
|
+
bad: stmt.rawCode,
|
|
5830
|
+
good: "// Global errors must not reference specific features. Keep error types pure."
|
|
5831
|
+
}
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5834
|
+
if (target.targetSharedModule && !target.isSharedDomain) {
|
|
5835
|
+
addViolation({
|
|
5836
|
+
feature: "global-errors",
|
|
5837
|
+
layer: "domain",
|
|
5838
|
+
file,
|
|
5839
|
+
relativePath: relPath,
|
|
5840
|
+
line: stmt.line,
|
|
5841
|
+
category: "Global Error Purity Violation",
|
|
5842
|
+
code: stmt.rawCode,
|
|
5843
|
+
ruleId: "E2_SHARED_NON_DOMAIN",
|
|
5844
|
+
severity: "error",
|
|
5845
|
+
advice: `Global errors can only depend on shared domain elements (e.g., value objects). They cannot depend on shared ports or infrastructure.`,
|
|
5846
|
+
fixExample: {
|
|
5847
|
+
bad: stmt.rawCode,
|
|
5848
|
+
good: "// Depend only on pure domain types from shared modules."
|
|
5849
|
+
}
|
|
5850
|
+
});
|
|
5851
|
+
}
|
|
5852
|
+
}
|
|
5853
|
+
continue;
|
|
5854
|
+
}
|
|
5563
5855
|
if (moduleType === "shared") {
|
|
5564
5856
|
if (layer === "domain") {
|
|
5565
5857
|
const lines = content.split(/\r?\n/);
|
|
@@ -5586,7 +5878,7 @@ Code: ${v.code}`;
|
|
|
5586
5878
|
}
|
|
5587
5879
|
}
|
|
5588
5880
|
for (const stmt of statements) {
|
|
5589
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5881
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5590
5882
|
if (target.targetFeature) {
|
|
5591
5883
|
addViolation({
|
|
5592
5884
|
feature: moduleName,
|
|
@@ -5716,7 +6008,7 @@ Code: ${v.code}`;
|
|
|
5716
6008
|
}
|
|
5717
6009
|
}
|
|
5718
6010
|
for (const stmt of statements) {
|
|
5719
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
6011
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5720
6012
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5721
6013
|
const evidence = {
|
|
5722
6014
|
fromFeature: feat.name,
|