@solid-stack/mason 1.0.33 → 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 +493 -382
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +491 -380
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +4 -4
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +496 -384
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +494 -383
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +5 -0
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +9 -1
- 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,70 +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
220
|
}
|
|
221
221
|
function loadLintConfig(projectRoot) {
|
|
222
|
-
const configPath =
|
|
223
|
-
if (
|
|
222
|
+
const configPath = path26__default.default.join(projectRoot, "mason-lint.config.json");
|
|
223
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
224
224
|
try {
|
|
225
|
-
return JSON.parse(
|
|
225
|
+
return JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
226
226
|
} catch {
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
-
return {
|
|
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
|
+
};
|
|
230
251
|
}
|
|
231
252
|
|
|
232
253
|
// src/utils/project.ts
|
|
233
254
|
function findProjectRoot(startDir = process.cwd()) {
|
|
234
|
-
let current =
|
|
255
|
+
let current = path26__default.default.resolve(startDir);
|
|
235
256
|
while (true) {
|
|
236
|
-
if (
|
|
257
|
+
if (fs17__default.default.existsSync(path26__default.default.join(current, "package.json"))) {
|
|
237
258
|
return current;
|
|
238
259
|
}
|
|
239
|
-
const parent =
|
|
260
|
+
const parent = path26__default.default.dirname(current);
|
|
240
261
|
if (parent === current) {
|
|
241
|
-
return
|
|
262
|
+
return path26__default.default.resolve(startDir);
|
|
242
263
|
}
|
|
243
264
|
current = parent;
|
|
244
265
|
}
|
|
@@ -251,9 +272,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
251
272
|
}
|
|
252
273
|
function listExistingFeatures(projectRoot) {
|
|
253
274
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
254
|
-
if (!
|
|
275
|
+
if (!fs17__default.default.existsSync(featuresDir)) return [];
|
|
255
276
|
try {
|
|
256
|
-
const entries =
|
|
277
|
+
const entries = fs17__default.default.readdirSync(featuresDir, { withFileTypes: true });
|
|
257
278
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
258
279
|
} catch {
|
|
259
280
|
return [];
|
|
@@ -263,14 +284,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
263
284
|
const results = [];
|
|
264
285
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
265
286
|
for (const feat of features) {
|
|
266
|
-
const domainDir =
|
|
267
|
-
if (!
|
|
287
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
288
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
268
289
|
try {
|
|
269
|
-
const files =
|
|
290
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
270
291
|
for (const file of files) {
|
|
271
292
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
272
293
|
const name = file.replace(/\.ts$/, "");
|
|
273
|
-
const fullPath =
|
|
294
|
+
const fullPath = path26__default.default.join(domainDir, file);
|
|
274
295
|
if (/^I[A-Z]/.test(name)) {
|
|
275
296
|
results.push({
|
|
276
297
|
name,
|
|
@@ -279,7 +300,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
279
300
|
});
|
|
280
301
|
} else {
|
|
281
302
|
try {
|
|
282
|
-
const content =
|
|
303
|
+
const content = fs17__default.default.readFileSync(fullPath, "utf-8");
|
|
283
304
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
284
305
|
results.push({
|
|
285
306
|
name,
|
|
@@ -301,17 +322,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
301
322
|
const results = [];
|
|
302
323
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
303
324
|
for (const feat of features) {
|
|
304
|
-
const domainDir =
|
|
305
|
-
if (!
|
|
325
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
326
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
306
327
|
try {
|
|
307
|
-
const files =
|
|
328
|
+
const files = fs17__default.default.readdirSync(domainDir);
|
|
308
329
|
for (const file of files) {
|
|
309
330
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
310
331
|
const name = file.replace(/\.ts$/, "");
|
|
311
332
|
if (!/^I[A-Z]/.test(name)) {
|
|
312
333
|
results.push({
|
|
313
334
|
name,
|
|
314
|
-
fullPath:
|
|
335
|
+
fullPath: path26__default.default.join(domainDir, file),
|
|
315
336
|
feature: feat
|
|
316
337
|
});
|
|
317
338
|
}
|
|
@@ -326,16 +347,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
326
347
|
const results = [];
|
|
327
348
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
328
349
|
for (const feat of features) {
|
|
329
|
-
const useCasesDir =
|
|
330
|
-
if (!
|
|
350
|
+
const useCasesDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
351
|
+
if (!fs17__default.default.existsSync(useCasesDir)) continue;
|
|
331
352
|
try {
|
|
332
|
-
const files =
|
|
353
|
+
const files = fs17__default.default.readdirSync(useCasesDir);
|
|
333
354
|
for (const file of files) {
|
|
334
355
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
335
356
|
const name = file.replace(/\.ts$/, "");
|
|
336
357
|
results.push({
|
|
337
358
|
name,
|
|
338
|
-
fullPath:
|
|
359
|
+
fullPath: path26__default.default.join(useCasesDir, file),
|
|
339
360
|
feature: feat
|
|
340
361
|
});
|
|
341
362
|
}
|
|
@@ -361,33 +382,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
361
382
|
});
|
|
362
383
|
var cachedTemplatesDir = null;
|
|
363
384
|
function getTemplatesDir() {
|
|
364
|
-
if (cachedTemplatesDir &&
|
|
385
|
+
if (cachedTemplatesDir && fs17__default.default.existsSync(cachedTemplatesDir)) {
|
|
365
386
|
return cachedTemplatesDir;
|
|
366
387
|
}
|
|
367
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
368
|
-
const cand1 =
|
|
369
|
-
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)) {
|
|
370
391
|
cachedTemplatesDir = cand1;
|
|
371
392
|
return cand1;
|
|
372
393
|
}
|
|
373
|
-
const cand2 =
|
|
374
|
-
if (
|
|
394
|
+
const cand2 = path26__default.default.resolve(currentDir, "../templates");
|
|
395
|
+
if (fs17__default.default.existsSync(cand2)) {
|
|
375
396
|
cachedTemplatesDir = cand2;
|
|
376
397
|
return cand2;
|
|
377
398
|
}
|
|
378
|
-
const cand3 =
|
|
379
|
-
if (
|
|
399
|
+
const cand3 = path26__default.default.resolve(currentDir, "templates");
|
|
400
|
+
if (fs17__default.default.existsSync(cand3)) {
|
|
380
401
|
cachedTemplatesDir = cand3;
|
|
381
402
|
return cand3;
|
|
382
403
|
}
|
|
383
404
|
let curr = currentDir;
|
|
384
405
|
while (true) {
|
|
385
|
-
const candTemplates =
|
|
386
|
-
if (
|
|
406
|
+
const candTemplates = path26__default.default.join(curr, "templates");
|
|
407
|
+
if (fs17__default.default.existsSync(candTemplates)) {
|
|
387
408
|
cachedTemplatesDir = candTemplates;
|
|
388
409
|
return candTemplates;
|
|
389
410
|
}
|
|
390
|
-
const parent =
|
|
411
|
+
const parent = path26__default.default.dirname(curr);
|
|
391
412
|
if (parent === curr) break;
|
|
392
413
|
curr = parent;
|
|
393
414
|
}
|
|
@@ -395,8 +416,8 @@ function getTemplatesDir() {
|
|
|
395
416
|
}
|
|
396
417
|
var templateCache = /* @__PURE__ */ new Map();
|
|
397
418
|
function loadTemplate(relativePath) {
|
|
398
|
-
const fullPath =
|
|
399
|
-
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");
|
|
400
421
|
}
|
|
401
422
|
function renderTemplateFile(relativePath, context) {
|
|
402
423
|
let compiled = templateCache.get(relativePath);
|
|
@@ -483,8 +504,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
483
504
|
function generateDomain(options) {
|
|
484
505
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
485
506
|
const entityName = toPascalCase(options.name);
|
|
486
|
-
const targetDir = options.targetDir ||
|
|
487
|
-
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`);
|
|
488
509
|
const code = generateDomainEntityCode(
|
|
489
510
|
entityName,
|
|
490
511
|
options.kind || "interface",
|
|
@@ -533,8 +554,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
533
554
|
function generateInterface(options) {
|
|
534
555
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
535
556
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
536
|
-
const targetDir = options.targetDir ||
|
|
537
|
-
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`);
|
|
538
559
|
const code = generateInterfaceCode(
|
|
539
560
|
ifaceName,
|
|
540
561
|
options.kind || "abstractClass",
|
|
@@ -769,7 +790,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
769
790
|
};
|
|
770
791
|
}
|
|
771
792
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
772
|
-
if (customPath &&
|
|
793
|
+
if (customPath && fs17__default.default.existsSync(customPath)) {
|
|
773
794
|
return { filePath: customPath, ifaceName };
|
|
774
795
|
}
|
|
775
796
|
const candidateNames = [
|
|
@@ -779,22 +800,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
779
800
|
];
|
|
780
801
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
781
802
|
for (const feat of features) {
|
|
782
|
-
const domainDir =
|
|
783
|
-
if (!
|
|
803
|
+
const domainDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
804
|
+
if (!fs17__default.default.existsSync(domainDir)) continue;
|
|
784
805
|
for (const cand of candidateNames) {
|
|
785
|
-
const p1 =
|
|
786
|
-
if (
|
|
806
|
+
const p1 = path26__default.default.join(domainDir, `${cand}.ts`);
|
|
807
|
+
if (fs17__default.default.existsSync(p1)) {
|
|
787
808
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
788
809
|
}
|
|
789
810
|
}
|
|
790
811
|
}
|
|
791
812
|
for (const cand of candidateNames) {
|
|
792
|
-
const p1 =
|
|
793
|
-
if (
|
|
794
|
-
const p22 =
|
|
795
|
-
if (
|
|
796
|
-
const p32 =
|
|
797
|
-
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 };
|
|
798
819
|
}
|
|
799
820
|
return null;
|
|
800
821
|
}
|
|
@@ -817,7 +838,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
817
838
|
while (currentFile && !visited.has(currentFile)) {
|
|
818
839
|
visited.add(currentFile);
|
|
819
840
|
try {
|
|
820
|
-
const content =
|
|
841
|
+
const content = fs17__default.default.readFileSync(currentFile, "utf-8");
|
|
821
842
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
822
843
|
for (const m of parsed.methods) {
|
|
823
844
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -893,7 +914,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
893
914
|
const entitiesVar = `${entityVar}s`;
|
|
894
915
|
const idKey = `${entityVar}Id`;
|
|
895
916
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
896
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
917
|
+
const entityImportPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
897
918
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
898
919
|
for (const imp of domainImports) {
|
|
899
920
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -942,7 +963,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
942
963
|
if (baseEntity) {
|
|
943
964
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
944
965
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
945
|
-
const importPath = ifaceImportPath ? `${
|
|
966
|
+
const importPath = ifaceImportPath ? `${path26__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
946
967
|
finalDomainImports.push({
|
|
947
968
|
name: baseEntity,
|
|
948
969
|
from: importPath,
|
|
@@ -987,8 +1008,8 @@ function generateInfrastructure(options) {
|
|
|
987
1008
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
988
1009
|
const className = toPascalCase(options.name);
|
|
989
1010
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
990
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
991
|
-
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`);
|
|
992
1013
|
let code;
|
|
993
1014
|
if (options.implementsInterface) {
|
|
994
1015
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -1002,7 +1023,7 @@ function generateInfrastructure(options) {
|
|
|
1002
1023
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
1003
1024
|
let ifaceImportPath;
|
|
1004
1025
|
if (ifaceFilePath) {
|
|
1005
|
-
let rel =
|
|
1026
|
+
let rel = path26__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
1006
1027
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1007
1028
|
ifaceImportPath = rel;
|
|
1008
1029
|
} else if (isFeatureScope) {
|
|
@@ -1015,8 +1036,8 @@ function generateInfrastructure(options) {
|
|
|
1015
1036
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
1016
1037
|
let fromPath;
|
|
1017
1038
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
1018
|
-
const absTarget =
|
|
1019
|
-
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, "/");
|
|
1020
1041
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1021
1042
|
fromPath = rel;
|
|
1022
1043
|
} else {
|
|
@@ -1203,8 +1224,8 @@ function generateUseCase(options) {
|
|
|
1203
1224
|
const files = [];
|
|
1204
1225
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1205
1226
|
const ucName = toPascalCase(options.name);
|
|
1206
|
-
const targetDir = options.targetDir ||
|
|
1207
|
-
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`);
|
|
1208
1229
|
const code = generateUseCaseCode(
|
|
1209
1230
|
ucName,
|
|
1210
1231
|
options.dependencies || [],
|
|
@@ -1220,7 +1241,7 @@ function generateUseCase(options) {
|
|
|
1220
1241
|
})
|
|
1221
1242
|
);
|
|
1222
1243
|
if (options.includeTest) {
|
|
1223
|
-
const testPath =
|
|
1244
|
+
const testPath = path26__default.default.join(targetDir, `${ucName}.test.ts`);
|
|
1224
1245
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1225
1246
|
files.push(
|
|
1226
1247
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1288,10 +1309,10 @@ function generateHttpHandler(options) {
|
|
|
1288
1309
|
const files = [];
|
|
1289
1310
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1290
1311
|
const className = toPascalCase(options.name);
|
|
1291
|
-
const pxDir =
|
|
1292
|
-
const handlersDir =
|
|
1293
|
-
const pxIndexPath =
|
|
1294
|
-
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)) {
|
|
1295
1316
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1296
1317
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1297
1318
|
routePath: featurePath
|
|
@@ -1304,7 +1325,7 @@ function generateHttpHandler(options) {
|
|
|
1304
1325
|
})
|
|
1305
1326
|
);
|
|
1306
1327
|
}
|
|
1307
|
-
const handlerFilePath =
|
|
1328
|
+
const handlerFilePath = path26__default.default.join(handlersDir, `${className}.ts`);
|
|
1308
1329
|
const code = generateHttpHandlerCode(
|
|
1309
1330
|
className,
|
|
1310
1331
|
options.method || "get",
|
|
@@ -1325,11 +1346,11 @@ function generateHttpHandler(options) {
|
|
|
1325
1346
|
return files;
|
|
1326
1347
|
}
|
|
1327
1348
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1328
|
-
const providerPath =
|
|
1349
|
+
const providerPath = path26__default.default.join(featureDir, "diProvider.ts");
|
|
1329
1350
|
const pascalFeature = toPascalCase(featureName);
|
|
1330
1351
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1331
|
-
if (
|
|
1332
|
-
let content =
|
|
1352
|
+
if (fs17__default.default.existsSync(providerPath) && !options.overwrite) {
|
|
1353
|
+
let content = fs17__default.default.readFileSync(providerPath, "utf-8");
|
|
1333
1354
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1334
1355
|
for (const b of bindings) {
|
|
1335
1356
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1388,7 +1409,7 @@ function generateFeature(options) {
|
|
|
1388
1409
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1389
1410
|
const rawName = options.name.trim();
|
|
1390
1411
|
const featureName = toCamelCase(rawName);
|
|
1391
|
-
const targetDir = options.targetDir ||
|
|
1412
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1392
1413
|
const routePath = options.routePath || `/${featureName}`;
|
|
1393
1414
|
const rawEntities = [];
|
|
1394
1415
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1409,7 +1430,7 @@ function generateFeature(options) {
|
|
|
1409
1430
|
const domainFile = generateDomain({
|
|
1410
1431
|
name: ent,
|
|
1411
1432
|
feature: featureName,
|
|
1412
|
-
targetDir:
|
|
1433
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1413
1434
|
projectRoot,
|
|
1414
1435
|
dryRun: options.dryRun,
|
|
1415
1436
|
overwrite: options.overwrite,
|
|
@@ -1429,7 +1450,7 @@ function generateFeature(options) {
|
|
|
1429
1450
|
const ifaceFile = generateInterface({
|
|
1430
1451
|
name: ifaceName,
|
|
1431
1452
|
feature: featureName,
|
|
1432
|
-
targetDir:
|
|
1453
|
+
targetDir: path26__default.default.join(targetDir, "domain"),
|
|
1433
1454
|
entityName: ent,
|
|
1434
1455
|
projectRoot,
|
|
1435
1456
|
dryRun: options.dryRun,
|
|
@@ -1446,7 +1467,7 @@ function generateFeature(options) {
|
|
|
1446
1467
|
const stubFile = generateInfrastructure({
|
|
1447
1468
|
name: stubName,
|
|
1448
1469
|
feature: featureName,
|
|
1449
|
-
targetDir:
|
|
1470
|
+
targetDir: path26__default.default.join(targetDir, "infrastructure"),
|
|
1450
1471
|
kind: "stub",
|
|
1451
1472
|
implementsInterface: ifaceName,
|
|
1452
1473
|
entityName: ent,
|
|
@@ -1488,7 +1509,7 @@ function generateFeature(options) {
|
|
|
1488
1509
|
const ucFiles = generateUseCase({
|
|
1489
1510
|
name: ucName,
|
|
1490
1511
|
feature: featureName,
|
|
1491
|
-
targetDir:
|
|
1512
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1492
1513
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1493
1514
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1494
1515
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1533,7 +1554,7 @@ function generateFeature(options) {
|
|
|
1533
1554
|
const ucFiles = generateUseCase({
|
|
1534
1555
|
name: ucName,
|
|
1535
1556
|
feature: featureName,
|
|
1536
|
-
targetDir:
|
|
1557
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1537
1558
|
dependencies: deps,
|
|
1538
1559
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1539
1560
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1566,7 +1587,7 @@ function generateFeature(options) {
|
|
|
1566
1587
|
const ucFiles = generateUseCase({
|
|
1567
1588
|
name: ucName,
|
|
1568
1589
|
feature: featureName,
|
|
1569
|
-
targetDir:
|
|
1590
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1570
1591
|
dependencies: deps,
|
|
1571
1592
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1572
1593
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1599,7 +1620,7 @@ function generateFeature(options) {
|
|
|
1599
1620
|
const ucFiles = generateUseCase({
|
|
1600
1621
|
name: ucName,
|
|
1601
1622
|
feature: featureName,
|
|
1602
|
-
targetDir:
|
|
1623
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1603
1624
|
dependencies: deps,
|
|
1604
1625
|
inputProperties: [
|
|
1605
1626
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1643,7 +1664,7 @@ function generateFeature(options) {
|
|
|
1643
1664
|
const ucFiles = generateUseCase({
|
|
1644
1665
|
name: ucName,
|
|
1645
1666
|
feature: featureName,
|
|
1646
|
-
targetDir:
|
|
1667
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1647
1668
|
dependencies: deps,
|
|
1648
1669
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1649
1670
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1676,7 +1697,7 @@ function generateFeature(options) {
|
|
|
1676
1697
|
const ucFiles = generateUseCase({
|
|
1677
1698
|
name: ucName,
|
|
1678
1699
|
feature: featureName,
|
|
1679
|
-
targetDir:
|
|
1700
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1680
1701
|
dependencies: deps,
|
|
1681
1702
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1682
1703
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -1713,7 +1734,7 @@ function generateFeature(options) {
|
|
|
1713
1734
|
const ucFiles = generateUseCase({
|
|
1714
1735
|
name: ucName,
|
|
1715
1736
|
feature: featureName,
|
|
1716
|
-
targetDir:
|
|
1737
|
+
targetDir: path26__default.default.join(targetDir, "useCases"),
|
|
1717
1738
|
dependencies: customUc.dependencies || [],
|
|
1718
1739
|
inputProperties: customUc.inputProperties || [],
|
|
1719
1740
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -1745,8 +1766,8 @@ function generateFeature(options) {
|
|
|
1745
1766
|
}
|
|
1746
1767
|
}
|
|
1747
1768
|
if (options.includeExpress !== false) {
|
|
1748
|
-
const pxDir =
|
|
1749
|
-
const pxIndexPath =
|
|
1769
|
+
const pxDir = path26__default.default.join(targetDir, "pxExpress");
|
|
1770
|
+
const pxIndexPath = path26__default.default.join(pxDir, "index.ts");
|
|
1750
1771
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
1751
1772
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
1752
1773
|
files.push(
|
|
@@ -1876,7 +1897,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1876
1897
|
}
|
|
1877
1898
|
const normalizedName = toCamelCase(name);
|
|
1878
1899
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
1879
|
-
const defaultDir =
|
|
1900
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseFeaturesDir, normalizedName));
|
|
1880
1901
|
let targetDirRel = defaultDir;
|
|
1881
1902
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
1882
1903
|
targetDirRel = handleCancel(
|
|
@@ -1887,7 +1908,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1887
1908
|
})
|
|
1888
1909
|
).trim();
|
|
1889
1910
|
}
|
|
1890
|
-
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);
|
|
1891
1912
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
1892
1913
|
let routePath = `/${normalizedName}`;
|
|
1893
1914
|
let includeDomain = true;
|
|
@@ -2129,8 +2150,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2129
2150
|
};
|
|
2130
2151
|
const files = generateFeature(options);
|
|
2131
2152
|
if (isRegistry) {
|
|
2132
|
-
const registryPath =
|
|
2133
|
-
if (!
|
|
2153
|
+
const registryPath = path26__default.default.join(targetDir, "registry.json");
|
|
2154
|
+
if (!fs17__default.default.existsSync(registryPath)) {
|
|
2134
2155
|
const manifest = {
|
|
2135
2156
|
name: toKebabCase(normalizedName),
|
|
2136
2157
|
type: "feature",
|
|
@@ -2156,7 +2177,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2156
2177
|
printGeneratedFiles(files, options.dryRun);
|
|
2157
2178
|
p19__namespace.outro(
|
|
2158
2179
|
pc13__default.default.green(
|
|
2159
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2180
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2160
2181
|
)
|
|
2161
2182
|
);
|
|
2162
2183
|
}
|
|
@@ -2167,7 +2188,7 @@ function generateShared(options) {
|
|
|
2167
2188
|
const pascalName = toPascalCase(rawName);
|
|
2168
2189
|
const camelName = toCamelCase(rawName);
|
|
2169
2190
|
const kebabName = toKebabCase(rawName);
|
|
2170
|
-
const targetDir = options.targetDir ||
|
|
2191
|
+
const targetDir = options.targetDir || path26__default.default.join(resolveSharedDir(projectRoot), kebabName);
|
|
2171
2192
|
const fileDefs = [];
|
|
2172
2193
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2173
2194
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2179,7 +2200,7 @@ export class ${pascalName} {
|
|
|
2179
2200
|
}
|
|
2180
2201
|
`;
|
|
2181
2202
|
fileDefs.push({
|
|
2182
|
-
absolutePath:
|
|
2203
|
+
absolutePath: path26__default.default.join(targetDir, `${pascalName}.ts`),
|
|
2183
2204
|
content: facadeContent
|
|
2184
2205
|
});
|
|
2185
2206
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2187,7 +2208,7 @@ export class ${pascalName} {
|
|
|
2187
2208
|
}
|
|
2188
2209
|
`;
|
|
2189
2210
|
fileDefs.push({
|
|
2190
|
-
absolutePath:
|
|
2211
|
+
absolutePath: path26__default.default.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2191
2212
|
content: portContent
|
|
2192
2213
|
});
|
|
2193
2214
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2199,7 +2220,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2199
2220
|
}
|
|
2200
2221
|
`;
|
|
2201
2222
|
fileDefs.push({
|
|
2202
|
-
absolutePath:
|
|
2223
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2203
2224
|
content: concreteContent
|
|
2204
2225
|
});
|
|
2205
2226
|
if (options.includeStub !== false) {
|
|
@@ -2212,7 +2233,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2212
2233
|
}
|
|
2213
2234
|
`;
|
|
2214
2235
|
fileDefs.push({
|
|
2215
|
-
absolutePath:
|
|
2236
|
+
absolutePath: path26__default.default.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2216
2237
|
content: stubContent
|
|
2217
2238
|
});
|
|
2218
2239
|
}
|
|
@@ -2227,7 +2248,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2227
2248
|
export default diProvider;
|
|
2228
2249
|
`;
|
|
2229
2250
|
fileDefs.push({
|
|
2230
|
-
absolutePath:
|
|
2251
|
+
absolutePath: path26__default.default.join(targetDir, "diProvider.ts"),
|
|
2231
2252
|
content: providerContent
|
|
2232
2253
|
});
|
|
2233
2254
|
if (options.isRegistry) {
|
|
@@ -2244,7 +2265,7 @@ export default diProvider;
|
|
|
2244
2265
|
files: []
|
|
2245
2266
|
};
|
|
2246
2267
|
fileDefs.push({
|
|
2247
|
-
absolutePath:
|
|
2268
|
+
absolutePath: path26__default.default.join(targetDir, "registry.json"),
|
|
2248
2269
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2249
2270
|
});
|
|
2250
2271
|
}
|
|
@@ -2281,7 +2302,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2281
2302
|
}
|
|
2282
2303
|
const normalizedName = toKebabCase(name);
|
|
2283
2304
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2284
|
-
const defaultDir =
|
|
2305
|
+
const defaultDir = path26__default.default.relative(projectRoot, path26__default.default.join(baseSharedDir, normalizedName));
|
|
2285
2306
|
let targetDirRel = defaultDir;
|
|
2286
2307
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2287
2308
|
targetDirRel = handleCancel(
|
|
@@ -2292,7 +2313,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2292
2313
|
})
|
|
2293
2314
|
).trim();
|
|
2294
2315
|
}
|
|
2295
|
-
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);
|
|
2296
2317
|
let description = flags.description || "";
|
|
2297
2318
|
let includeStub = true;
|
|
2298
2319
|
if (!flags.yes) {
|
|
@@ -2326,7 +2347,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2326
2347
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2327
2348
|
p19__namespace.outro(
|
|
2328
2349
|
pc13__default.default.green(
|
|
2329
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2350
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2330
2351
|
)
|
|
2331
2352
|
);
|
|
2332
2353
|
}
|
|
@@ -2753,7 +2774,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2753
2774
|
const infraFile = generateInfrastructure(options);
|
|
2754
2775
|
files.push(infraFile);
|
|
2755
2776
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
2756
|
-
const featureDir =
|
|
2777
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
2757
2778
|
const providerFile = updateOrGenerateDiProvider(
|
|
2758
2779
|
featureDir,
|
|
2759
2780
|
feature,
|
|
@@ -2915,7 +2936,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
2915
2936
|
files.push(stubFile);
|
|
2916
2937
|
}
|
|
2917
2938
|
if (registerInProvider && generateStub) {
|
|
2918
|
-
const featureDir =
|
|
2939
|
+
const featureDir = path26__default.default.join(resolveFeaturesDir(projectRoot), feature);
|
|
2919
2940
|
const providerFile = updateOrGenerateDiProvider(
|
|
2920
2941
|
featureDir,
|
|
2921
2942
|
feature,
|
|
@@ -3103,21 +3124,21 @@ function generateInjectable(options) {
|
|
|
3103
3124
|
const className = toPascalCase(options.name);
|
|
3104
3125
|
let targetDir = "";
|
|
3105
3126
|
if (options.customPath) {
|
|
3106
|
-
targetDir =
|
|
3127
|
+
targetDir = path26__default.default.isAbsolute(options.customPath) ? options.customPath : path26__default.default.join(projectRoot, options.customPath);
|
|
3107
3128
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3108
|
-
targetDir =
|
|
3129
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3109
3130
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3110
|
-
targetDir =
|
|
3131
|
+
targetDir = path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3111
3132
|
} else if (options.location === "module") {
|
|
3112
|
-
targetDir =
|
|
3133
|
+
targetDir = path26__default.default.join(projectRoot, "modules", className);
|
|
3113
3134
|
} else {
|
|
3114
|
-
targetDir = options.feature ?
|
|
3135
|
+
targetDir = options.feature ? path26__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26__default.default.join(projectRoot, "shared", className.toLowerCase());
|
|
3115
3136
|
}
|
|
3116
|
-
const filePath =
|
|
3137
|
+
const filePath = path26__default.default.join(targetDir, `${className}.ts`);
|
|
3117
3138
|
let ifaceName;
|
|
3118
3139
|
if (options.generateInterface) {
|
|
3119
3140
|
ifaceName = `I${className}`;
|
|
3120
|
-
const ifacePath =
|
|
3141
|
+
const ifacePath = path26__default.default.join(targetDir, `${ifaceName}.ts`);
|
|
3121
3142
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3122
3143
|
files.push(
|
|
3123
3144
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3246,7 +3267,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3246
3267
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3247
3268
|
const s = p19__namespace.spinner();
|
|
3248
3269
|
s.start("Scanning for .example environment files...");
|
|
3249
|
-
const files =
|
|
3270
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3250
3271
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3251
3272
|
if (exampleFiles.length === 0) {
|
|
3252
3273
|
s.stop("No .example files found.");
|
|
@@ -3256,11 +3277,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3256
3277
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3257
3278
|
const generatedFiles = [];
|
|
3258
3279
|
for (const exampleFile of exampleFiles) {
|
|
3259
|
-
const sourcePath =
|
|
3280
|
+
const sourcePath = path26__default.default.join(projectRoot, exampleFile);
|
|
3260
3281
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3261
|
-
const targetPath =
|
|
3282
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3262
3283
|
let overwrite = flags.overwrite;
|
|
3263
|
-
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)) {
|
|
3264
3285
|
overwrite = await p19__namespace.confirm({
|
|
3265
3286
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3266
3287
|
initialValue: false
|
|
@@ -3270,7 +3291,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3270
3291
|
process.exit(0);
|
|
3271
3292
|
}
|
|
3272
3293
|
}
|
|
3273
|
-
const content =
|
|
3294
|
+
const content = fs17__default.default.readFileSync(sourcePath, "utf-8");
|
|
3274
3295
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3275
3296
|
projectRoot,
|
|
3276
3297
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3288,7 +3309,7 @@ var EnvEditor = class {
|
|
|
3288
3309
|
filePath;
|
|
3289
3310
|
constructor(filePath) {
|
|
3290
3311
|
this.filePath = filePath;
|
|
3291
|
-
this.lines =
|
|
3312
|
+
this.lines = fs17__default.default.existsSync(filePath) ? fs17__default.default.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3292
3313
|
}
|
|
3293
3314
|
getLines() {
|
|
3294
3315
|
return [...this.lines];
|
|
@@ -3352,18 +3373,18 @@ var EnvEditor = class {
|
|
|
3352
3373
|
});
|
|
3353
3374
|
}
|
|
3354
3375
|
save() {
|
|
3355
|
-
const dir =
|
|
3356
|
-
if (!
|
|
3357
|
-
|
|
3376
|
+
const dir = path26__default.default.dirname(this.filePath);
|
|
3377
|
+
if (!fs17__default.default.existsSync(dir)) {
|
|
3378
|
+
fs17__default.default.mkdirSync(dir, { recursive: true });
|
|
3358
3379
|
}
|
|
3359
|
-
|
|
3380
|
+
fs17__default.default.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3360
3381
|
}
|
|
3361
3382
|
};
|
|
3362
3383
|
function discoverEnvironments(projectRoot) {
|
|
3363
|
-
if (!
|
|
3384
|
+
if (!fs17__default.default.existsSync(projectRoot)) {
|
|
3364
3385
|
return ["local", "dev", "prod"];
|
|
3365
3386
|
}
|
|
3366
|
-
const files =
|
|
3387
|
+
const files = fs17__default.default.readdirSync(projectRoot);
|
|
3367
3388
|
const envs = /* @__PURE__ */ new Set();
|
|
3368
3389
|
for (const file of files) {
|
|
3369
3390
|
if (file.startsWith(".env")) {
|
|
@@ -3388,7 +3409,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3388
3409
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3389
3410
|
const s = p19__namespace.spinner();
|
|
3390
3411
|
s.start("Scanning for local environment files...");
|
|
3391
|
-
const files =
|
|
3412
|
+
const files = fs17__default.default.readdirSync(projectRoot, { withFileTypes: true });
|
|
3392
3413
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3393
3414
|
if (envFiles.length === 0) {
|
|
3394
3415
|
s.stop("No local .env files found.");
|
|
@@ -3398,11 +3419,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3398
3419
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3399
3420
|
const generatedFiles = [];
|
|
3400
3421
|
for (const envFile of envFiles) {
|
|
3401
|
-
const sourcePath =
|
|
3422
|
+
const sourcePath = path26__default.default.join(projectRoot, envFile);
|
|
3402
3423
|
const targetFile = `${envFile}.example`;
|
|
3403
|
-
const targetPath =
|
|
3424
|
+
const targetPath = path26__default.default.join(projectRoot, targetFile);
|
|
3404
3425
|
let overwrite = flags.overwrite;
|
|
3405
|
-
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)) {
|
|
3406
3427
|
overwrite = await p19__namespace.confirm({
|
|
3407
3428
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3408
3429
|
initialValue: true
|
|
@@ -3435,12 +3456,12 @@ function isInteractive(flags) {
|
|
|
3435
3456
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3436
3457
|
const keys = /* @__PURE__ */ new Set();
|
|
3437
3458
|
for (const env of envs) {
|
|
3438
|
-
const localPath =
|
|
3439
|
-
const examplePath =
|
|
3440
|
-
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)) {
|
|
3441
3462
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3442
3463
|
}
|
|
3443
|
-
if (
|
|
3464
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3444
3465
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3445
3466
|
}
|
|
3446
3467
|
}
|
|
@@ -3512,8 +3533,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3512
3533
|
})
|
|
3513
3534
|
);
|
|
3514
3535
|
}
|
|
3515
|
-
const localPath =
|
|
3516
|
-
const examplePath =
|
|
3536
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3537
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3517
3538
|
if (flags.dryRun) {
|
|
3518
3539
|
p19__namespace.log.info(
|
|
3519
3540
|
pc13__default.default.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3560,8 +3581,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3560
3581
|
);
|
|
3561
3582
|
}
|
|
3562
3583
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3563
|
-
const localPath =
|
|
3564
|
-
return
|
|
3584
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3585
|
+
return fs17__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3565
3586
|
});
|
|
3566
3587
|
let targetEnvs = [];
|
|
3567
3588
|
if (flags.env) {
|
|
@@ -3587,7 +3608,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3587
3608
|
return { key: targetKey, environments: [] };
|
|
3588
3609
|
}
|
|
3589
3610
|
for (const env of targetEnvs) {
|
|
3590
|
-
const localPath =
|
|
3611
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3591
3612
|
const localEditor = new EnvEditor(localPath);
|
|
3592
3613
|
const currentVal = localEditor.getValue(targetKey);
|
|
3593
3614
|
let newVal = value;
|
|
@@ -3617,7 +3638,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3617
3638
|
} else {
|
|
3618
3639
|
localEditor.set(targetKey, newVal);
|
|
3619
3640
|
localEditor.save();
|
|
3620
|
-
const examplePath =
|
|
3641
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3621
3642
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3622
3643
|
if (!exampleEditor.has(targetKey)) {
|
|
3623
3644
|
exampleEditor.set(targetKey, "");
|
|
@@ -3653,9 +3674,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3653
3674
|
);
|
|
3654
3675
|
}
|
|
3655
3676
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3656
|
-
const localPath =
|
|
3657
|
-
const examplePath =
|
|
3658
|
-
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);
|
|
3659
3680
|
});
|
|
3660
3681
|
let targetEnvs = [];
|
|
3661
3682
|
if (flags.env) {
|
|
@@ -3693,19 +3714,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3693
3714
|
}
|
|
3694
3715
|
}
|
|
3695
3716
|
for (const env of targetEnvs) {
|
|
3696
|
-
const localPath =
|
|
3697
|
-
const examplePath =
|
|
3717
|
+
const localPath = path26__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3718
|
+
const examplePath = path26__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3698
3719
|
if (flags.dryRun) {
|
|
3699
3720
|
p19__namespace.log.info(
|
|
3700
3721
|
pc13__default.default.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3701
3722
|
);
|
|
3702
3723
|
} else {
|
|
3703
|
-
if (
|
|
3724
|
+
if (fs17__default.default.existsSync(localPath)) {
|
|
3704
3725
|
const localEditor = new EnvEditor(localPath);
|
|
3705
3726
|
localEditor.delete(targetKey);
|
|
3706
3727
|
localEditor.save();
|
|
3707
3728
|
}
|
|
3708
|
-
if (
|
|
3729
|
+
if (fs17__default.default.existsSync(examplePath)) {
|
|
3709
3730
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3710
3731
|
exampleEditor.delete(targetKey);
|
|
3711
3732
|
exampleEditor.save();
|
|
@@ -3730,10 +3751,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
3730
3751
|
for (const env of envs) {
|
|
3731
3752
|
const localFile = resolveEnvFilename(env, false);
|
|
3732
3753
|
const exampleFile = resolveEnvFilename(env, true);
|
|
3733
|
-
const localPath =
|
|
3734
|
-
const examplePath =
|
|
3735
|
-
const localExists =
|
|
3736
|
-
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);
|
|
3737
3758
|
const localEditor = new EnvEditor(localPath);
|
|
3738
3759
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3739
3760
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4060,25 +4081,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
4060
4081
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
4061
4082
|
}
|
|
4062
4083
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
4063
|
-
return
|
|
4084
|
+
return path26__default.default.resolve(baseDir, trimmed);
|
|
4064
4085
|
}
|
|
4065
4086
|
return trimmed;
|
|
4066
4087
|
}
|
|
4067
4088
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
4068
|
-
let current =
|
|
4089
|
+
let current = path26__default.default.resolve(startDir);
|
|
4069
4090
|
while (true) {
|
|
4070
|
-
const candidate =
|
|
4071
|
-
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"))) {
|
|
4072
4093
|
return candidate;
|
|
4073
4094
|
}
|
|
4074
|
-
const parent =
|
|
4095
|
+
const parent = path26__default.default.dirname(current);
|
|
4075
4096
|
if (parent === current) break;
|
|
4076
4097
|
current = parent;
|
|
4077
4098
|
}
|
|
4078
4099
|
return null;
|
|
4079
4100
|
}
|
|
4080
4101
|
function getRegistryBase(projectRoot, override) {
|
|
4081
|
-
const rootDir = projectRoot ?
|
|
4102
|
+
const rootDir = projectRoot ? path26__default.default.resolve(projectRoot) : process.cwd();
|
|
4082
4103
|
if (override && override.trim()) {
|
|
4083
4104
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
4084
4105
|
}
|
|
@@ -4086,30 +4107,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
4086
4107
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
4087
4108
|
}
|
|
4088
4109
|
const configLocations = [
|
|
4089
|
-
|
|
4090
|
-
|
|
4110
|
+
path26__default.default.join(rootDir, "mason.config.json"),
|
|
4111
|
+
path26__default.default.join(process.cwd(), "mason.config.json")
|
|
4091
4112
|
];
|
|
4092
4113
|
for (const configPath of configLocations) {
|
|
4093
|
-
if (
|
|
4114
|
+
if (fs17__default.default.existsSync(configPath)) {
|
|
4094
4115
|
try {
|
|
4095
|
-
const config = JSON.parse(
|
|
4116
|
+
const config = JSON.parse(fs17__default.default.readFileSync(configPath, "utf-8"));
|
|
4096
4117
|
if (config.registry && typeof config.registry === "string") {
|
|
4097
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
4118
|
+
return normalizeRegistryUrl(config.registry.trim(), path26__default.default.dirname(configPath));
|
|
4098
4119
|
}
|
|
4099
4120
|
} catch {
|
|
4100
4121
|
}
|
|
4101
4122
|
}
|
|
4102
4123
|
}
|
|
4103
4124
|
const pkgLocations = [
|
|
4104
|
-
|
|
4105
|
-
|
|
4125
|
+
path26__default.default.join(rootDir, "package.json"),
|
|
4126
|
+
path26__default.default.join(process.cwd(), "package.json")
|
|
4106
4127
|
];
|
|
4107
4128
|
for (const pkgPath of pkgLocations) {
|
|
4108
|
-
if (
|
|
4129
|
+
if (fs17__default.default.existsSync(pkgPath)) {
|
|
4109
4130
|
try {
|
|
4110
|
-
const pkg = JSON.parse(
|
|
4131
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgPath, "utf-8"));
|
|
4111
4132
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
4112
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
4133
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26__default.default.dirname(pkgPath));
|
|
4113
4134
|
}
|
|
4114
4135
|
} catch {
|
|
4115
4136
|
}
|
|
@@ -4157,16 +4178,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
4157
4178
|
if (baseDir.startsWith("file://")) {
|
|
4158
4179
|
baseDir = url.fileURLToPath(baseDir);
|
|
4159
4180
|
}
|
|
4160
|
-
let filePath =
|
|
4161
|
-
if (!
|
|
4162
|
-
const inSrcPath =
|
|
4163
|
-
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)) {
|
|
4164
4185
|
filePath = inSrcPath;
|
|
4165
4186
|
} else {
|
|
4166
4187
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
4167
4188
|
}
|
|
4168
4189
|
}
|
|
4169
|
-
return
|
|
4190
|
+
return fs17__default.default.promises.readFile(filePath, "utf-8");
|
|
4170
4191
|
}
|
|
4171
4192
|
async function fetchRegistryIndex(registryBase) {
|
|
4172
4193
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -4228,19 +4249,19 @@ function findClosestMatch(target, candidates) {
|
|
|
4228
4249
|
return null;
|
|
4229
4250
|
}
|
|
4230
4251
|
function detectPackageManager(projectRoot) {
|
|
4231
|
-
if (
|
|
4232
|
-
if (
|
|
4233
|
-
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";
|
|
4234
4255
|
return "npm";
|
|
4235
4256
|
}
|
|
4236
4257
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
4237
4258
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
4238
|
-
const pkgJsonPath =
|
|
4239
|
-
if (!
|
|
4259
|
+
const pkgJsonPath = path26__default.default.join(projectRoot, "package.json");
|
|
4260
|
+
if (!fs17__default.default.existsSync(pkgJsonPath)) {
|
|
4240
4261
|
return requiredPackages;
|
|
4241
4262
|
}
|
|
4242
4263
|
try {
|
|
4243
|
-
const pkg = JSON.parse(
|
|
4264
|
+
const pkg = JSON.parse(fs17__default.default.readFileSync(pkgJsonPath, "utf-8"));
|
|
4244
4265
|
const installed = /* @__PURE__ */ new Set([
|
|
4245
4266
|
...Object.keys(pkg.dependencies || {}),
|
|
4246
4267
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -4279,12 +4300,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
4279
4300
|
});
|
|
4280
4301
|
}
|
|
4281
4302
|
function getAllFiles(dir, baseDir = dir) {
|
|
4282
|
-
if (!
|
|
4283
|
-
const entries =
|
|
4303
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
4304
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4284
4305
|
const files = [];
|
|
4285
4306
|
for (const entry of entries) {
|
|
4286
|
-
const fullPath =
|
|
4287
|
-
const relPath =
|
|
4307
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
4308
|
+
const relPath = path26__default.default.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
4288
4309
|
if (entry.isDirectory()) {
|
|
4289
4310
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4290
4311
|
continue;
|
|
@@ -4300,13 +4321,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
4300
4321
|
return files.sort();
|
|
4301
4322
|
}
|
|
4302
4323
|
function computeDirectoryHash(dirPath, files) {
|
|
4303
|
-
if (!
|
|
4324
|
+
if (!fs17__default.default.existsSync(dirPath)) return "";
|
|
4304
4325
|
const hash = crypto.createHash("sha256");
|
|
4305
4326
|
const fileList = (getAllFiles(dirPath)).sort();
|
|
4306
4327
|
for (const relativePath of fileList) {
|
|
4307
|
-
const absolutePath =
|
|
4308
|
-
if (!
|
|
4309
|
-
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");
|
|
4310
4331
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
4311
4332
|
hash.update(relativePath);
|
|
4312
4333
|
hash.update(normalizedContent);
|
|
@@ -4344,17 +4365,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4344
4365
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4345
4366
|
}
|
|
4346
4367
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4347
|
-
return
|
|
4368
|
+
return path26__default.default.join(projectRoot, "src", folder);
|
|
4348
4369
|
}
|
|
4349
4370
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4350
4371
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4351
4372
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4352
|
-
const inConfigured =
|
|
4353
|
-
if (
|
|
4354
|
-
const inSrc =
|
|
4355
|
-
if (
|
|
4356
|
-
const inRoot =
|
|
4357
|
-
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;
|
|
4358
4379
|
return null;
|
|
4359
4380
|
}
|
|
4360
4381
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4365,14 +4386,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4365
4386
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4366
4387
|
const dirs = [
|
|
4367
4388
|
configuredDir,
|
|
4368
|
-
|
|
4369
|
-
|
|
4389
|
+
path26__default.default.join(projectRoot, "src", folder),
|
|
4390
|
+
path26__default.default.join(projectRoot, folder)
|
|
4370
4391
|
];
|
|
4371
4392
|
const set = /* @__PURE__ */ new Set();
|
|
4372
4393
|
for (const dir of dirs) {
|
|
4373
|
-
if (
|
|
4394
|
+
if (fs17__default.default.existsSync(dir)) {
|
|
4374
4395
|
try {
|
|
4375
|
-
const entries =
|
|
4396
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4376
4397
|
for (const e of entries) {
|
|
4377
4398
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4378
4399
|
set.add(e.name);
|
|
@@ -4574,7 +4595,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4574
4595
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4575
4596
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4576
4597
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4577
|
-
const displayDest =
|
|
4598
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, programmaticAlias));
|
|
4578
4599
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4579
4600
|
resolutions.set(key, {
|
|
4580
4601
|
type: modType,
|
|
@@ -4762,7 +4783,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4762
4783
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4763
4784
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4764
4785
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4765
|
-
const displayDest =
|
|
4786
|
+
const displayDest = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, newName));
|
|
4766
4787
|
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4767
4788
|
resolutions.set(key, {
|
|
4768
4789
|
type: modType,
|
|
@@ -4814,7 +4835,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4814
4835
|
continue;
|
|
4815
4836
|
}
|
|
4816
4837
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4817
|
-
const targetDir =
|
|
4838
|
+
const targetDir = path26__default.default.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4818
4839
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4819
4840
|
if (key !== rootKey) {
|
|
4820
4841
|
p19__namespace.log.step(
|
|
@@ -4824,30 +4845,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4824
4845
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4825
4846
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4826
4847
|
if (existingDir && existingDir !== targetDir) {
|
|
4827
|
-
|
|
4848
|
+
fs17__default.default.rmSync(existingDir, { recursive: true, force: true });
|
|
4828
4849
|
}
|
|
4829
|
-
if (
|
|
4830
|
-
|
|
4850
|
+
if (fs17__default.default.existsSync(targetDir)) {
|
|
4851
|
+
fs17__default.default.rmSync(targetDir, { recursive: true, force: true });
|
|
4831
4852
|
}
|
|
4832
4853
|
}
|
|
4833
4854
|
for (const file of res.manifest.files || []) {
|
|
4834
4855
|
if (file === "registry.json") continue;
|
|
4835
4856
|
if (!includeTests && isTestFile(file)) continue;
|
|
4836
4857
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4837
|
-
const localFilePath =
|
|
4838
|
-
if (
|
|
4858
|
+
const localFilePath = path26__default.default.join(targetDir, file);
|
|
4859
|
+
if (fs17__default.default.existsSync(localFilePath) && !isOverwrite) {
|
|
4839
4860
|
p19__namespace.log.warn(
|
|
4840
|
-
` \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)`
|
|
4841
4862
|
);
|
|
4842
4863
|
continue;
|
|
4843
4864
|
}
|
|
4844
4865
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4845
4866
|
content = rewriteImports(content, globalAliasMap);
|
|
4846
4867
|
if (!flags.dryRun) {
|
|
4847
|
-
|
|
4848
|
-
|
|
4868
|
+
fs17__default.default.mkdirSync(path26__default.default.dirname(localFilePath), { recursive: true });
|
|
4869
|
+
fs17__default.default.writeFileSync(localFilePath, content, "utf-8");
|
|
4849
4870
|
}
|
|
4850
|
-
const displayPath =
|
|
4871
|
+
const displayPath = path26__default.default.relative(projectRoot, localFilePath);
|
|
4851
4872
|
totalWrittenFiles.push(displayPath);
|
|
4852
4873
|
p19__namespace.log.message(` ${pc13__default.default.green("+")} ${displayPath}`);
|
|
4853
4874
|
}
|
|
@@ -4873,7 +4894,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4873
4894
|
} else {
|
|
4874
4895
|
const rootRes = resolutions.get(rootKey);
|
|
4875
4896
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4876
|
-
const displayTarget =
|
|
4897
|
+
const displayTarget = path26__default.default.relative(projectRoot, path26__default.default.join(baseDir, rootRes.targetName));
|
|
4877
4898
|
p19__namespace.log.success(
|
|
4878
4899
|
pc13__default.default.green(
|
|
4879
4900
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4892,19 +4913,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4892
4913
|
}
|
|
4893
4914
|
};
|
|
4894
4915
|
function isTestFile2(filePath, projectRoot) {
|
|
4895
|
-
const norm = (projectRoot ?
|
|
4896
|
-
const base =
|
|
4916
|
+
const norm = (projectRoot ? path26__default.default.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4917
|
+
const base = path26__default.default.basename(norm);
|
|
4897
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);
|
|
4898
4919
|
}
|
|
4899
4920
|
function scanDirFiles(dir, projectRoot) {
|
|
4900
|
-
if (!
|
|
4901
|
-
const entries =
|
|
4921
|
+
if (!fs17__default.default.existsSync(dir)) return [];
|
|
4922
|
+
const entries = fs17__default.default.readdirSync(dir, { withFileTypes: true });
|
|
4902
4923
|
const files = [];
|
|
4903
4924
|
for (const entry of entries) {
|
|
4904
4925
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4905
4926
|
continue;
|
|
4906
4927
|
}
|
|
4907
|
-
const fullPath =
|
|
4928
|
+
const fullPath = path26__default.default.join(dir, entry.name);
|
|
4908
4929
|
if (entry.isDirectory()) {
|
|
4909
4930
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4910
4931
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4913,42 +4934,19 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4913
4934
|
}
|
|
4914
4935
|
return files;
|
|
4915
4936
|
}
|
|
4916
|
-
function classifyFile(
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
} else if (subfolder === "infrastructure") {
|
|
4930
|
-
layer = "infrastructure";
|
|
4931
|
-
} else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
|
|
4932
|
-
layer = "presentation";
|
|
4933
|
-
}
|
|
4934
|
-
return { moduleType: "feature", moduleName: feature, feature, layer };
|
|
4935
|
-
}
|
|
4936
|
-
const sharedMatch = rel.match(/(?:^|\/)(?:src\/)?shared\/([^/]+)(?:\/(.*))?$/);
|
|
4937
|
-
if (sharedMatch && sharedMatch[1]) {
|
|
4938
|
-
const moduleName = sharedMatch[1];
|
|
4939
|
-
const rest = sharedMatch[2] || "";
|
|
4940
|
-
let layer = "root";
|
|
4941
|
-
if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
|
|
4942
|
-
layer = "domain";
|
|
4943
|
-
} else if (rest.startsWith("ports/") || rest === "ports") {
|
|
4944
|
-
layer = "ports";
|
|
4945
|
-
} else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
|
|
4946
|
-
layer = "infrastructure";
|
|
4947
|
-
}
|
|
4948
|
-
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4949
|
-
}
|
|
4950
|
-
const errorMatch = rel.match(/(?:^|\/)(?:src\/)?errors(?:\/(.*))?$/);
|
|
4951
|
-
if (errorMatch) {
|
|
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)) {
|
|
4952
4950
|
return {
|
|
4953
4951
|
moduleType: "error",
|
|
4954
4952
|
moduleName: "global-errors",
|
|
@@ -4956,46 +4954,154 @@ function classifyFile(fullPath, projectRoot) {
|
|
|
4956
4954
|
layer: "domain"
|
|
4957
4955
|
};
|
|
4958
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
|
+
}
|
|
4994
|
+
}
|
|
4959
4995
|
return {};
|
|
4960
4996
|
}
|
|
4961
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
4997
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4962
4998
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4963
4999
|
return { isExternal: true, isDiFramework: true };
|
|
4964
5000
|
}
|
|
4965
|
-
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)) {
|
|
4966
5015
|
return { isExternal: true, isDiFramework: false };
|
|
4967
5016
|
}
|
|
4968
|
-
|
|
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;
|
|
4969
5025
|
if (specifier.startsWith(".")) {
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
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 };
|
|
4995
5098
|
}
|
|
4996
|
-
const
|
|
4997
|
-
const
|
|
4998
|
-
|
|
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") {
|
|
4999
5105
|
return {
|
|
5000
5106
|
isExternal: false,
|
|
5001
5107
|
isDiFramework: false,
|
|
@@ -5003,33 +5109,19 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5003
5109
|
isTargetError: true
|
|
5004
5110
|
};
|
|
5005
5111
|
}
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
const
|
|
5009
|
-
const targetSubfolder =
|
|
5010
|
-
const
|
|
5011
|
-
let targetLayer;
|
|
5012
|
-
if (targetSubfolder === "domain" || targetSubfolder === "errors") {
|
|
5013
|
-
targetLayer = "domain";
|
|
5014
|
-
} else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
|
|
5015
|
-
targetLayer = "useCases";
|
|
5016
|
-
} else if (targetSubfolder === "services") {
|
|
5017
|
-
targetLayer = "services";
|
|
5018
|
-
} else if (targetSubfolder === "infrastructure") {
|
|
5019
|
-
targetLayer = "infrastructure";
|
|
5020
|
-
} else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
|
|
5021
|
-
targetLayer = "presentation";
|
|
5022
|
-
}
|
|
5023
|
-
const basePart = rest ? path2__default.default.basename(rest) : targetSubfolder;
|
|
5024
|
-
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)$/, "");
|
|
5025
5117
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
5026
5118
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
5027
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5119
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
5028
5120
|
return {
|
|
5029
5121
|
isExternal: false,
|
|
5030
5122
|
isDiFramework: false,
|
|
5031
|
-
targetFeature,
|
|
5032
|
-
targetLayer,
|
|
5123
|
+
targetFeature: classification.moduleName,
|
|
5124
|
+
targetLayer: classification.layer,
|
|
5033
5125
|
targetSubfolder,
|
|
5034
5126
|
targetFileName,
|
|
5035
5127
|
isTargetPort,
|
|
@@ -5037,25 +5129,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5037
5129
|
isTargetError
|
|
5038
5130
|
};
|
|
5039
5131
|
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
const
|
|
5043
|
-
const sharedRest = sharedMatch[2] || "";
|
|
5044
|
-
const isSharedRoot = !sharedRest.includes("/");
|
|
5045
|
-
const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
|
|
5046
|
-
const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
|
|
5047
|
-
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/"));
|
|
5048
5135
|
return {
|
|
5049
5136
|
isExternal: false,
|
|
5050
5137
|
isDiFramework: false,
|
|
5051
|
-
targetSharedModule,
|
|
5052
|
-
isSharedRoot,
|
|
5053
|
-
isSharedPorts,
|
|
5054
|
-
isSharedInfra,
|
|
5055
|
-
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
|
|
5056
5144
|
};
|
|
5057
5145
|
}
|
|
5058
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5146
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
5059
5147
|
}
|
|
5060
5148
|
function extractImportStatements(content) {
|
|
5061
5149
|
const statements = [];
|
|
@@ -5177,22 +5265,50 @@ var FeatureDependencyGraph = class {
|
|
|
5177
5265
|
return dfs(start);
|
|
5178
5266
|
}
|
|
5179
5267
|
};
|
|
5180
|
-
function discoverAllFeatures(
|
|
5181
|
-
const
|
|
5182
|
-
if (!
|
|
5183
|
-
const
|
|
5184
|
-
|
|
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) => ({
|
|
5185
5287
|
name: e.name,
|
|
5186
|
-
dir:
|
|
5288
|
+
dir: path26__default.default.join(base, e.name)
|
|
5187
5289
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5188
5290
|
}
|
|
5189
|
-
function discoverAllShared(
|
|
5190
|
-
const
|
|
5191
|
-
if (!
|
|
5192
|
-
const
|
|
5193
|
-
|
|
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) => ({
|
|
5194
5310
|
name: e.name,
|
|
5195
|
-
dir:
|
|
5311
|
+
dir: path26__default.default.join(base, e.name)
|
|
5196
5312
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5197
5313
|
}
|
|
5198
5314
|
function extractClassMethods(content, className) {
|
|
@@ -5348,7 +5464,7 @@ function extractClassMethods(content, className) {
|
|
|
5348
5464
|
return methods;
|
|
5349
5465
|
}
|
|
5350
5466
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5351
|
-
const fileName =
|
|
5467
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5352
5468
|
const lines = content.split(/\r?\n/);
|
|
5353
5469
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5354
5470
|
if (!classMatch) {
|
|
@@ -5444,7 +5560,7 @@ private ${m.name}(...) { ... }`
|
|
|
5444
5560
|
}
|
|
5445
5561
|
}
|
|
5446
5562
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5447
|
-
const fileName =
|
|
5563
|
+
const fileName = path26__default.default.basename(filePath);
|
|
5448
5564
|
const lines = content.split(/\r?\n/);
|
|
5449
5565
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5450
5566
|
if (!classMatch) {
|
|
@@ -5544,18 +5660,11 @@ function lintCodebase(options = {}) {
|
|
|
5544
5660
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5545
5661
|
const failFast = !options.complete;
|
|
5546
5662
|
const lintConfig = loadLintConfig(projectRoot);
|
|
5547
|
-
const
|
|
5548
|
-
const baseErrorAbs = path2__default.default.resolve(projectRoot, baseErrorRelative);
|
|
5549
|
-
const featuresBase = resolveFeaturesDir(projectRoot);
|
|
5550
|
-
const sharedBase = resolveSharedDir(projectRoot);
|
|
5551
|
-
let errorsBase = path2__default.default.join(projectRoot, "src/errors");
|
|
5552
|
-
if (!fs14__default.default.existsSync(errorsBase) && fs14__default.default.existsSync(path2__default.default.join(projectRoot, "errors"))) {
|
|
5553
|
-
errorsBase = path2__default.default.join(projectRoot, "errors");
|
|
5554
|
-
}
|
|
5663
|
+
const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
|
|
5555
5664
|
const missingFoundations = [];
|
|
5556
|
-
if (!
|
|
5557
|
-
if (!
|
|
5558
|
-
if (!
|
|
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/");
|
|
5559
5668
|
if (missingFoundations.length > 0) {
|
|
5560
5669
|
throw new DependencyRuleError(
|
|
5561
5670
|
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
@@ -5563,20 +5672,22 @@ function lintCodebase(options = {}) {
|
|
|
5563
5672
|
true
|
|
5564
5673
|
);
|
|
5565
5674
|
}
|
|
5566
|
-
|
|
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)) {
|
|
5567
5678
|
throw new DependencyRuleError(
|
|
5568
5679
|
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5569
5680
|
[],
|
|
5570
5681
|
true
|
|
5571
5682
|
);
|
|
5572
5683
|
}
|
|
5573
|
-
const baseErrorContent =
|
|
5684
|
+
const baseErrorContent = fs17__default.default.readFileSync(baseErrorAbs, "utf-8");
|
|
5574
5685
|
const baseClassMatch = baseErrorContent.match(
|
|
5575
5686
|
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5576
5687
|
);
|
|
5577
5688
|
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5578
|
-
const features = discoverAllFeatures(
|
|
5579
|
-
const sharedModules = discoverAllShared(
|
|
5689
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5690
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5580
5691
|
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5581
5692
|
const allModules = [
|
|
5582
5693
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
@@ -5619,12 +5730,12 @@ Code: ${v.code}`;
|
|
|
5619
5730
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5620
5731
|
modResult.scannedFilesCount = allFiles.length;
|
|
5621
5732
|
for (const file of allFiles) {
|
|
5622
|
-
const relPath =
|
|
5623
|
-
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);
|
|
5624
5735
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5625
|
-
const content =
|
|
5736
|
+
const content = fs17__default.default.readFileSync(file, "utf-8");
|
|
5626
5737
|
const statements = extractImportStatements(content);
|
|
5627
|
-
const baseName =
|
|
5738
|
+
const baseName = path26__default.default.basename(file);
|
|
5628
5739
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5629
5740
|
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5630
5741
|
let throwMatch;
|
|
@@ -5651,7 +5762,7 @@ Code: ${v.code}`;
|
|
|
5651
5762
|
});
|
|
5652
5763
|
}
|
|
5653
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");
|
|
5654
|
-
const isBaseErrorFile =
|
|
5765
|
+
const isBaseErrorFile = path26__default.default.resolve(file) === path26__default.default.resolve(baseErrorAbs);
|
|
5655
5766
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5656
5767
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5657
5768
|
let classMatch;
|
|
@@ -5701,7 +5812,7 @@ Code: ${v.code}`;
|
|
|
5701
5812
|
}
|
|
5702
5813
|
if (moduleType === "error") {
|
|
5703
5814
|
for (const stmt of statements) {
|
|
5704
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5815
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5705
5816
|
if (target.targetFeature) {
|
|
5706
5817
|
addViolation({
|
|
5707
5818
|
feature: "global-errors",
|
|
@@ -5767,7 +5878,7 @@ Code: ${v.code}`;
|
|
|
5767
5878
|
}
|
|
5768
5879
|
}
|
|
5769
5880
|
for (const stmt of statements) {
|
|
5770
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5881
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5771
5882
|
if (target.targetFeature) {
|
|
5772
5883
|
addViolation({
|
|
5773
5884
|
feature: moduleName,
|
|
@@ -5897,7 +6008,7 @@ Code: ${v.code}`;
|
|
|
5897
6008
|
}
|
|
5898
6009
|
}
|
|
5899
6010
|
for (const stmt of statements) {
|
|
5900
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
6011
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5901
6012
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5902
6013
|
const evidence = {
|
|
5903
6014
|
fromFeature: feat.name,
|