@solid-stack/mason 1.0.32 → 1.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +648 -356
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +646 -354
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +5 -4
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +652 -358
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +649 -357
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +10 -2
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +13 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { cac } from 'cac';
|
|
3
3
|
import * as p19 from '@clack/prompts';
|
|
4
4
|
import pc13 from 'picocolors';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import fs17 from 'fs';
|
|
6
|
+
import path26 from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import Handlebars from 'handlebars';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
@@ -11,8 +11,8 @@ import { createHash } from 'crypto';
|
|
|
11
11
|
|
|
12
12
|
function writeFileSafely(absolutePath, content, options = {}) {
|
|
13
13
|
const projectRoot = options.projectRoot || process.cwd();
|
|
14
|
-
const relPath =
|
|
15
|
-
const existedBefore =
|
|
14
|
+
const relPath = path26.relative(projectRoot, absolutePath).replace(/\\/g, "/");
|
|
15
|
+
const existedBefore = fs17.existsSync(absolutePath);
|
|
16
16
|
if (options.dryRun) {
|
|
17
17
|
return {
|
|
18
18
|
relativePath: relPath,
|
|
@@ -31,11 +31,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
|
|
|
31
31
|
existedBefore: true
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
const dir =
|
|
35
|
-
if (!
|
|
36
|
-
|
|
34
|
+
const dir = path26.dirname(absolutePath);
|
|
35
|
+
if (!fs17.existsSync(dir)) {
|
|
36
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
fs17.writeFileSync(absolutePath, content, "utf-8");
|
|
39
39
|
return {
|
|
40
40
|
relativePath: relPath,
|
|
41
41
|
absolutePath,
|
|
@@ -126,17 +126,17 @@ function toStubName(ifaceName) {
|
|
|
126
126
|
return `Stub${base}`;
|
|
127
127
|
}
|
|
128
128
|
function loadMasonConfig(startDir = process.cwd()) {
|
|
129
|
-
let current =
|
|
129
|
+
let current = path26.resolve(startDir);
|
|
130
130
|
while (true) {
|
|
131
|
-
const configPath =
|
|
132
|
-
if (
|
|
131
|
+
const configPath = path26.join(current, "mason.config.json");
|
|
132
|
+
if (fs17.existsSync(configPath)) {
|
|
133
133
|
try {
|
|
134
|
-
return JSON.parse(
|
|
134
|
+
return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
135
135
|
} catch {
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
const parent =
|
|
139
|
+
const parent = path26.dirname(current);
|
|
140
140
|
if (parent === current) break;
|
|
141
141
|
current = parent;
|
|
142
142
|
}
|
|
@@ -146,60 +146,91 @@ function getEffectiveMode(projectRoot, flags) {
|
|
|
146
146
|
if (flags?.registry) return "registry";
|
|
147
147
|
const config = loadMasonConfig(projectRoot);
|
|
148
148
|
if (config?.mode) return config.mode;
|
|
149
|
-
if (
|
|
149
|
+
if (fs17.existsSync(path26.join(projectRoot, "index.json")) && (fs17.existsSync(path26.join(projectRoot, "features")) || fs17.existsSync(path26.join(projectRoot, "shared")) || fs17.existsSync(path26.join(projectRoot, "src/features")) || fs17.existsSync(path26.join(projectRoot, "src/shared")))) {
|
|
150
150
|
return "registry";
|
|
151
151
|
}
|
|
152
152
|
return "app";
|
|
153
153
|
}
|
|
154
154
|
function getConfiguredFeaturesDir(projectRoot, flags) {
|
|
155
|
-
if (flags?.target) return
|
|
156
|
-
if (flags?.dest) return
|
|
155
|
+
if (flags?.target) return path26.resolve(projectRoot, flags.target);
|
|
156
|
+
if (flags?.dest) return path26.resolve(projectRoot, flags.dest);
|
|
157
157
|
const config = loadMasonConfig(projectRoot);
|
|
158
158
|
if (config?.paths?.features) {
|
|
159
|
-
return
|
|
159
|
+
return path26.resolve(projectRoot, config.paths.features);
|
|
160
160
|
}
|
|
161
161
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
162
162
|
if (mode === "registry") {
|
|
163
|
-
if (
|
|
164
|
-
return
|
|
163
|
+
if (fs17.existsSync(path26.join(projectRoot, "src/features"))) {
|
|
164
|
+
return path26.join(projectRoot, "src/features");
|
|
165
165
|
}
|
|
166
|
-
return
|
|
166
|
+
return path26.join(projectRoot, "features");
|
|
167
167
|
}
|
|
168
|
-
if (
|
|
169
|
-
return
|
|
168
|
+
if (fs17.existsSync(path26.join(projectRoot, "src/features"))) {
|
|
169
|
+
return path26.join(projectRoot, "src/features");
|
|
170
170
|
}
|
|
171
|
-
return
|
|
171
|
+
return path26.join(projectRoot, "features");
|
|
172
172
|
}
|
|
173
173
|
function getConfiguredSharedDir(projectRoot, flags) {
|
|
174
|
-
if (flags?.target) return
|
|
175
|
-
if (flags?.dest) return
|
|
174
|
+
if (flags?.target) return path26.resolve(projectRoot, flags.target);
|
|
175
|
+
if (flags?.dest) return path26.resolve(projectRoot, flags.dest);
|
|
176
176
|
const config = loadMasonConfig(projectRoot);
|
|
177
177
|
if (config?.paths?.shared) {
|
|
178
|
-
return
|
|
178
|
+
return path26.resolve(projectRoot, config.paths.shared);
|
|
179
179
|
}
|
|
180
180
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
181
181
|
if (mode === "registry") {
|
|
182
|
-
if (
|
|
183
|
-
return
|
|
182
|
+
if (fs17.existsSync(path26.join(projectRoot, "src/shared"))) {
|
|
183
|
+
return path26.join(projectRoot, "src/shared");
|
|
184
184
|
}
|
|
185
|
-
return
|
|
185
|
+
return path26.join(projectRoot, "shared");
|
|
186
186
|
}
|
|
187
|
-
if (
|
|
188
|
-
return
|
|
187
|
+
if (fs17.existsSync(path26.join(projectRoot, "src/shared"))) {
|
|
188
|
+
return path26.join(projectRoot, "src/shared");
|
|
189
189
|
}
|
|
190
|
-
return
|
|
190
|
+
return path26.join(projectRoot, "shared");
|
|
191
|
+
}
|
|
192
|
+
function loadLintConfig(projectRoot) {
|
|
193
|
+
const configPath = path26.join(projectRoot, "mason-lint.config.json");
|
|
194
|
+
if (fs17.existsSync(configPath)) {
|
|
195
|
+
try {
|
|
196
|
+
return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return {};
|
|
201
|
+
}
|
|
202
|
+
function getResolvedLintPaths(projectRoot, config) {
|
|
203
|
+
const masonConfig = loadMasonConfig(projectRoot);
|
|
204
|
+
const configuredFeatures = config.paths?.features ?? masonConfig?.paths?.features;
|
|
205
|
+
const configuredShared = config.paths?.shared ?? masonConfig?.paths?.shared;
|
|
206
|
+
const configuredErrors = config.paths?.errors;
|
|
207
|
+
const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
|
|
208
|
+
if (configuredPath !== void 0) {
|
|
209
|
+
return path26.resolve(projectRoot, configuredPath);
|
|
210
|
+
}
|
|
211
|
+
const srcPath = path26.join(projectRoot, defaultSrc);
|
|
212
|
+
if (fs17.existsSync(srcPath)) return srcPath;
|
|
213
|
+
const rootPath = path26.join(projectRoot, defaultRoot);
|
|
214
|
+
if (fs17.existsSync(rootPath)) return rootPath;
|
|
215
|
+
return srcPath;
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
featuresBase: resolveBase(configuredFeatures, "src/features", "features"),
|
|
219
|
+
sharedBase: resolveBase(configuredShared, "src/shared", "shared"),
|
|
220
|
+
errorsBase: resolveBase(configuredErrors, "src/errors", "errors")
|
|
221
|
+
};
|
|
191
222
|
}
|
|
192
223
|
|
|
193
224
|
// src/utils/project.ts
|
|
194
225
|
function findProjectRoot(startDir = process.cwd()) {
|
|
195
|
-
let current =
|
|
226
|
+
let current = path26.resolve(startDir);
|
|
196
227
|
while (true) {
|
|
197
|
-
if (
|
|
228
|
+
if (fs17.existsSync(path26.join(current, "package.json"))) {
|
|
198
229
|
return current;
|
|
199
230
|
}
|
|
200
|
-
const parent =
|
|
231
|
+
const parent = path26.dirname(current);
|
|
201
232
|
if (parent === current) {
|
|
202
|
-
return
|
|
233
|
+
return path26.resolve(startDir);
|
|
203
234
|
}
|
|
204
235
|
current = parent;
|
|
205
236
|
}
|
|
@@ -212,9 +243,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
212
243
|
}
|
|
213
244
|
function listExistingFeatures(projectRoot) {
|
|
214
245
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
215
|
-
if (!
|
|
246
|
+
if (!fs17.existsSync(featuresDir)) return [];
|
|
216
247
|
try {
|
|
217
|
-
const entries =
|
|
248
|
+
const entries = fs17.readdirSync(featuresDir, { withFileTypes: true });
|
|
218
249
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
219
250
|
} catch {
|
|
220
251
|
return [];
|
|
@@ -224,14 +255,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
224
255
|
const results = [];
|
|
225
256
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
226
257
|
for (const feat of features) {
|
|
227
|
-
const domainDir =
|
|
228
|
-
if (!
|
|
258
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
259
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
229
260
|
try {
|
|
230
|
-
const files =
|
|
261
|
+
const files = fs17.readdirSync(domainDir);
|
|
231
262
|
for (const file of files) {
|
|
232
263
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
233
264
|
const name = file.replace(/\.ts$/, "");
|
|
234
|
-
const fullPath =
|
|
265
|
+
const fullPath = path26.join(domainDir, file);
|
|
235
266
|
if (/^I[A-Z]/.test(name)) {
|
|
236
267
|
results.push({
|
|
237
268
|
name,
|
|
@@ -240,7 +271,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
240
271
|
});
|
|
241
272
|
} else {
|
|
242
273
|
try {
|
|
243
|
-
const content =
|
|
274
|
+
const content = fs17.readFileSync(fullPath, "utf-8");
|
|
244
275
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
245
276
|
results.push({
|
|
246
277
|
name,
|
|
@@ -262,17 +293,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
262
293
|
const results = [];
|
|
263
294
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
264
295
|
for (const feat of features) {
|
|
265
|
-
const domainDir =
|
|
266
|
-
if (!
|
|
296
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
297
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
267
298
|
try {
|
|
268
|
-
const files =
|
|
299
|
+
const files = fs17.readdirSync(domainDir);
|
|
269
300
|
for (const file of files) {
|
|
270
301
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
271
302
|
const name = file.replace(/\.ts$/, "");
|
|
272
303
|
if (!/^I[A-Z]/.test(name)) {
|
|
273
304
|
results.push({
|
|
274
305
|
name,
|
|
275
|
-
fullPath:
|
|
306
|
+
fullPath: path26.join(domainDir, file),
|
|
276
307
|
feature: feat
|
|
277
308
|
});
|
|
278
309
|
}
|
|
@@ -287,16 +318,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
287
318
|
const results = [];
|
|
288
319
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
289
320
|
for (const feat of features) {
|
|
290
|
-
const useCasesDir =
|
|
291
|
-
if (!
|
|
321
|
+
const useCasesDir = path26.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
322
|
+
if (!fs17.existsSync(useCasesDir)) continue;
|
|
292
323
|
try {
|
|
293
|
-
const files =
|
|
324
|
+
const files = fs17.readdirSync(useCasesDir);
|
|
294
325
|
for (const file of files) {
|
|
295
326
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
296
327
|
const name = file.replace(/\.ts$/, "");
|
|
297
328
|
results.push({
|
|
298
329
|
name,
|
|
299
|
-
fullPath:
|
|
330
|
+
fullPath: path26.join(useCasesDir, file),
|
|
300
331
|
feature: feat
|
|
301
332
|
});
|
|
302
333
|
}
|
|
@@ -322,33 +353,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
322
353
|
});
|
|
323
354
|
var cachedTemplatesDir = null;
|
|
324
355
|
function getTemplatesDir() {
|
|
325
|
-
if (cachedTemplatesDir &&
|
|
356
|
+
if (cachedTemplatesDir && fs17.existsSync(cachedTemplatesDir)) {
|
|
326
357
|
return cachedTemplatesDir;
|
|
327
358
|
}
|
|
328
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
329
|
-
const cand1 =
|
|
330
|
-
if (
|
|
359
|
+
const currentDir = typeof __dirname !== "undefined" ? __dirname : path26.dirname(fileURLToPath(import.meta.url));
|
|
360
|
+
const cand1 = path26.resolve(currentDir, "../../templates");
|
|
361
|
+
if (fs17.existsSync(cand1)) {
|
|
331
362
|
cachedTemplatesDir = cand1;
|
|
332
363
|
return cand1;
|
|
333
364
|
}
|
|
334
|
-
const cand2 =
|
|
335
|
-
if (
|
|
365
|
+
const cand2 = path26.resolve(currentDir, "../templates");
|
|
366
|
+
if (fs17.existsSync(cand2)) {
|
|
336
367
|
cachedTemplatesDir = cand2;
|
|
337
368
|
return cand2;
|
|
338
369
|
}
|
|
339
|
-
const cand3 =
|
|
340
|
-
if (
|
|
370
|
+
const cand3 = path26.resolve(currentDir, "templates");
|
|
371
|
+
if (fs17.existsSync(cand3)) {
|
|
341
372
|
cachedTemplatesDir = cand3;
|
|
342
373
|
return cand3;
|
|
343
374
|
}
|
|
344
375
|
let curr = currentDir;
|
|
345
376
|
while (true) {
|
|
346
|
-
const candTemplates =
|
|
347
|
-
if (
|
|
377
|
+
const candTemplates = path26.join(curr, "templates");
|
|
378
|
+
if (fs17.existsSync(candTemplates)) {
|
|
348
379
|
cachedTemplatesDir = candTemplates;
|
|
349
380
|
return candTemplates;
|
|
350
381
|
}
|
|
351
|
-
const parent =
|
|
382
|
+
const parent = path26.dirname(curr);
|
|
352
383
|
if (parent === curr) break;
|
|
353
384
|
curr = parent;
|
|
354
385
|
}
|
|
@@ -356,8 +387,8 @@ function getTemplatesDir() {
|
|
|
356
387
|
}
|
|
357
388
|
var templateCache = /* @__PURE__ */ new Map();
|
|
358
389
|
function loadTemplate(relativePath) {
|
|
359
|
-
const fullPath =
|
|
360
|
-
return
|
|
390
|
+
const fullPath = path26.isAbsolute(relativePath) ? relativePath : path26.join(getTemplatesDir(), relativePath);
|
|
391
|
+
return fs17.readFileSync(fullPath, "utf-8");
|
|
361
392
|
}
|
|
362
393
|
function renderTemplateFile(relativePath, context) {
|
|
363
394
|
let compiled = templateCache.get(relativePath);
|
|
@@ -444,8 +475,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
444
475
|
function generateDomain(options) {
|
|
445
476
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
446
477
|
const entityName = toPascalCase(options.name);
|
|
447
|
-
const targetDir = options.targetDir ||
|
|
448
|
-
const filePath =
|
|
478
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
479
|
+
const filePath = path26.join(targetDir, `${entityName}.ts`);
|
|
449
480
|
const code = generateDomainEntityCode(
|
|
450
481
|
entityName,
|
|
451
482
|
options.kind || "interface",
|
|
@@ -494,8 +525,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
494
525
|
function generateInterface(options) {
|
|
495
526
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
496
527
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
497
|
-
const targetDir = options.targetDir ||
|
|
498
|
-
const filePath =
|
|
528
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
529
|
+
const filePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
499
530
|
const code = generateInterfaceCode(
|
|
500
531
|
ifaceName,
|
|
501
532
|
options.kind || "abstractClass",
|
|
@@ -730,7 +761,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
730
761
|
};
|
|
731
762
|
}
|
|
732
763
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
733
|
-
if (customPath &&
|
|
764
|
+
if (customPath && fs17.existsSync(customPath)) {
|
|
734
765
|
return { filePath: customPath, ifaceName };
|
|
735
766
|
}
|
|
736
767
|
const candidateNames = [
|
|
@@ -740,22 +771,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
740
771
|
];
|
|
741
772
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
742
773
|
for (const feat of features) {
|
|
743
|
-
const domainDir =
|
|
744
|
-
if (!
|
|
774
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
775
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
745
776
|
for (const cand of candidateNames) {
|
|
746
|
-
const p1 =
|
|
747
|
-
if (
|
|
777
|
+
const p1 = path26.join(domainDir, `${cand}.ts`);
|
|
778
|
+
if (fs17.existsSync(p1)) {
|
|
748
779
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
749
780
|
}
|
|
750
781
|
}
|
|
751
782
|
}
|
|
752
783
|
for (const cand of candidateNames) {
|
|
753
|
-
const p1 =
|
|
754
|
-
if (
|
|
755
|
-
const p22 =
|
|
756
|
-
if (
|
|
757
|
-
const p32 =
|
|
758
|
-
if (
|
|
784
|
+
const p1 = path26.join(projectRoot, "domain", `${cand}.ts`);
|
|
785
|
+
if (fs17.existsSync(p1)) return { filePath: p1, ifaceName: cand };
|
|
786
|
+
const p22 = path26.join(projectRoot, "src", "domain", `${cand}.ts`);
|
|
787
|
+
if (fs17.existsSync(p22)) return { filePath: p22, ifaceName: cand };
|
|
788
|
+
const p32 = path26.join(projectRoot, "interfaces", `${cand}.ts`);
|
|
789
|
+
if (fs17.existsSync(p32)) return { filePath: p32, ifaceName: cand };
|
|
759
790
|
}
|
|
760
791
|
return null;
|
|
761
792
|
}
|
|
@@ -778,7 +809,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
778
809
|
while (currentFile && !visited.has(currentFile)) {
|
|
779
810
|
visited.add(currentFile);
|
|
780
811
|
try {
|
|
781
|
-
const content =
|
|
812
|
+
const content = fs17.readFileSync(currentFile, "utf-8");
|
|
782
813
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
783
814
|
for (const m of parsed.methods) {
|
|
784
815
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -854,7 +885,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
854
885
|
const entitiesVar = `${entityVar}s`;
|
|
855
886
|
const idKey = `${entityVar}Id`;
|
|
856
887
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
857
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
888
|
+
const entityImportPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
858
889
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
859
890
|
for (const imp of domainImports) {
|
|
860
891
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -903,7 +934,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
903
934
|
if (baseEntity) {
|
|
904
935
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
905
936
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
906
|
-
const importPath = ifaceImportPath ? `${
|
|
937
|
+
const importPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
907
938
|
finalDomainImports.push({
|
|
908
939
|
name: baseEntity,
|
|
909
940
|
from: importPath,
|
|
@@ -948,8 +979,8 @@ function generateInfrastructure(options) {
|
|
|
948
979
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
949
980
|
const className = toPascalCase(options.name);
|
|
950
981
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
951
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
952
|
-
const filePath =
|
|
982
|
+
const targetDir = options.targetDir || (isFeatureScope ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path26.join(projectRoot, "infrastructure"));
|
|
983
|
+
const filePath = path26.join(targetDir, `${className}.ts`);
|
|
953
984
|
let code;
|
|
954
985
|
if (options.implementsInterface) {
|
|
955
986
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -963,7 +994,7 @@ function generateInfrastructure(options) {
|
|
|
963
994
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
964
995
|
let ifaceImportPath;
|
|
965
996
|
if (ifaceFilePath) {
|
|
966
|
-
let rel =
|
|
997
|
+
let rel = path26.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
967
998
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
968
999
|
ifaceImportPath = rel;
|
|
969
1000
|
} else if (isFeatureScope) {
|
|
@@ -976,8 +1007,8 @@ function generateInfrastructure(options) {
|
|
|
976
1007
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
977
1008
|
let fromPath;
|
|
978
1009
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
979
|
-
const absTarget =
|
|
980
|
-
let rel =
|
|
1010
|
+
const absTarget = path26.resolve(path26.dirname(ifaceFilePath), imp.specifier);
|
|
1011
|
+
let rel = path26.relative(targetDir, absTarget).replace(/\\/g, "/");
|
|
981
1012
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
982
1013
|
fromPath = rel;
|
|
983
1014
|
} else {
|
|
@@ -1164,8 +1195,8 @@ function generateUseCase(options) {
|
|
|
1164
1195
|
const files = [];
|
|
1165
1196
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1166
1197
|
const ucName = toPascalCase(options.name);
|
|
1167
|
-
const targetDir = options.targetDir ||
|
|
1168
|
-
const filePath =
|
|
1198
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
|
|
1199
|
+
const filePath = path26.join(targetDir, `${ucName}.ts`);
|
|
1169
1200
|
const code = generateUseCaseCode(
|
|
1170
1201
|
ucName,
|
|
1171
1202
|
options.dependencies || [],
|
|
@@ -1181,7 +1212,7 @@ function generateUseCase(options) {
|
|
|
1181
1212
|
})
|
|
1182
1213
|
);
|
|
1183
1214
|
if (options.includeTest) {
|
|
1184
|
-
const testPath =
|
|
1215
|
+
const testPath = path26.join(targetDir, `${ucName}.test.ts`);
|
|
1185
1216
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1186
1217
|
files.push(
|
|
1187
1218
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1249,10 +1280,10 @@ function generateHttpHandler(options) {
|
|
|
1249
1280
|
const files = [];
|
|
1250
1281
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1251
1282
|
const className = toPascalCase(options.name);
|
|
1252
|
-
const pxDir =
|
|
1253
|
-
const handlersDir =
|
|
1254
|
-
const pxIndexPath =
|
|
1255
|
-
if (!
|
|
1283
|
+
const pxDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
|
|
1284
|
+
const handlersDir = path26.join(pxDir, "handlers");
|
|
1285
|
+
const pxIndexPath = path26.join(pxDir, "index.ts");
|
|
1286
|
+
if (!fs17.existsSync(pxIndexPath)) {
|
|
1256
1287
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1257
1288
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1258
1289
|
routePath: featurePath
|
|
@@ -1265,7 +1296,7 @@ function generateHttpHandler(options) {
|
|
|
1265
1296
|
})
|
|
1266
1297
|
);
|
|
1267
1298
|
}
|
|
1268
|
-
const handlerFilePath =
|
|
1299
|
+
const handlerFilePath = path26.join(handlersDir, `${className}.ts`);
|
|
1269
1300
|
const code = generateHttpHandlerCode(
|
|
1270
1301
|
className,
|
|
1271
1302
|
options.method || "get",
|
|
@@ -1286,11 +1317,11 @@ function generateHttpHandler(options) {
|
|
|
1286
1317
|
return files;
|
|
1287
1318
|
}
|
|
1288
1319
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1289
|
-
const providerPath =
|
|
1320
|
+
const providerPath = path26.join(featureDir, "diProvider.ts");
|
|
1290
1321
|
const pascalFeature = toPascalCase(featureName);
|
|
1291
1322
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1292
|
-
if (
|
|
1293
|
-
let content =
|
|
1323
|
+
if (fs17.existsSync(providerPath) && !options.overwrite) {
|
|
1324
|
+
let content = fs17.readFileSync(providerPath, "utf-8");
|
|
1294
1325
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1295
1326
|
for (const b of bindings) {
|
|
1296
1327
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1349,7 +1380,7 @@ function generateFeature(options) {
|
|
|
1349
1380
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1350
1381
|
const rawName = options.name.trim();
|
|
1351
1382
|
const featureName = toCamelCase(rawName);
|
|
1352
|
-
const targetDir = options.targetDir ||
|
|
1383
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1353
1384
|
const routePath = options.routePath || `/${featureName}`;
|
|
1354
1385
|
const rawEntities = [];
|
|
1355
1386
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1370,7 +1401,7 @@ function generateFeature(options) {
|
|
|
1370
1401
|
const domainFile = generateDomain({
|
|
1371
1402
|
name: ent,
|
|
1372
1403
|
feature: featureName,
|
|
1373
|
-
targetDir:
|
|
1404
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1374
1405
|
projectRoot,
|
|
1375
1406
|
dryRun: options.dryRun,
|
|
1376
1407
|
overwrite: options.overwrite,
|
|
@@ -1390,7 +1421,7 @@ function generateFeature(options) {
|
|
|
1390
1421
|
const ifaceFile = generateInterface({
|
|
1391
1422
|
name: ifaceName,
|
|
1392
1423
|
feature: featureName,
|
|
1393
|
-
targetDir:
|
|
1424
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1394
1425
|
entityName: ent,
|
|
1395
1426
|
projectRoot,
|
|
1396
1427
|
dryRun: options.dryRun,
|
|
@@ -1407,7 +1438,7 @@ function generateFeature(options) {
|
|
|
1407
1438
|
const stubFile = generateInfrastructure({
|
|
1408
1439
|
name: stubName,
|
|
1409
1440
|
feature: featureName,
|
|
1410
|
-
targetDir:
|
|
1441
|
+
targetDir: path26.join(targetDir, "infrastructure"),
|
|
1411
1442
|
kind: "stub",
|
|
1412
1443
|
implementsInterface: ifaceName,
|
|
1413
1444
|
entityName: ent,
|
|
@@ -1449,7 +1480,7 @@ function generateFeature(options) {
|
|
|
1449
1480
|
const ucFiles = generateUseCase({
|
|
1450
1481
|
name: ucName,
|
|
1451
1482
|
feature: featureName,
|
|
1452
|
-
targetDir:
|
|
1483
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1453
1484
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1454
1485
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1455
1486
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1494,7 +1525,7 @@ function generateFeature(options) {
|
|
|
1494
1525
|
const ucFiles = generateUseCase({
|
|
1495
1526
|
name: ucName,
|
|
1496
1527
|
feature: featureName,
|
|
1497
|
-
targetDir:
|
|
1528
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1498
1529
|
dependencies: deps,
|
|
1499
1530
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1500
1531
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1527,7 +1558,7 @@ function generateFeature(options) {
|
|
|
1527
1558
|
const ucFiles = generateUseCase({
|
|
1528
1559
|
name: ucName,
|
|
1529
1560
|
feature: featureName,
|
|
1530
|
-
targetDir:
|
|
1561
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1531
1562
|
dependencies: deps,
|
|
1532
1563
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1533
1564
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1560,7 +1591,7 @@ function generateFeature(options) {
|
|
|
1560
1591
|
const ucFiles = generateUseCase({
|
|
1561
1592
|
name: ucName,
|
|
1562
1593
|
feature: featureName,
|
|
1563
|
-
targetDir:
|
|
1594
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1564
1595
|
dependencies: deps,
|
|
1565
1596
|
inputProperties: [
|
|
1566
1597
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1604,7 +1635,7 @@ function generateFeature(options) {
|
|
|
1604
1635
|
const ucFiles = generateUseCase({
|
|
1605
1636
|
name: ucName,
|
|
1606
1637
|
feature: featureName,
|
|
1607
|
-
targetDir:
|
|
1638
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1608
1639
|
dependencies: deps,
|
|
1609
1640
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1610
1641
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1637,7 +1668,7 @@ function generateFeature(options) {
|
|
|
1637
1668
|
const ucFiles = generateUseCase({
|
|
1638
1669
|
name: ucName,
|
|
1639
1670
|
feature: featureName,
|
|
1640
|
-
targetDir:
|
|
1671
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1641
1672
|
dependencies: deps,
|
|
1642
1673
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1643
1674
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -1674,7 +1705,7 @@ function generateFeature(options) {
|
|
|
1674
1705
|
const ucFiles = generateUseCase({
|
|
1675
1706
|
name: ucName,
|
|
1676
1707
|
feature: featureName,
|
|
1677
|
-
targetDir:
|
|
1708
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1678
1709
|
dependencies: customUc.dependencies || [],
|
|
1679
1710
|
inputProperties: customUc.inputProperties || [],
|
|
1680
1711
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -1706,8 +1737,8 @@ function generateFeature(options) {
|
|
|
1706
1737
|
}
|
|
1707
1738
|
}
|
|
1708
1739
|
if (options.includeExpress !== false) {
|
|
1709
|
-
const pxDir =
|
|
1710
|
-
const pxIndexPath =
|
|
1740
|
+
const pxDir = path26.join(targetDir, "pxExpress");
|
|
1741
|
+
const pxIndexPath = path26.join(pxDir, "index.ts");
|
|
1711
1742
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
1712
1743
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
1713
1744
|
files.push(
|
|
@@ -1837,7 +1868,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1837
1868
|
}
|
|
1838
1869
|
const normalizedName = toCamelCase(name);
|
|
1839
1870
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
1840
|
-
const defaultDir =
|
|
1871
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseFeaturesDir, normalizedName));
|
|
1841
1872
|
let targetDirRel = defaultDir;
|
|
1842
1873
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
1843
1874
|
targetDirRel = handleCancel(
|
|
@@ -1848,7 +1879,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1848
1879
|
})
|
|
1849
1880
|
).trim();
|
|
1850
1881
|
}
|
|
1851
|
-
const targetDir = flags.dest ?
|
|
1882
|
+
const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
|
|
1852
1883
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
1853
1884
|
let routePath = `/${normalizedName}`;
|
|
1854
1885
|
let includeDomain = true;
|
|
@@ -2090,8 +2121,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2090
2121
|
};
|
|
2091
2122
|
const files = generateFeature(options);
|
|
2092
2123
|
if (isRegistry) {
|
|
2093
|
-
const registryPath =
|
|
2094
|
-
if (!
|
|
2124
|
+
const registryPath = path26.join(targetDir, "registry.json");
|
|
2125
|
+
if (!fs17.existsSync(registryPath)) {
|
|
2095
2126
|
const manifest = {
|
|
2096
2127
|
name: toKebabCase(normalizedName),
|
|
2097
2128
|
type: "feature",
|
|
@@ -2117,7 +2148,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2117
2148
|
printGeneratedFiles(files, options.dryRun);
|
|
2118
2149
|
p19.outro(
|
|
2119
2150
|
pc13.green(
|
|
2120
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2151
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2121
2152
|
)
|
|
2122
2153
|
);
|
|
2123
2154
|
}
|
|
@@ -2128,7 +2159,7 @@ function generateShared(options) {
|
|
|
2128
2159
|
const pascalName = toPascalCase(rawName);
|
|
2129
2160
|
const camelName = toCamelCase(rawName);
|
|
2130
2161
|
const kebabName = toKebabCase(rawName);
|
|
2131
|
-
const targetDir = options.targetDir ||
|
|
2162
|
+
const targetDir = options.targetDir || path26.join(resolveSharedDir(projectRoot), kebabName);
|
|
2132
2163
|
const fileDefs = [];
|
|
2133
2164
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2134
2165
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2140,7 +2171,7 @@ export class ${pascalName} {
|
|
|
2140
2171
|
}
|
|
2141
2172
|
`;
|
|
2142
2173
|
fileDefs.push({
|
|
2143
|
-
absolutePath:
|
|
2174
|
+
absolutePath: path26.join(targetDir, `${pascalName}.ts`),
|
|
2144
2175
|
content: facadeContent
|
|
2145
2176
|
});
|
|
2146
2177
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2148,7 +2179,7 @@ export class ${pascalName} {
|
|
|
2148
2179
|
}
|
|
2149
2180
|
`;
|
|
2150
2181
|
fileDefs.push({
|
|
2151
|
-
absolutePath:
|
|
2182
|
+
absolutePath: path26.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2152
2183
|
content: portContent
|
|
2153
2184
|
});
|
|
2154
2185
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2160,7 +2191,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2160
2191
|
}
|
|
2161
2192
|
`;
|
|
2162
2193
|
fileDefs.push({
|
|
2163
|
-
absolutePath:
|
|
2194
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2164
2195
|
content: concreteContent
|
|
2165
2196
|
});
|
|
2166
2197
|
if (options.includeStub !== false) {
|
|
@@ -2173,7 +2204,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2173
2204
|
}
|
|
2174
2205
|
`;
|
|
2175
2206
|
fileDefs.push({
|
|
2176
|
-
absolutePath:
|
|
2207
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2177
2208
|
content: stubContent
|
|
2178
2209
|
});
|
|
2179
2210
|
}
|
|
@@ -2188,7 +2219,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2188
2219
|
export default diProvider;
|
|
2189
2220
|
`;
|
|
2190
2221
|
fileDefs.push({
|
|
2191
|
-
absolutePath:
|
|
2222
|
+
absolutePath: path26.join(targetDir, "diProvider.ts"),
|
|
2192
2223
|
content: providerContent
|
|
2193
2224
|
});
|
|
2194
2225
|
if (options.isRegistry) {
|
|
@@ -2205,7 +2236,7 @@ export default diProvider;
|
|
|
2205
2236
|
files: []
|
|
2206
2237
|
};
|
|
2207
2238
|
fileDefs.push({
|
|
2208
|
-
absolutePath:
|
|
2239
|
+
absolutePath: path26.join(targetDir, "registry.json"),
|
|
2209
2240
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2210
2241
|
});
|
|
2211
2242
|
}
|
|
@@ -2242,7 +2273,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2242
2273
|
}
|
|
2243
2274
|
const normalizedName = toKebabCase(name);
|
|
2244
2275
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2245
|
-
const defaultDir =
|
|
2276
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseSharedDir, normalizedName));
|
|
2246
2277
|
let targetDirRel = defaultDir;
|
|
2247
2278
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2248
2279
|
targetDirRel = handleCancel(
|
|
@@ -2253,7 +2284,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2253
2284
|
})
|
|
2254
2285
|
).trim();
|
|
2255
2286
|
}
|
|
2256
|
-
const targetDir = flags.dest ?
|
|
2287
|
+
const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
|
|
2257
2288
|
let description = flags.description || "";
|
|
2258
2289
|
let includeStub = true;
|
|
2259
2290
|
if (!flags.yes) {
|
|
@@ -2287,7 +2318,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2287
2318
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2288
2319
|
p19.outro(
|
|
2289
2320
|
pc13.green(
|
|
2290
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2321
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2291
2322
|
)
|
|
2292
2323
|
);
|
|
2293
2324
|
}
|
|
@@ -2714,7 +2745,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2714
2745
|
const infraFile = generateInfrastructure(options);
|
|
2715
2746
|
files.push(infraFile);
|
|
2716
2747
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
2717
|
-
const featureDir =
|
|
2748
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2718
2749
|
const providerFile = updateOrGenerateDiProvider(
|
|
2719
2750
|
featureDir,
|
|
2720
2751
|
feature,
|
|
@@ -2876,7 +2907,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
2876
2907
|
files.push(stubFile);
|
|
2877
2908
|
}
|
|
2878
2909
|
if (registerInProvider && generateStub) {
|
|
2879
|
-
const featureDir =
|
|
2910
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2880
2911
|
const providerFile = updateOrGenerateDiProvider(
|
|
2881
2912
|
featureDir,
|
|
2882
2913
|
feature,
|
|
@@ -3064,21 +3095,21 @@ function generateInjectable(options) {
|
|
|
3064
3095
|
const className = toPascalCase(options.name);
|
|
3065
3096
|
let targetDir = "";
|
|
3066
3097
|
if (options.customPath) {
|
|
3067
|
-
targetDir =
|
|
3098
|
+
targetDir = path26.isAbsolute(options.customPath) ? options.customPath : path26.join(projectRoot, options.customPath);
|
|
3068
3099
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3069
|
-
targetDir =
|
|
3100
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3070
3101
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3071
|
-
targetDir =
|
|
3102
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3072
3103
|
} else if (options.location === "module") {
|
|
3073
|
-
targetDir =
|
|
3104
|
+
targetDir = path26.join(projectRoot, "modules", className);
|
|
3074
3105
|
} else {
|
|
3075
|
-
targetDir = options.feature ?
|
|
3106
|
+
targetDir = options.feature ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26.join(projectRoot, "shared", className.toLowerCase());
|
|
3076
3107
|
}
|
|
3077
|
-
const filePath =
|
|
3108
|
+
const filePath = path26.join(targetDir, `${className}.ts`);
|
|
3078
3109
|
let ifaceName;
|
|
3079
3110
|
if (options.generateInterface) {
|
|
3080
3111
|
ifaceName = `I${className}`;
|
|
3081
|
-
const ifacePath =
|
|
3112
|
+
const ifacePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
3082
3113
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3083
3114
|
files.push(
|
|
3084
3115
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3207,7 +3238,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3207
3238
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3208
3239
|
const s = p19.spinner();
|
|
3209
3240
|
s.start("Scanning for .example environment files...");
|
|
3210
|
-
const files =
|
|
3241
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3211
3242
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3212
3243
|
if (exampleFiles.length === 0) {
|
|
3213
3244
|
s.stop("No .example files found.");
|
|
@@ -3217,11 +3248,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3217
3248
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3218
3249
|
const generatedFiles = [];
|
|
3219
3250
|
for (const exampleFile of exampleFiles) {
|
|
3220
|
-
const sourcePath =
|
|
3251
|
+
const sourcePath = path26.join(projectRoot, exampleFile);
|
|
3221
3252
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3222
|
-
const targetPath =
|
|
3253
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3223
3254
|
let overwrite = flags.overwrite;
|
|
3224
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3255
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
|
|
3225
3256
|
overwrite = await p19.confirm({
|
|
3226
3257
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3227
3258
|
initialValue: false
|
|
@@ -3231,7 +3262,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3231
3262
|
process.exit(0);
|
|
3232
3263
|
}
|
|
3233
3264
|
}
|
|
3234
|
-
const content =
|
|
3265
|
+
const content = fs17.readFileSync(sourcePath, "utf-8");
|
|
3235
3266
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3236
3267
|
projectRoot,
|
|
3237
3268
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3249,7 +3280,7 @@ var EnvEditor = class {
|
|
|
3249
3280
|
filePath;
|
|
3250
3281
|
constructor(filePath) {
|
|
3251
3282
|
this.filePath = filePath;
|
|
3252
|
-
this.lines =
|
|
3283
|
+
this.lines = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3253
3284
|
}
|
|
3254
3285
|
getLines() {
|
|
3255
3286
|
return [...this.lines];
|
|
@@ -3313,18 +3344,18 @@ var EnvEditor = class {
|
|
|
3313
3344
|
});
|
|
3314
3345
|
}
|
|
3315
3346
|
save() {
|
|
3316
|
-
const dir =
|
|
3317
|
-
if (!
|
|
3318
|
-
|
|
3347
|
+
const dir = path26.dirname(this.filePath);
|
|
3348
|
+
if (!fs17.existsSync(dir)) {
|
|
3349
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
3319
3350
|
}
|
|
3320
|
-
|
|
3351
|
+
fs17.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3321
3352
|
}
|
|
3322
3353
|
};
|
|
3323
3354
|
function discoverEnvironments(projectRoot) {
|
|
3324
|
-
if (!
|
|
3355
|
+
if (!fs17.existsSync(projectRoot)) {
|
|
3325
3356
|
return ["local", "dev", "prod"];
|
|
3326
3357
|
}
|
|
3327
|
-
const files =
|
|
3358
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3328
3359
|
const envs = /* @__PURE__ */ new Set();
|
|
3329
3360
|
for (const file of files) {
|
|
3330
3361
|
if (file.startsWith(".env")) {
|
|
@@ -3349,7 +3380,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3349
3380
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3350
3381
|
const s = p19.spinner();
|
|
3351
3382
|
s.start("Scanning for local environment files...");
|
|
3352
|
-
const files =
|
|
3383
|
+
const files = fs17.readdirSync(projectRoot, { withFileTypes: true });
|
|
3353
3384
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3354
3385
|
if (envFiles.length === 0) {
|
|
3355
3386
|
s.stop("No local .env files found.");
|
|
@@ -3359,11 +3390,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3359
3390
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3360
3391
|
const generatedFiles = [];
|
|
3361
3392
|
for (const envFile of envFiles) {
|
|
3362
|
-
const sourcePath =
|
|
3393
|
+
const sourcePath = path26.join(projectRoot, envFile);
|
|
3363
3394
|
const targetFile = `${envFile}.example`;
|
|
3364
|
-
const targetPath =
|
|
3395
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3365
3396
|
let overwrite = flags.overwrite;
|
|
3366
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3397
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
|
|
3367
3398
|
overwrite = await p19.confirm({
|
|
3368
3399
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3369
3400
|
initialValue: true
|
|
@@ -3396,12 +3427,12 @@ function isInteractive(flags) {
|
|
|
3396
3427
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3397
3428
|
const keys = /* @__PURE__ */ new Set();
|
|
3398
3429
|
for (const env of envs) {
|
|
3399
|
-
const localPath =
|
|
3400
|
-
const examplePath =
|
|
3401
|
-
if (
|
|
3430
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3431
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3432
|
+
if (fs17.existsSync(localPath)) {
|
|
3402
3433
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3403
3434
|
}
|
|
3404
|
-
if (
|
|
3435
|
+
if (fs17.existsSync(examplePath)) {
|
|
3405
3436
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3406
3437
|
}
|
|
3407
3438
|
}
|
|
@@ -3473,8 +3504,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3473
3504
|
})
|
|
3474
3505
|
);
|
|
3475
3506
|
}
|
|
3476
|
-
const localPath =
|
|
3477
|
-
const examplePath =
|
|
3507
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3508
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3478
3509
|
if (flags.dryRun) {
|
|
3479
3510
|
p19.log.info(
|
|
3480
3511
|
pc13.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3521,8 +3552,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3521
3552
|
);
|
|
3522
3553
|
}
|
|
3523
3554
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3524
|
-
const localPath =
|
|
3525
|
-
return
|
|
3555
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3556
|
+
return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3526
3557
|
});
|
|
3527
3558
|
let targetEnvs = [];
|
|
3528
3559
|
if (flags.env) {
|
|
@@ -3548,7 +3579,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3548
3579
|
return { key: targetKey, environments: [] };
|
|
3549
3580
|
}
|
|
3550
3581
|
for (const env of targetEnvs) {
|
|
3551
|
-
const localPath =
|
|
3582
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3552
3583
|
const localEditor = new EnvEditor(localPath);
|
|
3553
3584
|
const currentVal = localEditor.getValue(targetKey);
|
|
3554
3585
|
let newVal = value;
|
|
@@ -3578,7 +3609,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3578
3609
|
} else {
|
|
3579
3610
|
localEditor.set(targetKey, newVal);
|
|
3580
3611
|
localEditor.save();
|
|
3581
|
-
const examplePath =
|
|
3612
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3582
3613
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3583
3614
|
if (!exampleEditor.has(targetKey)) {
|
|
3584
3615
|
exampleEditor.set(targetKey, "");
|
|
@@ -3614,9 +3645,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3614
3645
|
);
|
|
3615
3646
|
}
|
|
3616
3647
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3617
|
-
const localPath =
|
|
3618
|
-
const examplePath =
|
|
3619
|
-
return
|
|
3648
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3649
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3650
|
+
return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs17.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
|
|
3620
3651
|
});
|
|
3621
3652
|
let targetEnvs = [];
|
|
3622
3653
|
if (flags.env) {
|
|
@@ -3654,19 +3685,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3654
3685
|
}
|
|
3655
3686
|
}
|
|
3656
3687
|
for (const env of targetEnvs) {
|
|
3657
|
-
const localPath =
|
|
3658
|
-
const examplePath =
|
|
3688
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3689
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3659
3690
|
if (flags.dryRun) {
|
|
3660
3691
|
p19.log.info(
|
|
3661
3692
|
pc13.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3662
3693
|
);
|
|
3663
3694
|
} else {
|
|
3664
|
-
if (
|
|
3695
|
+
if (fs17.existsSync(localPath)) {
|
|
3665
3696
|
const localEditor = new EnvEditor(localPath);
|
|
3666
3697
|
localEditor.delete(targetKey);
|
|
3667
3698
|
localEditor.save();
|
|
3668
3699
|
}
|
|
3669
|
-
if (
|
|
3700
|
+
if (fs17.existsSync(examplePath)) {
|
|
3670
3701
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3671
3702
|
exampleEditor.delete(targetKey);
|
|
3672
3703
|
exampleEditor.save();
|
|
@@ -3691,10 +3722,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
3691
3722
|
for (const env of envs) {
|
|
3692
3723
|
const localFile = resolveEnvFilename(env, false);
|
|
3693
3724
|
const exampleFile = resolveEnvFilename(env, true);
|
|
3694
|
-
const localPath =
|
|
3695
|
-
const examplePath =
|
|
3696
|
-
const localExists =
|
|
3697
|
-
const exampleExists =
|
|
3725
|
+
const localPath = path26.join(projectRoot, localFile);
|
|
3726
|
+
const examplePath = path26.join(projectRoot, exampleFile);
|
|
3727
|
+
const localExists = fs17.existsSync(localPath);
|
|
3728
|
+
const exampleExists = fs17.existsSync(examplePath);
|
|
3698
3729
|
const localEditor = new EnvEditor(localPath);
|
|
3699
3730
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3700
3731
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4021,25 +4052,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
4021
4052
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
4022
4053
|
}
|
|
4023
4054
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
4024
|
-
return
|
|
4055
|
+
return path26.resolve(baseDir, trimmed);
|
|
4025
4056
|
}
|
|
4026
4057
|
return trimmed;
|
|
4027
4058
|
}
|
|
4028
4059
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
4029
|
-
let current =
|
|
4060
|
+
let current = path26.resolve(startDir);
|
|
4030
4061
|
while (true) {
|
|
4031
|
-
const candidate =
|
|
4032
|
-
if (
|
|
4062
|
+
const candidate = path26.join(current, "mason-registry");
|
|
4063
|
+
if (fs17.existsSync(candidate) && fs17.existsSync(path26.join(candidate, "index.json"))) {
|
|
4033
4064
|
return candidate;
|
|
4034
4065
|
}
|
|
4035
|
-
const parent =
|
|
4066
|
+
const parent = path26.dirname(current);
|
|
4036
4067
|
if (parent === current) break;
|
|
4037
4068
|
current = parent;
|
|
4038
4069
|
}
|
|
4039
4070
|
return null;
|
|
4040
4071
|
}
|
|
4041
4072
|
function getRegistryBase(projectRoot, override) {
|
|
4042
|
-
const rootDir = projectRoot ?
|
|
4073
|
+
const rootDir = projectRoot ? path26.resolve(projectRoot) : process.cwd();
|
|
4043
4074
|
if (override && override.trim()) {
|
|
4044
4075
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
4045
4076
|
}
|
|
@@ -4047,30 +4078,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
4047
4078
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
4048
4079
|
}
|
|
4049
4080
|
const configLocations = [
|
|
4050
|
-
|
|
4051
|
-
|
|
4081
|
+
path26.join(rootDir, "mason.config.json"),
|
|
4082
|
+
path26.join(process.cwd(), "mason.config.json")
|
|
4052
4083
|
];
|
|
4053
4084
|
for (const configPath of configLocations) {
|
|
4054
|
-
if (
|
|
4085
|
+
if (fs17.existsSync(configPath)) {
|
|
4055
4086
|
try {
|
|
4056
|
-
const config = JSON.parse(
|
|
4087
|
+
const config = JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
4057
4088
|
if (config.registry && typeof config.registry === "string") {
|
|
4058
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
4089
|
+
return normalizeRegistryUrl(config.registry.trim(), path26.dirname(configPath));
|
|
4059
4090
|
}
|
|
4060
4091
|
} catch {
|
|
4061
4092
|
}
|
|
4062
4093
|
}
|
|
4063
4094
|
}
|
|
4064
4095
|
const pkgLocations = [
|
|
4065
|
-
|
|
4066
|
-
|
|
4096
|
+
path26.join(rootDir, "package.json"),
|
|
4097
|
+
path26.join(process.cwd(), "package.json")
|
|
4067
4098
|
];
|
|
4068
4099
|
for (const pkgPath of pkgLocations) {
|
|
4069
|
-
if (
|
|
4100
|
+
if (fs17.existsSync(pkgPath)) {
|
|
4070
4101
|
try {
|
|
4071
|
-
const pkg = JSON.parse(
|
|
4102
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
|
|
4072
4103
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
4073
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
4104
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26.dirname(pkgPath));
|
|
4074
4105
|
}
|
|
4075
4106
|
} catch {
|
|
4076
4107
|
}
|
|
@@ -4118,16 +4149,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
4118
4149
|
if (baseDir.startsWith("file://")) {
|
|
4119
4150
|
baseDir = fileURLToPath(baseDir);
|
|
4120
4151
|
}
|
|
4121
|
-
let filePath =
|
|
4122
|
-
if (!
|
|
4123
|
-
const inSrcPath =
|
|
4124
|
-
if (
|
|
4152
|
+
let filePath = path26.resolve(baseDir, cleanRelPath);
|
|
4153
|
+
if (!fs17.existsSync(filePath)) {
|
|
4154
|
+
const inSrcPath = path26.resolve(baseDir, "src", cleanRelPath);
|
|
4155
|
+
if (fs17.existsSync(inSrcPath)) {
|
|
4125
4156
|
filePath = inSrcPath;
|
|
4126
4157
|
} else {
|
|
4127
4158
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
4128
4159
|
}
|
|
4129
4160
|
}
|
|
4130
|
-
return
|
|
4161
|
+
return fs17.promises.readFile(filePath, "utf-8");
|
|
4131
4162
|
}
|
|
4132
4163
|
async function fetchRegistryIndex(registryBase) {
|
|
4133
4164
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -4189,19 +4220,19 @@ function findClosestMatch(target, candidates) {
|
|
|
4189
4220
|
return null;
|
|
4190
4221
|
}
|
|
4191
4222
|
function detectPackageManager(projectRoot) {
|
|
4192
|
-
if (
|
|
4193
|
-
if (
|
|
4194
|
-
if (
|
|
4223
|
+
if (fs17.existsSync(path26.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
4224
|
+
if (fs17.existsSync(path26.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
4225
|
+
if (fs17.existsSync(path26.join(projectRoot, "bun.lockb")) || fs17.existsSync(path26.join(projectRoot, "bun.lock"))) return "bun";
|
|
4195
4226
|
return "npm";
|
|
4196
4227
|
}
|
|
4197
4228
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
4198
4229
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
4199
|
-
const pkgJsonPath =
|
|
4200
|
-
if (!
|
|
4230
|
+
const pkgJsonPath = path26.join(projectRoot, "package.json");
|
|
4231
|
+
if (!fs17.existsSync(pkgJsonPath)) {
|
|
4201
4232
|
return requiredPackages;
|
|
4202
4233
|
}
|
|
4203
4234
|
try {
|
|
4204
|
-
const pkg = JSON.parse(
|
|
4235
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgJsonPath, "utf-8"));
|
|
4205
4236
|
const installed = /* @__PURE__ */ new Set([
|
|
4206
4237
|
...Object.keys(pkg.dependencies || {}),
|
|
4207
4238
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -4240,12 +4271,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
4240
4271
|
});
|
|
4241
4272
|
}
|
|
4242
4273
|
function getAllFiles(dir, baseDir = dir) {
|
|
4243
|
-
if (!
|
|
4244
|
-
const entries =
|
|
4274
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4275
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4245
4276
|
const files = [];
|
|
4246
4277
|
for (const entry of entries) {
|
|
4247
|
-
const fullPath =
|
|
4248
|
-
const relPath =
|
|
4278
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4279
|
+
const relPath = path26.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
4249
4280
|
if (entry.isDirectory()) {
|
|
4250
4281
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4251
4282
|
continue;
|
|
@@ -4261,13 +4292,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
4261
4292
|
return files.sort();
|
|
4262
4293
|
}
|
|
4263
4294
|
function computeDirectoryHash(dirPath, files) {
|
|
4264
|
-
if (!
|
|
4295
|
+
if (!fs17.existsSync(dirPath)) return "";
|
|
4265
4296
|
const hash = createHash("sha256");
|
|
4266
4297
|
const fileList = (getAllFiles(dirPath)).sort();
|
|
4267
4298
|
for (const relativePath of fileList) {
|
|
4268
|
-
const absolutePath =
|
|
4269
|
-
if (!
|
|
4270
|
-
const content =
|
|
4299
|
+
const absolutePath = path26.join(dirPath, relativePath);
|
|
4300
|
+
if (!fs17.existsSync(absolutePath)) continue;
|
|
4301
|
+
const content = fs17.readFileSync(absolutePath, "utf8");
|
|
4271
4302
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
4272
4303
|
hash.update(relativePath);
|
|
4273
4304
|
hash.update(normalizedContent);
|
|
@@ -4305,17 +4336,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4305
4336
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4306
4337
|
}
|
|
4307
4338
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4308
|
-
return
|
|
4339
|
+
return path26.join(projectRoot, "src", folder);
|
|
4309
4340
|
}
|
|
4310
4341
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4311
4342
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4312
4343
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4313
|
-
const inConfigured =
|
|
4314
|
-
if (
|
|
4315
|
-
const inSrc =
|
|
4316
|
-
if (
|
|
4317
|
-
const inRoot =
|
|
4318
|
-
if (
|
|
4344
|
+
const inConfigured = path26.join(configuredDir, modName);
|
|
4345
|
+
if (fs17.existsSync(inConfigured)) return inConfigured;
|
|
4346
|
+
const inSrc = path26.join(projectRoot, "src", folder, modName);
|
|
4347
|
+
if (fs17.existsSync(inSrc)) return inSrc;
|
|
4348
|
+
const inRoot = path26.join(projectRoot, folder, modName);
|
|
4349
|
+
if (fs17.existsSync(inRoot)) return inRoot;
|
|
4319
4350
|
return null;
|
|
4320
4351
|
}
|
|
4321
4352
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4326,14 +4357,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4326
4357
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4327
4358
|
const dirs = [
|
|
4328
4359
|
configuredDir,
|
|
4329
|
-
|
|
4330
|
-
|
|
4360
|
+
path26.join(projectRoot, "src", folder),
|
|
4361
|
+
path26.join(projectRoot, folder)
|
|
4331
4362
|
];
|
|
4332
4363
|
const set = /* @__PURE__ */ new Set();
|
|
4333
4364
|
for (const dir of dirs) {
|
|
4334
|
-
if (
|
|
4365
|
+
if (fs17.existsSync(dir)) {
|
|
4335
4366
|
try {
|
|
4336
|
-
const entries =
|
|
4367
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4337
4368
|
for (const e of entries) {
|
|
4338
4369
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4339
4370
|
set.add(e.name);
|
|
@@ -4535,7 +4566,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4535
4566
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4536
4567
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4537
4568
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4538
|
-
const displayDest =
|
|
4569
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, programmaticAlias));
|
|
4539
4570
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4540
4571
|
resolutions.set(key, {
|
|
4541
4572
|
type: modType,
|
|
@@ -4723,7 +4754,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4723
4754
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4724
4755
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4725
4756
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4726
|
-
const displayDest =
|
|
4757
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, newName));
|
|
4727
4758
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4728
4759
|
resolutions.set(key, {
|
|
4729
4760
|
type: modType,
|
|
@@ -4775,7 +4806,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4775
4806
|
continue;
|
|
4776
4807
|
}
|
|
4777
4808
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4778
|
-
const targetDir =
|
|
4809
|
+
const targetDir = path26.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4779
4810
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4780
4811
|
if (key !== rootKey) {
|
|
4781
4812
|
p19.log.step(
|
|
@@ -4785,30 +4816,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4785
4816
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4786
4817
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4787
4818
|
if (existingDir && existingDir !== targetDir) {
|
|
4788
|
-
|
|
4819
|
+
fs17.rmSync(existingDir, { recursive: true, force: true });
|
|
4789
4820
|
}
|
|
4790
|
-
if (
|
|
4791
|
-
|
|
4821
|
+
if (fs17.existsSync(targetDir)) {
|
|
4822
|
+
fs17.rmSync(targetDir, { recursive: true, force: true });
|
|
4792
4823
|
}
|
|
4793
4824
|
}
|
|
4794
4825
|
for (const file of res.manifest.files || []) {
|
|
4795
4826
|
if (file === "registry.json") continue;
|
|
4796
4827
|
if (!includeTests && isTestFile(file)) continue;
|
|
4797
4828
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4798
|
-
const localFilePath =
|
|
4799
|
-
if (
|
|
4829
|
+
const localFilePath = path26.join(targetDir, file);
|
|
4830
|
+
if (fs17.existsSync(localFilePath) && !isOverwrite) {
|
|
4800
4831
|
p19.log.warn(
|
|
4801
|
-
` \u21B3 Skipping existing file: ${pc13.dim(
|
|
4832
|
+
` \u21B3 Skipping existing file: ${pc13.dim(path26.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4802
4833
|
);
|
|
4803
4834
|
continue;
|
|
4804
4835
|
}
|
|
4805
4836
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4806
4837
|
content = rewriteImports(content, globalAliasMap);
|
|
4807
4838
|
if (!flags.dryRun) {
|
|
4808
|
-
|
|
4809
|
-
|
|
4839
|
+
fs17.mkdirSync(path26.dirname(localFilePath), { recursive: true });
|
|
4840
|
+
fs17.writeFileSync(localFilePath, content, "utf-8");
|
|
4810
4841
|
}
|
|
4811
|
-
const displayPath =
|
|
4842
|
+
const displayPath = path26.relative(projectRoot, localFilePath);
|
|
4812
4843
|
totalWrittenFiles.push(displayPath);
|
|
4813
4844
|
p19.log.message(` ${pc13.green("+")} ${displayPath}`);
|
|
4814
4845
|
}
|
|
@@ -4834,7 +4865,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4834
4865
|
} else {
|
|
4835
4866
|
const rootRes = resolutions.get(rootKey);
|
|
4836
4867
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4837
|
-
const displayTarget =
|
|
4868
|
+
const displayTarget = path26.relative(projectRoot, path26.join(baseDir, rootRes.targetName));
|
|
4838
4869
|
p19.log.success(
|
|
4839
4870
|
pc13.green(
|
|
4840
4871
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4853,19 +4884,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4853
4884
|
}
|
|
4854
4885
|
};
|
|
4855
4886
|
function isTestFile2(filePath, projectRoot) {
|
|
4856
|
-
const norm = (projectRoot ?
|
|
4857
|
-
const base =
|
|
4887
|
+
const norm = (projectRoot ? path26.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4888
|
+
const base = path26.basename(norm);
|
|
4858
4889
|
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);
|
|
4859
4890
|
}
|
|
4860
4891
|
function scanDirFiles(dir, projectRoot) {
|
|
4861
|
-
if (!
|
|
4862
|
-
const entries =
|
|
4892
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4893
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4863
4894
|
const files = [];
|
|
4864
4895
|
for (const entry of entries) {
|
|
4865
4896
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4866
4897
|
continue;
|
|
4867
4898
|
}
|
|
4868
|
-
const fullPath =
|
|
4899
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4869
4900
|
if (entry.isDirectory()) {
|
|
4870
4901
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4871
4902
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4874,101 +4905,194 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4874
4905
|
}
|
|
4875
4906
|
return files;
|
|
4876
4907
|
}
|
|
4877
|
-
function classifyFile(
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
}
|
|
4897
|
-
const
|
|
4898
|
-
if (
|
|
4899
|
-
const
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
layer = "
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4908
|
+
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4909
|
+
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4910
|
+
const paths = getResolvedLintPaths(featuresBaseAbs, loadLintConfig(featuresBaseAbs));
|
|
4911
|
+
featuresBaseAbs = paths.featuresBase;
|
|
4912
|
+
sharedBaseAbs = paths.sharedBase;
|
|
4913
|
+
errorsBaseAbs = paths.errorsBase;
|
|
4914
|
+
}
|
|
4915
|
+
const normFullPath = path26.resolve(fullPathAbs);
|
|
4916
|
+
const normErrorsBase = path26.resolve(errorsBaseAbs);
|
|
4917
|
+
const normSharedBase = path26.resolve(sharedBaseAbs);
|
|
4918
|
+
const normFeaturesBase = path26.resolve(featuresBaseAbs);
|
|
4919
|
+
const relErrors = path26.relative(normErrorsBase, normFullPath).replace(/\\/g, "/");
|
|
4920
|
+
if (!relErrors.startsWith("..") && !path26.isAbsolute(relErrors)) {
|
|
4921
|
+
return {
|
|
4922
|
+
moduleType: "error",
|
|
4923
|
+
moduleName: "global-errors",
|
|
4924
|
+
feature: "global-errors",
|
|
4925
|
+
layer: "domain"
|
|
4926
|
+
};
|
|
4927
|
+
}
|
|
4928
|
+
const relShared = path26.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4929
|
+
if (!relShared.startsWith("..") && !path26.isAbsolute(relShared)) {
|
|
4930
|
+
const parts = relShared.split("/").filter(Boolean);
|
|
4931
|
+
if (parts.length >= 1) {
|
|
4932
|
+
const moduleName = parts[0];
|
|
4933
|
+
const rest = parts.slice(1).join("/");
|
|
4934
|
+
let layer = "root";
|
|
4935
|
+
if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
|
|
4936
|
+
layer = "domain";
|
|
4937
|
+
} else if (rest.startsWith("ports/") || rest === "ports") {
|
|
4938
|
+
layer = "ports";
|
|
4939
|
+
} else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
|
|
4940
|
+
layer = "infrastructure";
|
|
4941
|
+
}
|
|
4942
|
+
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
const relFeatures = path26.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4946
|
+
if (!relFeatures.startsWith("..") && !path26.isAbsolute(relFeatures)) {
|
|
4947
|
+
const parts = relFeatures.split("/").filter(Boolean);
|
|
4948
|
+
if (parts.length >= 2) {
|
|
4949
|
+
const moduleName = parts[0];
|
|
4950
|
+
const subfolder = parts[1];
|
|
4951
|
+
let layer;
|
|
4952
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4953
|
+
layer = "domain";
|
|
4954
|
+
} else if (subfolder === "useCases" || subfolder === "usecases") {
|
|
4955
|
+
layer = "useCases";
|
|
4956
|
+
} else if (subfolder === "services") {
|
|
4957
|
+
layer = "services";
|
|
4958
|
+
} else if (subfolder === "infrastructure") {
|
|
4959
|
+
layer = "infrastructure";
|
|
4960
|
+
} else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
|
|
4961
|
+
layer = "presentation";
|
|
4962
|
+
}
|
|
4963
|
+
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
4964
|
+
}
|
|
4910
4965
|
}
|
|
4911
4966
|
return {};
|
|
4912
4967
|
}
|
|
4913
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
4968
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4914
4969
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4915
4970
|
return { isExternal: true, isDiFramework: true };
|
|
4916
4971
|
}
|
|
4917
|
-
if (specifier.startsWith("node:") || [
|
|
4972
|
+
if (specifier.startsWith("node:") || [
|
|
4973
|
+
"crypto",
|
|
4974
|
+
"fs",
|
|
4975
|
+
"path",
|
|
4976
|
+
"http",
|
|
4977
|
+
"https",
|
|
4978
|
+
"url",
|
|
4979
|
+
"os",
|
|
4980
|
+
"child_process",
|
|
4981
|
+
"stream",
|
|
4982
|
+
"express",
|
|
4983
|
+
"cors",
|
|
4984
|
+
"dotenv"
|
|
4985
|
+
].includes(specifier)) {
|
|
4918
4986
|
return { isExternal: true, isDiFramework: false };
|
|
4919
4987
|
}
|
|
4920
|
-
|
|
4988
|
+
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
4989
|
+
const paths = getResolvedLintPaths(projectRoot, loadLintConfig(projectRoot));
|
|
4990
|
+
featuresBaseAbs = featuresBaseAbs || paths.featuresBase;
|
|
4991
|
+
sharedBaseAbs = sharedBaseAbs || paths.sharedBase;
|
|
4992
|
+
errorsBaseAbs = errorsBaseAbs || paths.errorsBase;
|
|
4993
|
+
}
|
|
4994
|
+
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
4995
|
+
let targetAbs = null;
|
|
4921
4996
|
if (specifier.startsWith(".")) {
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4997
|
+
targetAbs = path26.resolve(path26.dirname(sourceFileAbs), specifier);
|
|
4998
|
+
} else {
|
|
4999
|
+
const featRel = path26.relative(projectRoot, featuresBaseAbs).replace(/\\/g, "/");
|
|
5000
|
+
const sharedRel = path26.relative(projectRoot, sharedBaseAbs).replace(/\\/g, "/");
|
|
5001
|
+
const errorsRel = path26.relative(projectRoot, errorsBaseAbs).replace(/\\/g, "/");
|
|
5002
|
+
const featPrefixes = ["features"];
|
|
5003
|
+
if (featRel && featRel !== "." && !featRel.startsWith("..")) {
|
|
5004
|
+
featPrefixes.push(featRel);
|
|
5005
|
+
const b = path26.basename(featRel);
|
|
5006
|
+
if (!featPrefixes.includes(b)) featPrefixes.push(b);
|
|
5007
|
+
}
|
|
5008
|
+
const sharedPrefixes = ["shared"];
|
|
5009
|
+
if (sharedRel && sharedRel !== "." && !sharedRel.startsWith("..")) {
|
|
5010
|
+
sharedPrefixes.push(sharedRel);
|
|
5011
|
+
const b = path26.basename(sharedRel);
|
|
5012
|
+
if (!sharedPrefixes.includes(b)) sharedPrefixes.push(b);
|
|
5013
|
+
}
|
|
5014
|
+
const errorsPrefixes = ["errors"];
|
|
5015
|
+
if (errorsRel && errorsRel !== "." && !errorsRel.startsWith("..")) {
|
|
5016
|
+
errorsPrefixes.push(errorsRel);
|
|
5017
|
+
const b = path26.basename(errorsRel);
|
|
5018
|
+
if (!errorsPrefixes.includes(b)) errorsPrefixes.push(b);
|
|
5019
|
+
}
|
|
5020
|
+
for (const prefix of featPrefixes) {
|
|
5021
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5022
|
+
const m = specifier.match(aliasRegex);
|
|
5023
|
+
if (m && m[2]) {
|
|
5024
|
+
targetAbs = path26.resolve(featuresBaseAbs, m[2]);
|
|
5025
|
+
break;
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
if (!targetAbs) {
|
|
5029
|
+
for (const prefix of sharedPrefixes) {
|
|
5030
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5031
|
+
const m = specifier.match(aliasRegex);
|
|
5032
|
+
if (m && m[2]) {
|
|
5033
|
+
targetAbs = path26.resolve(sharedBaseAbs, m[2]);
|
|
5034
|
+
break;
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
if (!targetAbs) {
|
|
5039
|
+
for (const prefix of errorsPrefixes) {
|
|
5040
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}(?:\\/(.*))?$`);
|
|
5041
|
+
const m = specifier.match(aliasRegex);
|
|
5042
|
+
if (m) {
|
|
5043
|
+
targetAbs = path26.resolve(errorsBaseAbs, m[2] || "");
|
|
5044
|
+
break;
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
if (!targetAbs && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
|
|
5049
|
+
const sub = specifier.slice(2);
|
|
5050
|
+
const srcCandidate = path26.resolve(projectRoot, "src", sub);
|
|
5051
|
+
const rootCandidate = path26.resolve(projectRoot, sub);
|
|
5052
|
+
if (fs17.existsSync(srcCandidate)) {
|
|
5053
|
+
targetAbs = srcCandidate;
|
|
5054
|
+
} else if (fs17.existsSync(rootCandidate)) {
|
|
5055
|
+
targetAbs = rootCandidate;
|
|
5056
|
+
} else {
|
|
5057
|
+
targetAbs = srcCandidate;
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
if (!targetAbs) {
|
|
5061
|
+
const featCandidate = path26.resolve(featuresBaseAbs, specifier);
|
|
5062
|
+
if (fs17.existsSync(featCandidate) || fs17.existsSync(featCandidate + ".ts") || fs17.existsSync(featCandidate + ".js")) {
|
|
5063
|
+
targetAbs = featCandidate;
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (!targetAbs) {
|
|
5068
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5069
|
+
}
|
|
5070
|
+
const normTarget = path26.resolve(targetAbs);
|
|
5071
|
+
const srcConfig = path26.resolve(projectRoot, "src/configs");
|
|
5072
|
+
const rootConfig = path26.resolve(projectRoot, "configs");
|
|
5073
|
+
const isTargetConfig = isGlobalConfig || normTarget === srcConfig || normTarget.startsWith(srcConfig + path26.sep) || normTarget === rootConfig || normTarget.startsWith(rootConfig + path26.sep);
|
|
5074
|
+
const classification = classifyFile(targetAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs);
|
|
5075
|
+
if (classification.moduleType === "error") {
|
|
5076
|
+
return {
|
|
5077
|
+
isExternal: false,
|
|
5078
|
+
isDiFramework: false,
|
|
5079
|
+
isGlobalError: true,
|
|
5080
|
+
isTargetError: true
|
|
5081
|
+
};
|
|
4943
5082
|
}
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
const
|
|
4948
|
-
const
|
|
4949
|
-
const rest = featMatch[3] || "";
|
|
4950
|
-
let targetLayer;
|
|
4951
|
-
if (targetSubfolder === "domain" || targetSubfolder === "errors") {
|
|
4952
|
-
targetLayer = "domain";
|
|
4953
|
-
} else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
|
|
4954
|
-
targetLayer = "useCases";
|
|
4955
|
-
} else if (targetSubfolder === "services") {
|
|
4956
|
-
targetLayer = "services";
|
|
4957
|
-
} else if (targetSubfolder === "infrastructure") {
|
|
4958
|
-
targetLayer = "infrastructure";
|
|
4959
|
-
} else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
|
|
4960
|
-
targetLayer = "presentation";
|
|
4961
|
-
}
|
|
4962
|
-
const basePart = rest ? path2.basename(rest) : targetSubfolder;
|
|
4963
|
-
const targetFileName = basePart.replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5083
|
+
if (classification.moduleType === "feature") {
|
|
5084
|
+
const rel = path26.relative(featuresBaseAbs, targetAbs).replace(/\\/g, "/");
|
|
5085
|
+
const parts = rel.split("/").filter(Boolean);
|
|
5086
|
+
const targetSubfolder = parts[1];
|
|
5087
|
+
const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
4964
5088
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
4965
5089
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
4966
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5090
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
4967
5091
|
return {
|
|
4968
5092
|
isExternal: false,
|
|
4969
5093
|
isDiFramework: false,
|
|
4970
|
-
targetFeature,
|
|
4971
|
-
targetLayer,
|
|
5094
|
+
targetFeature: classification.moduleName,
|
|
5095
|
+
targetLayer: classification.layer,
|
|
4972
5096
|
targetSubfolder,
|
|
4973
5097
|
targetFileName,
|
|
4974
5098
|
isTargetPort,
|
|
@@ -4976,25 +5100,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
4976
5100
|
isTargetError
|
|
4977
5101
|
};
|
|
4978
5102
|
}
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
const
|
|
4982
|
-
const sharedRest = sharedMatch[2] || "";
|
|
4983
|
-
const isSharedRoot = !sharedRest.includes("/");
|
|
4984
|
-
const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
|
|
4985
|
-
const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
|
|
4986
|
-
const isSharedDomain = sharedRest.startsWith("domain/") || sharedRest === "domain";
|
|
5103
|
+
if (classification.moduleType === "shared") {
|
|
5104
|
+
const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5105
|
+
const isTargetError = classification.layer === "domain" && (targetFileName.endsWith("Error") || targetFileName.endsWith("Errors") || targetAbs.includes("/errors/"));
|
|
4987
5106
|
return {
|
|
4988
5107
|
isExternal: false,
|
|
4989
5108
|
isDiFramework: false,
|
|
4990
|
-
targetSharedModule,
|
|
4991
|
-
isSharedRoot,
|
|
4992
|
-
isSharedPorts,
|
|
4993
|
-
isSharedInfra,
|
|
4994
|
-
isSharedDomain
|
|
5109
|
+
targetSharedModule: classification.moduleName,
|
|
5110
|
+
isSharedRoot: classification.layer === "root",
|
|
5111
|
+
isSharedPorts: classification.layer === "ports",
|
|
5112
|
+
isSharedInfra: classification.layer === "infrastructure",
|
|
5113
|
+
isSharedDomain: classification.layer === "domain",
|
|
5114
|
+
isTargetError
|
|
4995
5115
|
};
|
|
4996
5116
|
}
|
|
4997
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5117
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
4998
5118
|
}
|
|
4999
5119
|
function extractImportStatements(content) {
|
|
5000
5120
|
const statements = [];
|
|
@@ -5116,22 +5236,50 @@ var FeatureDependencyGraph = class {
|
|
|
5116
5236
|
return dfs(start);
|
|
5117
5237
|
}
|
|
5118
5238
|
};
|
|
5119
|
-
function discoverAllFeatures(
|
|
5120
|
-
const
|
|
5121
|
-
if (!
|
|
5122
|
-
const
|
|
5123
|
-
|
|
5239
|
+
function discoverAllFeatures(featuresBaseAbs, excludeDirs = []) {
|
|
5240
|
+
const base = featuresBaseAbs;
|
|
5241
|
+
if (!fs17.existsSync(base)) return [];
|
|
5242
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
|
|
5243
|
+
const entries = fs17.readdirSync(base, { withFileTypes: true });
|
|
5244
|
+
return entries.filter((e) => {
|
|
5245
|
+
if (!e.isDirectory()) return false;
|
|
5246
|
+
if (e.name.startsWith(".")) return false;
|
|
5247
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5248
|
+
return false;
|
|
5249
|
+
}
|
|
5250
|
+
const resolved = path26.resolve(base, e.name);
|
|
5251
|
+
for (const ex of excludeSet) {
|
|
5252
|
+
if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
|
|
5253
|
+
return false;
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
return true;
|
|
5257
|
+
}).map((e) => ({
|
|
5124
5258
|
name: e.name,
|
|
5125
|
-
dir:
|
|
5259
|
+
dir: path26.join(base, e.name)
|
|
5126
5260
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5127
5261
|
}
|
|
5128
|
-
function discoverAllShared(
|
|
5129
|
-
const
|
|
5130
|
-
if (!
|
|
5131
|
-
const
|
|
5132
|
-
|
|
5262
|
+
function discoverAllShared(sharedBaseAbs, excludeDirs = []) {
|
|
5263
|
+
const base = sharedBaseAbs;
|
|
5264
|
+
if (!fs17.existsSync(base)) return [];
|
|
5265
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
|
|
5266
|
+
const entries = fs17.readdirSync(base, { withFileTypes: true });
|
|
5267
|
+
return entries.filter((e) => {
|
|
5268
|
+
if (!e.isDirectory()) return false;
|
|
5269
|
+
if (e.name.startsWith(".")) return false;
|
|
5270
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5271
|
+
return false;
|
|
5272
|
+
}
|
|
5273
|
+
const resolved = path26.resolve(base, e.name);
|
|
5274
|
+
for (const ex of excludeSet) {
|
|
5275
|
+
if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
|
|
5276
|
+
return false;
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
5279
|
+
return true;
|
|
5280
|
+
}).map((e) => ({
|
|
5133
5281
|
name: e.name,
|
|
5134
|
-
dir:
|
|
5282
|
+
dir: path26.join(base, e.name)
|
|
5135
5283
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5136
5284
|
}
|
|
5137
5285
|
function extractClassMethods(content, className) {
|
|
@@ -5287,7 +5435,7 @@ function extractClassMethods(content, className) {
|
|
|
5287
5435
|
return methods;
|
|
5288
5436
|
}
|
|
5289
5437
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5290
|
-
const fileName =
|
|
5438
|
+
const fileName = path26.basename(filePath);
|
|
5291
5439
|
const lines = content.split(/\r?\n/);
|
|
5292
5440
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5293
5441
|
if (!classMatch) {
|
|
@@ -5383,7 +5531,7 @@ private ${m.name}(...) { ... }`
|
|
|
5383
5531
|
}
|
|
5384
5532
|
}
|
|
5385
5533
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5386
|
-
const fileName =
|
|
5534
|
+
const fileName = path26.basename(filePath);
|
|
5387
5535
|
const lines = content.split(/\r?\n/);
|
|
5388
5536
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5389
5537
|
if (!classMatch) {
|
|
@@ -5482,11 +5630,40 @@ function lintCodebase(options = {}) {
|
|
|
5482
5630
|
const startTime = Date.now();
|
|
5483
5631
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5484
5632
|
const failFast = !options.complete;
|
|
5485
|
-
const
|
|
5486
|
-
const
|
|
5633
|
+
const lintConfig = loadLintConfig(projectRoot);
|
|
5634
|
+
const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
|
|
5635
|
+
const missingFoundations = [];
|
|
5636
|
+
if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5637
|
+
if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5638
|
+
if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5639
|
+
if (missingFoundations.length > 0) {
|
|
5640
|
+
throw new DependencyRuleError(
|
|
5641
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5642
|
+
[],
|
|
5643
|
+
true
|
|
5644
|
+
);
|
|
5645
|
+
}
|
|
5646
|
+
const baseErrorRelative = lintConfig.baseErrorPath || path26.relative(projectRoot, path26.join(errorsBase, "AppError.ts")).replace(/\\/g, "/");
|
|
5647
|
+
const baseErrorAbs = path26.resolve(projectRoot, baseErrorRelative);
|
|
5648
|
+
if (!fs17.existsSync(baseErrorAbs)) {
|
|
5649
|
+
throw new DependencyRuleError(
|
|
5650
|
+
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5651
|
+
[],
|
|
5652
|
+
true
|
|
5653
|
+
);
|
|
5654
|
+
}
|
|
5655
|
+
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5656
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5657
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5658
|
+
);
|
|
5659
|
+
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5660
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5661
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5662
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5487
5663
|
const allModules = [
|
|
5488
5664
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5489
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5665
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" })),
|
|
5666
|
+
errorModule
|
|
5490
5667
|
];
|
|
5491
5668
|
const graph = new FeatureDependencyGraph();
|
|
5492
5669
|
const allViolations = [];
|
|
@@ -5516,7 +5693,7 @@ Code: ${v.code}`;
|
|
|
5516
5693
|
scannedFilesCount: 0,
|
|
5517
5694
|
passed: true,
|
|
5518
5695
|
violations: [],
|
|
5519
|
-
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : ["domain", "ports", "infrastructure", "root"]
|
|
5696
|
+
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : mod.type === "shared" ? ["domain", "ports", "infrastructure", "root"] : ["domain"]
|
|
5520
5697
|
});
|
|
5521
5698
|
}
|
|
5522
5699
|
for (const mod of allModules) {
|
|
@@ -5524,13 +5701,128 @@ Code: ${v.code}`;
|
|
|
5524
5701
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5525
5702
|
modResult.scannedFilesCount = allFiles.length;
|
|
5526
5703
|
for (const file of allFiles) {
|
|
5527
|
-
const relPath =
|
|
5528
|
-
const { moduleType, moduleName, layer } = classifyFile(file,
|
|
5704
|
+
const relPath = path26.relative(projectRoot, file).replace(/\\/g, "/");
|
|
5705
|
+
const { moduleType, moduleName, layer } = classifyFile(file, featuresBase, sharedBase, errorsBase);
|
|
5529
5706
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5530
|
-
const content =
|
|
5707
|
+
const content = fs17.readFileSync(file, "utf-8");
|
|
5531
5708
|
const statements = extractImportStatements(content);
|
|
5532
|
-
const baseName =
|
|
5709
|
+
const baseName = path26.basename(file);
|
|
5533
5710
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5711
|
+
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5712
|
+
let throwMatch;
|
|
5713
|
+
while ((throwMatch = throwErrorRegex.exec(content)) !== null) {
|
|
5714
|
+
const charIndex = throwMatch.index;
|
|
5715
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5716
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || throwMatch[0];
|
|
5717
|
+
if (lineCode.startsWith("//")) continue;
|
|
5718
|
+
addViolation({
|
|
5719
|
+
feature: moduleName,
|
|
5720
|
+
layer,
|
|
5721
|
+
file,
|
|
5722
|
+
relativePath: relPath,
|
|
5723
|
+
line: lineNum,
|
|
5724
|
+
category: "Plain Error Throw Violation",
|
|
5725
|
+
code: lineCode,
|
|
5726
|
+
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5727
|
+
severity: "error",
|
|
5728
|
+
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a domain-specific error class that inherits from it.`,
|
|
5729
|
+
fixExample: {
|
|
5730
|
+
bad: lineCode,
|
|
5731
|
+
good: `throw new ${baseErrorClassName}("Something went wrong");`
|
|
5732
|
+
}
|
|
5733
|
+
});
|
|
5734
|
+
}
|
|
5735
|
+
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");
|
|
5736
|
+
const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5737
|
+
if (isErrorFile && !isBaseErrorFile) {
|
|
5738
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5739
|
+
let classMatch;
|
|
5740
|
+
while ((classMatch = classRegex.exec(content)) !== null) {
|
|
5741
|
+
const className = classMatch[1];
|
|
5742
|
+
const extendsName = classMatch[2];
|
|
5743
|
+
const charIndex = classMatch.index;
|
|
5744
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5745
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || classMatch[0];
|
|
5746
|
+
if (lineCode.startsWith("//")) continue;
|
|
5747
|
+
if (!extendsName) {
|
|
5748
|
+
addViolation({
|
|
5749
|
+
feature: moduleName,
|
|
5750
|
+
layer,
|
|
5751
|
+
file,
|
|
5752
|
+
relativePath: relPath,
|
|
5753
|
+
line: lineNum,
|
|
5754
|
+
category: "Error Inheritance Violation",
|
|
5755
|
+
code: lineCode,
|
|
5756
|
+
ruleId: "E4_MISSING_INHERITANCE",
|
|
5757
|
+
severity: "error",
|
|
5758
|
+
advice: `Error class '${className}' must extend '${baseErrorClassName}' (or another class that derives from it).`,
|
|
5759
|
+
fixExample: {
|
|
5760
|
+
bad: lineCode,
|
|
5761
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5762
|
+
}
|
|
5763
|
+
});
|
|
5764
|
+
} else if (extendsName === "Error") {
|
|
5765
|
+
addViolation({
|
|
5766
|
+
feature: moduleName,
|
|
5767
|
+
layer,
|
|
5768
|
+
file,
|
|
5769
|
+
relativePath: relPath,
|
|
5770
|
+
line: lineNum,
|
|
5771
|
+
category: "Error Inheritance Violation",
|
|
5772
|
+
code: lineCode,
|
|
5773
|
+
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5774
|
+
severity: "error",
|
|
5775
|
+
advice: `Error class '${className}' extends the native 'Error'. It must extend your base error class ('${baseErrorClassName}').`,
|
|
5776
|
+
fixExample: {
|
|
5777
|
+
bad: lineCode,
|
|
5778
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5779
|
+
}
|
|
5780
|
+
});
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
if (moduleType === "error") {
|
|
5785
|
+
for (const stmt of statements) {
|
|
5786
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5787
|
+
if (target.targetFeature) {
|
|
5788
|
+
addViolation({
|
|
5789
|
+
feature: "global-errors",
|
|
5790
|
+
layer: "domain",
|
|
5791
|
+
file,
|
|
5792
|
+
relativePath: relPath,
|
|
5793
|
+
line: stmt.line,
|
|
5794
|
+
category: "Global Error Purity Violation",
|
|
5795
|
+
code: stmt.rawCode,
|
|
5796
|
+
ruleId: "E1_FEATURE_DEPENDENCY",
|
|
5797
|
+
severity: "error",
|
|
5798
|
+
advice: `Global errors must not depend on features ('${target.targetFeature}'). Global errors represent pure domain or system states.`,
|
|
5799
|
+
fixExample: {
|
|
5800
|
+
bad: stmt.rawCode,
|
|
5801
|
+
good: "// Global errors must not reference specific features. Keep error types pure."
|
|
5802
|
+
}
|
|
5803
|
+
});
|
|
5804
|
+
}
|
|
5805
|
+
if (target.targetSharedModule && !target.isSharedDomain) {
|
|
5806
|
+
addViolation({
|
|
5807
|
+
feature: "global-errors",
|
|
5808
|
+
layer: "domain",
|
|
5809
|
+
file,
|
|
5810
|
+
relativePath: relPath,
|
|
5811
|
+
line: stmt.line,
|
|
5812
|
+
category: "Global Error Purity Violation",
|
|
5813
|
+
code: stmt.rawCode,
|
|
5814
|
+
ruleId: "E2_SHARED_NON_DOMAIN",
|
|
5815
|
+
severity: "error",
|
|
5816
|
+
advice: `Global errors can only depend on shared domain elements (e.g., value objects). They cannot depend on shared ports or infrastructure.`,
|
|
5817
|
+
fixExample: {
|
|
5818
|
+
bad: stmt.rawCode,
|
|
5819
|
+
good: "// Depend only on pure domain types from shared modules."
|
|
5820
|
+
}
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
continue;
|
|
5825
|
+
}
|
|
5534
5826
|
if (moduleType === "shared") {
|
|
5535
5827
|
if (layer === "domain") {
|
|
5536
5828
|
const lines = content.split(/\r?\n/);
|
|
@@ -5557,7 +5849,7 @@ Code: ${v.code}`;
|
|
|
5557
5849
|
}
|
|
5558
5850
|
}
|
|
5559
5851
|
for (const stmt of statements) {
|
|
5560
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5852
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5561
5853
|
if (target.targetFeature) {
|
|
5562
5854
|
addViolation({
|
|
5563
5855
|
feature: moduleName,
|
|
@@ -5687,7 +5979,7 @@ Code: ${v.code}`;
|
|
|
5687
5979
|
}
|
|
5688
5980
|
}
|
|
5689
5981
|
for (const stmt of statements) {
|
|
5690
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5982
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5691
5983
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5692
5984
|
const evidence = {
|
|
5693
5985
|
fromFeature: feat.name,
|