@solid-stack/mason 1.0.33 → 1.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +493 -382
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +491 -380
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +4 -4
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +496 -384
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +494 -383
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +5 -0
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +9 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bin.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,70 +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
191
|
}
|
|
192
192
|
function loadLintConfig(projectRoot) {
|
|
193
|
-
const configPath =
|
|
194
|
-
if (
|
|
193
|
+
const configPath = path26.join(projectRoot, "mason-lint.config.json");
|
|
194
|
+
if (fs17.existsSync(configPath)) {
|
|
195
195
|
try {
|
|
196
|
-
return JSON.parse(
|
|
196
|
+
return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
197
197
|
} catch {
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
-
return {
|
|
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
|
+
};
|
|
201
222
|
}
|
|
202
223
|
|
|
203
224
|
// src/utils/project.ts
|
|
204
225
|
function findProjectRoot(startDir = process.cwd()) {
|
|
205
|
-
let current =
|
|
226
|
+
let current = path26.resolve(startDir);
|
|
206
227
|
while (true) {
|
|
207
|
-
if (
|
|
228
|
+
if (fs17.existsSync(path26.join(current, "package.json"))) {
|
|
208
229
|
return current;
|
|
209
230
|
}
|
|
210
|
-
const parent =
|
|
231
|
+
const parent = path26.dirname(current);
|
|
211
232
|
if (parent === current) {
|
|
212
|
-
return
|
|
233
|
+
return path26.resolve(startDir);
|
|
213
234
|
}
|
|
214
235
|
current = parent;
|
|
215
236
|
}
|
|
@@ -222,9 +243,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
222
243
|
}
|
|
223
244
|
function listExistingFeatures(projectRoot) {
|
|
224
245
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
225
|
-
if (!
|
|
246
|
+
if (!fs17.existsSync(featuresDir)) return [];
|
|
226
247
|
try {
|
|
227
|
-
const entries =
|
|
248
|
+
const entries = fs17.readdirSync(featuresDir, { withFileTypes: true });
|
|
228
249
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
229
250
|
} catch {
|
|
230
251
|
return [];
|
|
@@ -234,14 +255,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
234
255
|
const results = [];
|
|
235
256
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
236
257
|
for (const feat of features) {
|
|
237
|
-
const domainDir =
|
|
238
|
-
if (!
|
|
258
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
259
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
239
260
|
try {
|
|
240
|
-
const files =
|
|
261
|
+
const files = fs17.readdirSync(domainDir);
|
|
241
262
|
for (const file of files) {
|
|
242
263
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
243
264
|
const name = file.replace(/\.ts$/, "");
|
|
244
|
-
const fullPath =
|
|
265
|
+
const fullPath = path26.join(domainDir, file);
|
|
245
266
|
if (/^I[A-Z]/.test(name)) {
|
|
246
267
|
results.push({
|
|
247
268
|
name,
|
|
@@ -250,7 +271,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
250
271
|
});
|
|
251
272
|
} else {
|
|
252
273
|
try {
|
|
253
|
-
const content =
|
|
274
|
+
const content = fs17.readFileSync(fullPath, "utf-8");
|
|
254
275
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
255
276
|
results.push({
|
|
256
277
|
name,
|
|
@@ -272,17 +293,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
272
293
|
const results = [];
|
|
273
294
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
274
295
|
for (const feat of features) {
|
|
275
|
-
const domainDir =
|
|
276
|
-
if (!
|
|
296
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
297
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
277
298
|
try {
|
|
278
|
-
const files =
|
|
299
|
+
const files = fs17.readdirSync(domainDir);
|
|
279
300
|
for (const file of files) {
|
|
280
301
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
281
302
|
const name = file.replace(/\.ts$/, "");
|
|
282
303
|
if (!/^I[A-Z]/.test(name)) {
|
|
283
304
|
results.push({
|
|
284
305
|
name,
|
|
285
|
-
fullPath:
|
|
306
|
+
fullPath: path26.join(domainDir, file),
|
|
286
307
|
feature: feat
|
|
287
308
|
});
|
|
288
309
|
}
|
|
@@ -297,16 +318,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
297
318
|
const results = [];
|
|
298
319
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
299
320
|
for (const feat of features) {
|
|
300
|
-
const useCasesDir =
|
|
301
|
-
if (!
|
|
321
|
+
const useCasesDir = path26.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
322
|
+
if (!fs17.existsSync(useCasesDir)) continue;
|
|
302
323
|
try {
|
|
303
|
-
const files =
|
|
324
|
+
const files = fs17.readdirSync(useCasesDir);
|
|
304
325
|
for (const file of files) {
|
|
305
326
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
306
327
|
const name = file.replace(/\.ts$/, "");
|
|
307
328
|
results.push({
|
|
308
329
|
name,
|
|
309
|
-
fullPath:
|
|
330
|
+
fullPath: path26.join(useCasesDir, file),
|
|
310
331
|
feature: feat
|
|
311
332
|
});
|
|
312
333
|
}
|
|
@@ -332,33 +353,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
332
353
|
});
|
|
333
354
|
var cachedTemplatesDir = null;
|
|
334
355
|
function getTemplatesDir() {
|
|
335
|
-
if (cachedTemplatesDir &&
|
|
356
|
+
if (cachedTemplatesDir && fs17.existsSync(cachedTemplatesDir)) {
|
|
336
357
|
return cachedTemplatesDir;
|
|
337
358
|
}
|
|
338
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
339
|
-
const cand1 =
|
|
340
|
-
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)) {
|
|
341
362
|
cachedTemplatesDir = cand1;
|
|
342
363
|
return cand1;
|
|
343
364
|
}
|
|
344
|
-
const cand2 =
|
|
345
|
-
if (
|
|
365
|
+
const cand2 = path26.resolve(currentDir, "../templates");
|
|
366
|
+
if (fs17.existsSync(cand2)) {
|
|
346
367
|
cachedTemplatesDir = cand2;
|
|
347
368
|
return cand2;
|
|
348
369
|
}
|
|
349
|
-
const cand3 =
|
|
350
|
-
if (
|
|
370
|
+
const cand3 = path26.resolve(currentDir, "templates");
|
|
371
|
+
if (fs17.existsSync(cand3)) {
|
|
351
372
|
cachedTemplatesDir = cand3;
|
|
352
373
|
return cand3;
|
|
353
374
|
}
|
|
354
375
|
let curr = currentDir;
|
|
355
376
|
while (true) {
|
|
356
|
-
const candTemplates =
|
|
357
|
-
if (
|
|
377
|
+
const candTemplates = path26.join(curr, "templates");
|
|
378
|
+
if (fs17.existsSync(candTemplates)) {
|
|
358
379
|
cachedTemplatesDir = candTemplates;
|
|
359
380
|
return candTemplates;
|
|
360
381
|
}
|
|
361
|
-
const parent =
|
|
382
|
+
const parent = path26.dirname(curr);
|
|
362
383
|
if (parent === curr) break;
|
|
363
384
|
curr = parent;
|
|
364
385
|
}
|
|
@@ -366,8 +387,8 @@ function getTemplatesDir() {
|
|
|
366
387
|
}
|
|
367
388
|
var templateCache = /* @__PURE__ */ new Map();
|
|
368
389
|
function loadTemplate(relativePath) {
|
|
369
|
-
const fullPath =
|
|
370
|
-
return
|
|
390
|
+
const fullPath = path26.isAbsolute(relativePath) ? relativePath : path26.join(getTemplatesDir(), relativePath);
|
|
391
|
+
return fs17.readFileSync(fullPath, "utf-8");
|
|
371
392
|
}
|
|
372
393
|
function renderTemplateFile(relativePath, context) {
|
|
373
394
|
let compiled = templateCache.get(relativePath);
|
|
@@ -454,8 +475,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
454
475
|
function generateDomain(options) {
|
|
455
476
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
456
477
|
const entityName = toPascalCase(options.name);
|
|
457
|
-
const targetDir = options.targetDir ||
|
|
458
|
-
const filePath =
|
|
478
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
479
|
+
const filePath = path26.join(targetDir, `${entityName}.ts`);
|
|
459
480
|
const code = generateDomainEntityCode(
|
|
460
481
|
entityName,
|
|
461
482
|
options.kind || "interface",
|
|
@@ -504,8 +525,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
504
525
|
function generateInterface(options) {
|
|
505
526
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
506
527
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
507
|
-
const targetDir = options.targetDir ||
|
|
508
|
-
const filePath =
|
|
528
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
529
|
+
const filePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
509
530
|
const code = generateInterfaceCode(
|
|
510
531
|
ifaceName,
|
|
511
532
|
options.kind || "abstractClass",
|
|
@@ -740,7 +761,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
740
761
|
};
|
|
741
762
|
}
|
|
742
763
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
743
|
-
if (customPath &&
|
|
764
|
+
if (customPath && fs17.existsSync(customPath)) {
|
|
744
765
|
return { filePath: customPath, ifaceName };
|
|
745
766
|
}
|
|
746
767
|
const candidateNames = [
|
|
@@ -750,22 +771,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
750
771
|
];
|
|
751
772
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
752
773
|
for (const feat of features) {
|
|
753
|
-
const domainDir =
|
|
754
|
-
if (!
|
|
774
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
775
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
755
776
|
for (const cand of candidateNames) {
|
|
756
|
-
const p1 =
|
|
757
|
-
if (
|
|
777
|
+
const p1 = path26.join(domainDir, `${cand}.ts`);
|
|
778
|
+
if (fs17.existsSync(p1)) {
|
|
758
779
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
759
780
|
}
|
|
760
781
|
}
|
|
761
782
|
}
|
|
762
783
|
for (const cand of candidateNames) {
|
|
763
|
-
const p1 =
|
|
764
|
-
if (
|
|
765
|
-
const p22 =
|
|
766
|
-
if (
|
|
767
|
-
const p32 =
|
|
768
|
-
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 };
|
|
769
790
|
}
|
|
770
791
|
return null;
|
|
771
792
|
}
|
|
@@ -788,7 +809,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
788
809
|
while (currentFile && !visited.has(currentFile)) {
|
|
789
810
|
visited.add(currentFile);
|
|
790
811
|
try {
|
|
791
|
-
const content =
|
|
812
|
+
const content = fs17.readFileSync(currentFile, "utf-8");
|
|
792
813
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
793
814
|
for (const m of parsed.methods) {
|
|
794
815
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -864,7 +885,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
864
885
|
const entitiesVar = `${entityVar}s`;
|
|
865
886
|
const idKey = `${entityVar}Id`;
|
|
866
887
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
867
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
888
|
+
const entityImportPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
868
889
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
869
890
|
for (const imp of domainImports) {
|
|
870
891
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -913,7 +934,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
913
934
|
if (baseEntity) {
|
|
914
935
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
915
936
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
916
|
-
const importPath = ifaceImportPath ? `${
|
|
937
|
+
const importPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
917
938
|
finalDomainImports.push({
|
|
918
939
|
name: baseEntity,
|
|
919
940
|
from: importPath,
|
|
@@ -958,8 +979,8 @@ function generateInfrastructure(options) {
|
|
|
958
979
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
959
980
|
const className = toPascalCase(options.name);
|
|
960
981
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
961
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
962
|
-
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`);
|
|
963
984
|
let code;
|
|
964
985
|
if (options.implementsInterface) {
|
|
965
986
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -973,7 +994,7 @@ function generateInfrastructure(options) {
|
|
|
973
994
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
974
995
|
let ifaceImportPath;
|
|
975
996
|
if (ifaceFilePath) {
|
|
976
|
-
let rel =
|
|
997
|
+
let rel = path26.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
977
998
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
978
999
|
ifaceImportPath = rel;
|
|
979
1000
|
} else if (isFeatureScope) {
|
|
@@ -986,8 +1007,8 @@ function generateInfrastructure(options) {
|
|
|
986
1007
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
987
1008
|
let fromPath;
|
|
988
1009
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
989
|
-
const absTarget =
|
|
990
|
-
let rel =
|
|
1010
|
+
const absTarget = path26.resolve(path26.dirname(ifaceFilePath), imp.specifier);
|
|
1011
|
+
let rel = path26.relative(targetDir, absTarget).replace(/\\/g, "/");
|
|
991
1012
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
992
1013
|
fromPath = rel;
|
|
993
1014
|
} else {
|
|
@@ -1174,8 +1195,8 @@ function generateUseCase(options) {
|
|
|
1174
1195
|
const files = [];
|
|
1175
1196
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1176
1197
|
const ucName = toPascalCase(options.name);
|
|
1177
|
-
const targetDir = options.targetDir ||
|
|
1178
|
-
const filePath =
|
|
1198
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
|
|
1199
|
+
const filePath = path26.join(targetDir, `${ucName}.ts`);
|
|
1179
1200
|
const code = generateUseCaseCode(
|
|
1180
1201
|
ucName,
|
|
1181
1202
|
options.dependencies || [],
|
|
@@ -1191,7 +1212,7 @@ function generateUseCase(options) {
|
|
|
1191
1212
|
})
|
|
1192
1213
|
);
|
|
1193
1214
|
if (options.includeTest) {
|
|
1194
|
-
const testPath =
|
|
1215
|
+
const testPath = path26.join(targetDir, `${ucName}.test.ts`);
|
|
1195
1216
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1196
1217
|
files.push(
|
|
1197
1218
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1259,10 +1280,10 @@ function generateHttpHandler(options) {
|
|
|
1259
1280
|
const files = [];
|
|
1260
1281
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1261
1282
|
const className = toPascalCase(options.name);
|
|
1262
|
-
const pxDir =
|
|
1263
|
-
const handlersDir =
|
|
1264
|
-
const pxIndexPath =
|
|
1265
|
-
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)) {
|
|
1266
1287
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1267
1288
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1268
1289
|
routePath: featurePath
|
|
@@ -1275,7 +1296,7 @@ function generateHttpHandler(options) {
|
|
|
1275
1296
|
})
|
|
1276
1297
|
);
|
|
1277
1298
|
}
|
|
1278
|
-
const handlerFilePath =
|
|
1299
|
+
const handlerFilePath = path26.join(handlersDir, `${className}.ts`);
|
|
1279
1300
|
const code = generateHttpHandlerCode(
|
|
1280
1301
|
className,
|
|
1281
1302
|
options.method || "get",
|
|
@@ -1296,11 +1317,11 @@ function generateHttpHandler(options) {
|
|
|
1296
1317
|
return files;
|
|
1297
1318
|
}
|
|
1298
1319
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1299
|
-
const providerPath =
|
|
1320
|
+
const providerPath = path26.join(featureDir, "diProvider.ts");
|
|
1300
1321
|
const pascalFeature = toPascalCase(featureName);
|
|
1301
1322
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1302
|
-
if (
|
|
1303
|
-
let content =
|
|
1323
|
+
if (fs17.existsSync(providerPath) && !options.overwrite) {
|
|
1324
|
+
let content = fs17.readFileSync(providerPath, "utf-8");
|
|
1304
1325
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1305
1326
|
for (const b of bindings) {
|
|
1306
1327
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1359,7 +1380,7 @@ function generateFeature(options) {
|
|
|
1359
1380
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1360
1381
|
const rawName = options.name.trim();
|
|
1361
1382
|
const featureName = toCamelCase(rawName);
|
|
1362
|
-
const targetDir = options.targetDir ||
|
|
1383
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1363
1384
|
const routePath = options.routePath || `/${featureName}`;
|
|
1364
1385
|
const rawEntities = [];
|
|
1365
1386
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1380,7 +1401,7 @@ function generateFeature(options) {
|
|
|
1380
1401
|
const domainFile = generateDomain({
|
|
1381
1402
|
name: ent,
|
|
1382
1403
|
feature: featureName,
|
|
1383
|
-
targetDir:
|
|
1404
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1384
1405
|
projectRoot,
|
|
1385
1406
|
dryRun: options.dryRun,
|
|
1386
1407
|
overwrite: options.overwrite,
|
|
@@ -1400,7 +1421,7 @@ function generateFeature(options) {
|
|
|
1400
1421
|
const ifaceFile = generateInterface({
|
|
1401
1422
|
name: ifaceName,
|
|
1402
1423
|
feature: featureName,
|
|
1403
|
-
targetDir:
|
|
1424
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1404
1425
|
entityName: ent,
|
|
1405
1426
|
projectRoot,
|
|
1406
1427
|
dryRun: options.dryRun,
|
|
@@ -1417,7 +1438,7 @@ function generateFeature(options) {
|
|
|
1417
1438
|
const stubFile = generateInfrastructure({
|
|
1418
1439
|
name: stubName,
|
|
1419
1440
|
feature: featureName,
|
|
1420
|
-
targetDir:
|
|
1441
|
+
targetDir: path26.join(targetDir, "infrastructure"),
|
|
1421
1442
|
kind: "stub",
|
|
1422
1443
|
implementsInterface: ifaceName,
|
|
1423
1444
|
entityName: ent,
|
|
@@ -1459,7 +1480,7 @@ function generateFeature(options) {
|
|
|
1459
1480
|
const ucFiles = generateUseCase({
|
|
1460
1481
|
name: ucName,
|
|
1461
1482
|
feature: featureName,
|
|
1462
|
-
targetDir:
|
|
1483
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1463
1484
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1464
1485
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1465
1486
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1504,7 +1525,7 @@ function generateFeature(options) {
|
|
|
1504
1525
|
const ucFiles = generateUseCase({
|
|
1505
1526
|
name: ucName,
|
|
1506
1527
|
feature: featureName,
|
|
1507
|
-
targetDir:
|
|
1528
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1508
1529
|
dependencies: deps,
|
|
1509
1530
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1510
1531
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1537,7 +1558,7 @@ function generateFeature(options) {
|
|
|
1537
1558
|
const ucFiles = generateUseCase({
|
|
1538
1559
|
name: ucName,
|
|
1539
1560
|
feature: featureName,
|
|
1540
|
-
targetDir:
|
|
1561
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1541
1562
|
dependencies: deps,
|
|
1542
1563
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1543
1564
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1570,7 +1591,7 @@ function generateFeature(options) {
|
|
|
1570
1591
|
const ucFiles = generateUseCase({
|
|
1571
1592
|
name: ucName,
|
|
1572
1593
|
feature: featureName,
|
|
1573
|
-
targetDir:
|
|
1594
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1574
1595
|
dependencies: deps,
|
|
1575
1596
|
inputProperties: [
|
|
1576
1597
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1614,7 +1635,7 @@ function generateFeature(options) {
|
|
|
1614
1635
|
const ucFiles = generateUseCase({
|
|
1615
1636
|
name: ucName,
|
|
1616
1637
|
feature: featureName,
|
|
1617
|
-
targetDir:
|
|
1638
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1618
1639
|
dependencies: deps,
|
|
1619
1640
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1620
1641
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1647,7 +1668,7 @@ function generateFeature(options) {
|
|
|
1647
1668
|
const ucFiles = generateUseCase({
|
|
1648
1669
|
name: ucName,
|
|
1649
1670
|
feature: featureName,
|
|
1650
|
-
targetDir:
|
|
1671
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1651
1672
|
dependencies: deps,
|
|
1652
1673
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1653
1674
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -1684,7 +1705,7 @@ function generateFeature(options) {
|
|
|
1684
1705
|
const ucFiles = generateUseCase({
|
|
1685
1706
|
name: ucName,
|
|
1686
1707
|
feature: featureName,
|
|
1687
|
-
targetDir:
|
|
1708
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1688
1709
|
dependencies: customUc.dependencies || [],
|
|
1689
1710
|
inputProperties: customUc.inputProperties || [],
|
|
1690
1711
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -1716,8 +1737,8 @@ function generateFeature(options) {
|
|
|
1716
1737
|
}
|
|
1717
1738
|
}
|
|
1718
1739
|
if (options.includeExpress !== false) {
|
|
1719
|
-
const pxDir =
|
|
1720
|
-
const pxIndexPath =
|
|
1740
|
+
const pxDir = path26.join(targetDir, "pxExpress");
|
|
1741
|
+
const pxIndexPath = path26.join(pxDir, "index.ts");
|
|
1721
1742
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
1722
1743
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
1723
1744
|
files.push(
|
|
@@ -1847,7 +1868,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1847
1868
|
}
|
|
1848
1869
|
const normalizedName = toCamelCase(name);
|
|
1849
1870
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
1850
|
-
const defaultDir =
|
|
1871
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseFeaturesDir, normalizedName));
|
|
1851
1872
|
let targetDirRel = defaultDir;
|
|
1852
1873
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
1853
1874
|
targetDirRel = handleCancel(
|
|
@@ -1858,7 +1879,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1858
1879
|
})
|
|
1859
1880
|
).trim();
|
|
1860
1881
|
}
|
|
1861
|
-
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);
|
|
1862
1883
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
1863
1884
|
let routePath = `/${normalizedName}`;
|
|
1864
1885
|
let includeDomain = true;
|
|
@@ -2100,8 +2121,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2100
2121
|
};
|
|
2101
2122
|
const files = generateFeature(options);
|
|
2102
2123
|
if (isRegistry) {
|
|
2103
|
-
const registryPath =
|
|
2104
|
-
if (!
|
|
2124
|
+
const registryPath = path26.join(targetDir, "registry.json");
|
|
2125
|
+
if (!fs17.existsSync(registryPath)) {
|
|
2105
2126
|
const manifest = {
|
|
2106
2127
|
name: toKebabCase(normalizedName),
|
|
2107
2128
|
type: "feature",
|
|
@@ -2127,7 +2148,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2127
2148
|
printGeneratedFiles(files, options.dryRun);
|
|
2128
2149
|
p19.outro(
|
|
2129
2150
|
pc13.green(
|
|
2130
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2151
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2131
2152
|
)
|
|
2132
2153
|
);
|
|
2133
2154
|
}
|
|
@@ -2138,7 +2159,7 @@ function generateShared(options) {
|
|
|
2138
2159
|
const pascalName = toPascalCase(rawName);
|
|
2139
2160
|
const camelName = toCamelCase(rawName);
|
|
2140
2161
|
const kebabName = toKebabCase(rawName);
|
|
2141
|
-
const targetDir = options.targetDir ||
|
|
2162
|
+
const targetDir = options.targetDir || path26.join(resolveSharedDir(projectRoot), kebabName);
|
|
2142
2163
|
const fileDefs = [];
|
|
2143
2164
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2144
2165
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2150,7 +2171,7 @@ export class ${pascalName} {
|
|
|
2150
2171
|
}
|
|
2151
2172
|
`;
|
|
2152
2173
|
fileDefs.push({
|
|
2153
|
-
absolutePath:
|
|
2174
|
+
absolutePath: path26.join(targetDir, `${pascalName}.ts`),
|
|
2154
2175
|
content: facadeContent
|
|
2155
2176
|
});
|
|
2156
2177
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2158,7 +2179,7 @@ export class ${pascalName} {
|
|
|
2158
2179
|
}
|
|
2159
2180
|
`;
|
|
2160
2181
|
fileDefs.push({
|
|
2161
|
-
absolutePath:
|
|
2182
|
+
absolutePath: path26.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2162
2183
|
content: portContent
|
|
2163
2184
|
});
|
|
2164
2185
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2170,7 +2191,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2170
2191
|
}
|
|
2171
2192
|
`;
|
|
2172
2193
|
fileDefs.push({
|
|
2173
|
-
absolutePath:
|
|
2194
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2174
2195
|
content: concreteContent
|
|
2175
2196
|
});
|
|
2176
2197
|
if (options.includeStub !== false) {
|
|
@@ -2183,7 +2204,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2183
2204
|
}
|
|
2184
2205
|
`;
|
|
2185
2206
|
fileDefs.push({
|
|
2186
|
-
absolutePath:
|
|
2207
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2187
2208
|
content: stubContent
|
|
2188
2209
|
});
|
|
2189
2210
|
}
|
|
@@ -2198,7 +2219,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2198
2219
|
export default diProvider;
|
|
2199
2220
|
`;
|
|
2200
2221
|
fileDefs.push({
|
|
2201
|
-
absolutePath:
|
|
2222
|
+
absolutePath: path26.join(targetDir, "diProvider.ts"),
|
|
2202
2223
|
content: providerContent
|
|
2203
2224
|
});
|
|
2204
2225
|
if (options.isRegistry) {
|
|
@@ -2215,7 +2236,7 @@ export default diProvider;
|
|
|
2215
2236
|
files: []
|
|
2216
2237
|
};
|
|
2217
2238
|
fileDefs.push({
|
|
2218
|
-
absolutePath:
|
|
2239
|
+
absolutePath: path26.join(targetDir, "registry.json"),
|
|
2219
2240
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2220
2241
|
});
|
|
2221
2242
|
}
|
|
@@ -2252,7 +2273,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2252
2273
|
}
|
|
2253
2274
|
const normalizedName = toKebabCase(name);
|
|
2254
2275
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2255
|
-
const defaultDir =
|
|
2276
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseSharedDir, normalizedName));
|
|
2256
2277
|
let targetDirRel = defaultDir;
|
|
2257
2278
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2258
2279
|
targetDirRel = handleCancel(
|
|
@@ -2263,7 +2284,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2263
2284
|
})
|
|
2264
2285
|
).trim();
|
|
2265
2286
|
}
|
|
2266
|
-
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);
|
|
2267
2288
|
let description = flags.description || "";
|
|
2268
2289
|
let includeStub = true;
|
|
2269
2290
|
if (!flags.yes) {
|
|
@@ -2297,7 +2318,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2297
2318
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2298
2319
|
p19.outro(
|
|
2299
2320
|
pc13.green(
|
|
2300
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2321
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2301
2322
|
)
|
|
2302
2323
|
);
|
|
2303
2324
|
}
|
|
@@ -2724,7 +2745,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2724
2745
|
const infraFile = generateInfrastructure(options);
|
|
2725
2746
|
files.push(infraFile);
|
|
2726
2747
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
2727
|
-
const featureDir =
|
|
2748
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2728
2749
|
const providerFile = updateOrGenerateDiProvider(
|
|
2729
2750
|
featureDir,
|
|
2730
2751
|
feature,
|
|
@@ -2886,7 +2907,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
2886
2907
|
files.push(stubFile);
|
|
2887
2908
|
}
|
|
2888
2909
|
if (registerInProvider && generateStub) {
|
|
2889
|
-
const featureDir =
|
|
2910
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2890
2911
|
const providerFile = updateOrGenerateDiProvider(
|
|
2891
2912
|
featureDir,
|
|
2892
2913
|
feature,
|
|
@@ -3074,21 +3095,21 @@ function generateInjectable(options) {
|
|
|
3074
3095
|
const className = toPascalCase(options.name);
|
|
3075
3096
|
let targetDir = "";
|
|
3076
3097
|
if (options.customPath) {
|
|
3077
|
-
targetDir =
|
|
3098
|
+
targetDir = path26.isAbsolute(options.customPath) ? options.customPath : path26.join(projectRoot, options.customPath);
|
|
3078
3099
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3079
|
-
targetDir =
|
|
3100
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3080
3101
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3081
|
-
targetDir =
|
|
3102
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3082
3103
|
} else if (options.location === "module") {
|
|
3083
|
-
targetDir =
|
|
3104
|
+
targetDir = path26.join(projectRoot, "modules", className);
|
|
3084
3105
|
} else {
|
|
3085
|
-
targetDir = options.feature ?
|
|
3106
|
+
targetDir = options.feature ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26.join(projectRoot, "shared", className.toLowerCase());
|
|
3086
3107
|
}
|
|
3087
|
-
const filePath =
|
|
3108
|
+
const filePath = path26.join(targetDir, `${className}.ts`);
|
|
3088
3109
|
let ifaceName;
|
|
3089
3110
|
if (options.generateInterface) {
|
|
3090
3111
|
ifaceName = `I${className}`;
|
|
3091
|
-
const ifacePath =
|
|
3112
|
+
const ifacePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
3092
3113
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3093
3114
|
files.push(
|
|
3094
3115
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3217,7 +3238,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3217
3238
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3218
3239
|
const s = p19.spinner();
|
|
3219
3240
|
s.start("Scanning for .example environment files...");
|
|
3220
|
-
const files =
|
|
3241
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3221
3242
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3222
3243
|
if (exampleFiles.length === 0) {
|
|
3223
3244
|
s.stop("No .example files found.");
|
|
@@ -3227,11 +3248,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3227
3248
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3228
3249
|
const generatedFiles = [];
|
|
3229
3250
|
for (const exampleFile of exampleFiles) {
|
|
3230
|
-
const sourcePath =
|
|
3251
|
+
const sourcePath = path26.join(projectRoot, exampleFile);
|
|
3231
3252
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3232
|
-
const targetPath =
|
|
3253
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3233
3254
|
let overwrite = flags.overwrite;
|
|
3234
|
-
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)) {
|
|
3235
3256
|
overwrite = await p19.confirm({
|
|
3236
3257
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3237
3258
|
initialValue: false
|
|
@@ -3241,7 +3262,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3241
3262
|
process.exit(0);
|
|
3242
3263
|
}
|
|
3243
3264
|
}
|
|
3244
|
-
const content =
|
|
3265
|
+
const content = fs17.readFileSync(sourcePath, "utf-8");
|
|
3245
3266
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3246
3267
|
projectRoot,
|
|
3247
3268
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3259,7 +3280,7 @@ var EnvEditor = class {
|
|
|
3259
3280
|
filePath;
|
|
3260
3281
|
constructor(filePath) {
|
|
3261
3282
|
this.filePath = filePath;
|
|
3262
|
-
this.lines =
|
|
3283
|
+
this.lines = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3263
3284
|
}
|
|
3264
3285
|
getLines() {
|
|
3265
3286
|
return [...this.lines];
|
|
@@ -3323,18 +3344,18 @@ var EnvEditor = class {
|
|
|
3323
3344
|
});
|
|
3324
3345
|
}
|
|
3325
3346
|
save() {
|
|
3326
|
-
const dir =
|
|
3327
|
-
if (!
|
|
3328
|
-
|
|
3347
|
+
const dir = path26.dirname(this.filePath);
|
|
3348
|
+
if (!fs17.existsSync(dir)) {
|
|
3349
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
3329
3350
|
}
|
|
3330
|
-
|
|
3351
|
+
fs17.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3331
3352
|
}
|
|
3332
3353
|
};
|
|
3333
3354
|
function discoverEnvironments(projectRoot) {
|
|
3334
|
-
if (!
|
|
3355
|
+
if (!fs17.existsSync(projectRoot)) {
|
|
3335
3356
|
return ["local", "dev", "prod"];
|
|
3336
3357
|
}
|
|
3337
|
-
const files =
|
|
3358
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3338
3359
|
const envs = /* @__PURE__ */ new Set();
|
|
3339
3360
|
for (const file of files) {
|
|
3340
3361
|
if (file.startsWith(".env")) {
|
|
@@ -3359,7 +3380,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3359
3380
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3360
3381
|
const s = p19.spinner();
|
|
3361
3382
|
s.start("Scanning for local environment files...");
|
|
3362
|
-
const files =
|
|
3383
|
+
const files = fs17.readdirSync(projectRoot, { withFileTypes: true });
|
|
3363
3384
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3364
3385
|
if (envFiles.length === 0) {
|
|
3365
3386
|
s.stop("No local .env files found.");
|
|
@@ -3369,11 +3390,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3369
3390
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3370
3391
|
const generatedFiles = [];
|
|
3371
3392
|
for (const envFile of envFiles) {
|
|
3372
|
-
const sourcePath =
|
|
3393
|
+
const sourcePath = path26.join(projectRoot, envFile);
|
|
3373
3394
|
const targetFile = `${envFile}.example`;
|
|
3374
|
-
const targetPath =
|
|
3395
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3375
3396
|
let overwrite = flags.overwrite;
|
|
3376
|
-
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)) {
|
|
3377
3398
|
overwrite = await p19.confirm({
|
|
3378
3399
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3379
3400
|
initialValue: true
|
|
@@ -3406,12 +3427,12 @@ function isInteractive(flags) {
|
|
|
3406
3427
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3407
3428
|
const keys = /* @__PURE__ */ new Set();
|
|
3408
3429
|
for (const env of envs) {
|
|
3409
|
-
const localPath =
|
|
3410
|
-
const examplePath =
|
|
3411
|
-
if (
|
|
3430
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3431
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3432
|
+
if (fs17.existsSync(localPath)) {
|
|
3412
3433
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3413
3434
|
}
|
|
3414
|
-
if (
|
|
3435
|
+
if (fs17.existsSync(examplePath)) {
|
|
3415
3436
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3416
3437
|
}
|
|
3417
3438
|
}
|
|
@@ -3483,8 +3504,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3483
3504
|
})
|
|
3484
3505
|
);
|
|
3485
3506
|
}
|
|
3486
|
-
const localPath =
|
|
3487
|
-
const examplePath =
|
|
3507
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3508
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3488
3509
|
if (flags.dryRun) {
|
|
3489
3510
|
p19.log.info(
|
|
3490
3511
|
pc13.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3531,8 +3552,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3531
3552
|
);
|
|
3532
3553
|
}
|
|
3533
3554
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3534
|
-
const localPath =
|
|
3535
|
-
return
|
|
3555
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3556
|
+
return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3536
3557
|
});
|
|
3537
3558
|
let targetEnvs = [];
|
|
3538
3559
|
if (flags.env) {
|
|
@@ -3558,7 +3579,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3558
3579
|
return { key: targetKey, environments: [] };
|
|
3559
3580
|
}
|
|
3560
3581
|
for (const env of targetEnvs) {
|
|
3561
|
-
const localPath =
|
|
3582
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3562
3583
|
const localEditor = new EnvEditor(localPath);
|
|
3563
3584
|
const currentVal = localEditor.getValue(targetKey);
|
|
3564
3585
|
let newVal = value;
|
|
@@ -3588,7 +3609,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3588
3609
|
} else {
|
|
3589
3610
|
localEditor.set(targetKey, newVal);
|
|
3590
3611
|
localEditor.save();
|
|
3591
|
-
const examplePath =
|
|
3612
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3592
3613
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3593
3614
|
if (!exampleEditor.has(targetKey)) {
|
|
3594
3615
|
exampleEditor.set(targetKey, "");
|
|
@@ -3624,9 +3645,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3624
3645
|
);
|
|
3625
3646
|
}
|
|
3626
3647
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3627
|
-
const localPath =
|
|
3628
|
-
const examplePath =
|
|
3629
|
-
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);
|
|
3630
3651
|
});
|
|
3631
3652
|
let targetEnvs = [];
|
|
3632
3653
|
if (flags.env) {
|
|
@@ -3664,19 +3685,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3664
3685
|
}
|
|
3665
3686
|
}
|
|
3666
3687
|
for (const env of targetEnvs) {
|
|
3667
|
-
const localPath =
|
|
3668
|
-
const examplePath =
|
|
3688
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3689
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3669
3690
|
if (flags.dryRun) {
|
|
3670
3691
|
p19.log.info(
|
|
3671
3692
|
pc13.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3672
3693
|
);
|
|
3673
3694
|
} else {
|
|
3674
|
-
if (
|
|
3695
|
+
if (fs17.existsSync(localPath)) {
|
|
3675
3696
|
const localEditor = new EnvEditor(localPath);
|
|
3676
3697
|
localEditor.delete(targetKey);
|
|
3677
3698
|
localEditor.save();
|
|
3678
3699
|
}
|
|
3679
|
-
if (
|
|
3700
|
+
if (fs17.existsSync(examplePath)) {
|
|
3680
3701
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3681
3702
|
exampleEditor.delete(targetKey);
|
|
3682
3703
|
exampleEditor.save();
|
|
@@ -3701,10 +3722,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
3701
3722
|
for (const env of envs) {
|
|
3702
3723
|
const localFile = resolveEnvFilename(env, false);
|
|
3703
3724
|
const exampleFile = resolveEnvFilename(env, true);
|
|
3704
|
-
const localPath =
|
|
3705
|
-
const examplePath =
|
|
3706
|
-
const localExists =
|
|
3707
|
-
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);
|
|
3708
3729
|
const localEditor = new EnvEditor(localPath);
|
|
3709
3730
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3710
3731
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4031,25 +4052,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
4031
4052
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
4032
4053
|
}
|
|
4033
4054
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
4034
|
-
return
|
|
4055
|
+
return path26.resolve(baseDir, trimmed);
|
|
4035
4056
|
}
|
|
4036
4057
|
return trimmed;
|
|
4037
4058
|
}
|
|
4038
4059
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
4039
|
-
let current =
|
|
4060
|
+
let current = path26.resolve(startDir);
|
|
4040
4061
|
while (true) {
|
|
4041
|
-
const candidate =
|
|
4042
|
-
if (
|
|
4062
|
+
const candidate = path26.join(current, "mason-registry");
|
|
4063
|
+
if (fs17.existsSync(candidate) && fs17.existsSync(path26.join(candidate, "index.json"))) {
|
|
4043
4064
|
return candidate;
|
|
4044
4065
|
}
|
|
4045
|
-
const parent =
|
|
4066
|
+
const parent = path26.dirname(current);
|
|
4046
4067
|
if (parent === current) break;
|
|
4047
4068
|
current = parent;
|
|
4048
4069
|
}
|
|
4049
4070
|
return null;
|
|
4050
4071
|
}
|
|
4051
4072
|
function getRegistryBase(projectRoot, override) {
|
|
4052
|
-
const rootDir = projectRoot ?
|
|
4073
|
+
const rootDir = projectRoot ? path26.resolve(projectRoot) : process.cwd();
|
|
4053
4074
|
if (override && override.trim()) {
|
|
4054
4075
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
4055
4076
|
}
|
|
@@ -4057,30 +4078,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
4057
4078
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
4058
4079
|
}
|
|
4059
4080
|
const configLocations = [
|
|
4060
|
-
|
|
4061
|
-
|
|
4081
|
+
path26.join(rootDir, "mason.config.json"),
|
|
4082
|
+
path26.join(process.cwd(), "mason.config.json")
|
|
4062
4083
|
];
|
|
4063
4084
|
for (const configPath of configLocations) {
|
|
4064
|
-
if (
|
|
4085
|
+
if (fs17.existsSync(configPath)) {
|
|
4065
4086
|
try {
|
|
4066
|
-
const config = JSON.parse(
|
|
4087
|
+
const config = JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
4067
4088
|
if (config.registry && typeof config.registry === "string") {
|
|
4068
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
4089
|
+
return normalizeRegistryUrl(config.registry.trim(), path26.dirname(configPath));
|
|
4069
4090
|
}
|
|
4070
4091
|
} catch {
|
|
4071
4092
|
}
|
|
4072
4093
|
}
|
|
4073
4094
|
}
|
|
4074
4095
|
const pkgLocations = [
|
|
4075
|
-
|
|
4076
|
-
|
|
4096
|
+
path26.join(rootDir, "package.json"),
|
|
4097
|
+
path26.join(process.cwd(), "package.json")
|
|
4077
4098
|
];
|
|
4078
4099
|
for (const pkgPath of pkgLocations) {
|
|
4079
|
-
if (
|
|
4100
|
+
if (fs17.existsSync(pkgPath)) {
|
|
4080
4101
|
try {
|
|
4081
|
-
const pkg = JSON.parse(
|
|
4102
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
|
|
4082
4103
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
4083
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
4104
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26.dirname(pkgPath));
|
|
4084
4105
|
}
|
|
4085
4106
|
} catch {
|
|
4086
4107
|
}
|
|
@@ -4128,16 +4149,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
4128
4149
|
if (baseDir.startsWith("file://")) {
|
|
4129
4150
|
baseDir = fileURLToPath(baseDir);
|
|
4130
4151
|
}
|
|
4131
|
-
let filePath =
|
|
4132
|
-
if (!
|
|
4133
|
-
const inSrcPath =
|
|
4134
|
-
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)) {
|
|
4135
4156
|
filePath = inSrcPath;
|
|
4136
4157
|
} else {
|
|
4137
4158
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
4138
4159
|
}
|
|
4139
4160
|
}
|
|
4140
|
-
return
|
|
4161
|
+
return fs17.promises.readFile(filePath, "utf-8");
|
|
4141
4162
|
}
|
|
4142
4163
|
async function fetchRegistryIndex(registryBase) {
|
|
4143
4164
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -4199,19 +4220,19 @@ function findClosestMatch(target, candidates) {
|
|
|
4199
4220
|
return null;
|
|
4200
4221
|
}
|
|
4201
4222
|
function detectPackageManager(projectRoot) {
|
|
4202
|
-
if (
|
|
4203
|
-
if (
|
|
4204
|
-
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";
|
|
4205
4226
|
return "npm";
|
|
4206
4227
|
}
|
|
4207
4228
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
4208
4229
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
4209
|
-
const pkgJsonPath =
|
|
4210
|
-
if (!
|
|
4230
|
+
const pkgJsonPath = path26.join(projectRoot, "package.json");
|
|
4231
|
+
if (!fs17.existsSync(pkgJsonPath)) {
|
|
4211
4232
|
return requiredPackages;
|
|
4212
4233
|
}
|
|
4213
4234
|
try {
|
|
4214
|
-
const pkg = JSON.parse(
|
|
4235
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgJsonPath, "utf-8"));
|
|
4215
4236
|
const installed = /* @__PURE__ */ new Set([
|
|
4216
4237
|
...Object.keys(pkg.dependencies || {}),
|
|
4217
4238
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -4250,12 +4271,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
4250
4271
|
});
|
|
4251
4272
|
}
|
|
4252
4273
|
function getAllFiles(dir, baseDir = dir) {
|
|
4253
|
-
if (!
|
|
4254
|
-
const entries =
|
|
4274
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4275
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4255
4276
|
const files = [];
|
|
4256
4277
|
for (const entry of entries) {
|
|
4257
|
-
const fullPath =
|
|
4258
|
-
const relPath =
|
|
4278
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4279
|
+
const relPath = path26.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
4259
4280
|
if (entry.isDirectory()) {
|
|
4260
4281
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4261
4282
|
continue;
|
|
@@ -4271,13 +4292,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
4271
4292
|
return files.sort();
|
|
4272
4293
|
}
|
|
4273
4294
|
function computeDirectoryHash(dirPath, files) {
|
|
4274
|
-
if (!
|
|
4295
|
+
if (!fs17.existsSync(dirPath)) return "";
|
|
4275
4296
|
const hash = createHash("sha256");
|
|
4276
4297
|
const fileList = (getAllFiles(dirPath)).sort();
|
|
4277
4298
|
for (const relativePath of fileList) {
|
|
4278
|
-
const absolutePath =
|
|
4279
|
-
if (!
|
|
4280
|
-
const content =
|
|
4299
|
+
const absolutePath = path26.join(dirPath, relativePath);
|
|
4300
|
+
if (!fs17.existsSync(absolutePath)) continue;
|
|
4301
|
+
const content = fs17.readFileSync(absolutePath, "utf8");
|
|
4281
4302
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
4282
4303
|
hash.update(relativePath);
|
|
4283
4304
|
hash.update(normalizedContent);
|
|
@@ -4315,17 +4336,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4315
4336
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4316
4337
|
}
|
|
4317
4338
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4318
|
-
return
|
|
4339
|
+
return path26.join(projectRoot, "src", folder);
|
|
4319
4340
|
}
|
|
4320
4341
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4321
4342
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4322
4343
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4323
|
-
const inConfigured =
|
|
4324
|
-
if (
|
|
4325
|
-
const inSrc =
|
|
4326
|
-
if (
|
|
4327
|
-
const inRoot =
|
|
4328
|
-
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;
|
|
4329
4350
|
return null;
|
|
4330
4351
|
}
|
|
4331
4352
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4336,14 +4357,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4336
4357
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4337
4358
|
const dirs = [
|
|
4338
4359
|
configuredDir,
|
|
4339
|
-
|
|
4340
|
-
|
|
4360
|
+
path26.join(projectRoot, "src", folder),
|
|
4361
|
+
path26.join(projectRoot, folder)
|
|
4341
4362
|
];
|
|
4342
4363
|
const set = /* @__PURE__ */ new Set();
|
|
4343
4364
|
for (const dir of dirs) {
|
|
4344
|
-
if (
|
|
4365
|
+
if (fs17.existsSync(dir)) {
|
|
4345
4366
|
try {
|
|
4346
|
-
const entries =
|
|
4367
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4347
4368
|
for (const e of entries) {
|
|
4348
4369
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4349
4370
|
set.add(e.name);
|
|
@@ -4545,7 +4566,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4545
4566
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4546
4567
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4547
4568
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4548
|
-
const displayDest =
|
|
4569
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, programmaticAlias));
|
|
4549
4570
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4550
4571
|
resolutions.set(key, {
|
|
4551
4572
|
type: modType,
|
|
@@ -4733,7 +4754,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4733
4754
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4734
4755
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4735
4756
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4736
|
-
const displayDest =
|
|
4757
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, newName));
|
|
4737
4758
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4738
4759
|
resolutions.set(key, {
|
|
4739
4760
|
type: modType,
|
|
@@ -4785,7 +4806,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4785
4806
|
continue;
|
|
4786
4807
|
}
|
|
4787
4808
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4788
|
-
const targetDir =
|
|
4809
|
+
const targetDir = path26.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4789
4810
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4790
4811
|
if (key !== rootKey) {
|
|
4791
4812
|
p19.log.step(
|
|
@@ -4795,30 +4816,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4795
4816
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4796
4817
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4797
4818
|
if (existingDir && existingDir !== targetDir) {
|
|
4798
|
-
|
|
4819
|
+
fs17.rmSync(existingDir, { recursive: true, force: true });
|
|
4799
4820
|
}
|
|
4800
|
-
if (
|
|
4801
|
-
|
|
4821
|
+
if (fs17.existsSync(targetDir)) {
|
|
4822
|
+
fs17.rmSync(targetDir, { recursive: true, force: true });
|
|
4802
4823
|
}
|
|
4803
4824
|
}
|
|
4804
4825
|
for (const file of res.manifest.files || []) {
|
|
4805
4826
|
if (file === "registry.json") continue;
|
|
4806
4827
|
if (!includeTests && isTestFile(file)) continue;
|
|
4807
4828
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4808
|
-
const localFilePath =
|
|
4809
|
-
if (
|
|
4829
|
+
const localFilePath = path26.join(targetDir, file);
|
|
4830
|
+
if (fs17.existsSync(localFilePath) && !isOverwrite) {
|
|
4810
4831
|
p19.log.warn(
|
|
4811
|
-
` \u21B3 Skipping existing file: ${pc13.dim(
|
|
4832
|
+
` \u21B3 Skipping existing file: ${pc13.dim(path26.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4812
4833
|
);
|
|
4813
4834
|
continue;
|
|
4814
4835
|
}
|
|
4815
4836
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4816
4837
|
content = rewriteImports(content, globalAliasMap);
|
|
4817
4838
|
if (!flags.dryRun) {
|
|
4818
|
-
|
|
4819
|
-
|
|
4839
|
+
fs17.mkdirSync(path26.dirname(localFilePath), { recursive: true });
|
|
4840
|
+
fs17.writeFileSync(localFilePath, content, "utf-8");
|
|
4820
4841
|
}
|
|
4821
|
-
const displayPath =
|
|
4842
|
+
const displayPath = path26.relative(projectRoot, localFilePath);
|
|
4822
4843
|
totalWrittenFiles.push(displayPath);
|
|
4823
4844
|
p19.log.message(` ${pc13.green("+")} ${displayPath}`);
|
|
4824
4845
|
}
|
|
@@ -4844,7 +4865,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4844
4865
|
} else {
|
|
4845
4866
|
const rootRes = resolutions.get(rootKey);
|
|
4846
4867
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4847
|
-
const displayTarget =
|
|
4868
|
+
const displayTarget = path26.relative(projectRoot, path26.join(baseDir, rootRes.targetName));
|
|
4848
4869
|
p19.log.success(
|
|
4849
4870
|
pc13.green(
|
|
4850
4871
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4863,19 +4884,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4863
4884
|
}
|
|
4864
4885
|
};
|
|
4865
4886
|
function isTestFile2(filePath, projectRoot) {
|
|
4866
|
-
const norm = (projectRoot ?
|
|
4867
|
-
const base =
|
|
4887
|
+
const norm = (projectRoot ? path26.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4888
|
+
const base = path26.basename(norm);
|
|
4868
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);
|
|
4869
4890
|
}
|
|
4870
4891
|
function scanDirFiles(dir, projectRoot) {
|
|
4871
|
-
if (!
|
|
4872
|
-
const entries =
|
|
4892
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4893
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4873
4894
|
const files = [];
|
|
4874
4895
|
for (const entry of entries) {
|
|
4875
4896
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4876
4897
|
continue;
|
|
4877
4898
|
}
|
|
4878
|
-
const fullPath =
|
|
4899
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4879
4900
|
if (entry.isDirectory()) {
|
|
4880
4901
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4881
4902
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4884,42 +4905,19 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4884
4905
|
}
|
|
4885
4906
|
return files;
|
|
4886
4907
|
}
|
|
4887
|
-
function classifyFile(
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
} else if (subfolder === "infrastructure") {
|
|
4901
|
-
layer = "infrastructure";
|
|
4902
|
-
} else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
|
|
4903
|
-
layer = "presentation";
|
|
4904
|
-
}
|
|
4905
|
-
return { moduleType: "feature", moduleName: feature, feature, layer };
|
|
4906
|
-
}
|
|
4907
|
-
const sharedMatch = rel.match(/(?:^|\/)(?:src\/)?shared\/([^/]+)(?:\/(.*))?$/);
|
|
4908
|
-
if (sharedMatch && sharedMatch[1]) {
|
|
4909
|
-
const moduleName = sharedMatch[1];
|
|
4910
|
-
const rest = sharedMatch[2] || "";
|
|
4911
|
-
let layer = "root";
|
|
4912
|
-
if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
|
|
4913
|
-
layer = "domain";
|
|
4914
|
-
} else if (rest.startsWith("ports/") || rest === "ports") {
|
|
4915
|
-
layer = "ports";
|
|
4916
|
-
} else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
|
|
4917
|
-
layer = "infrastructure";
|
|
4918
|
-
}
|
|
4919
|
-
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4920
|
-
}
|
|
4921
|
-
const errorMatch = rel.match(/(?:^|\/)(?:src\/)?errors(?:\/(.*))?$/);
|
|
4922
|
-
if (errorMatch) {
|
|
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)) {
|
|
4923
4921
|
return {
|
|
4924
4922
|
moduleType: "error",
|
|
4925
4923
|
moduleName: "global-errors",
|
|
@@ -4927,46 +4925,154 @@ function classifyFile(fullPath, projectRoot) {
|
|
|
4927
4925
|
layer: "domain"
|
|
4928
4926
|
};
|
|
4929
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
|
+
}
|
|
4965
|
+
}
|
|
4930
4966
|
return {};
|
|
4931
4967
|
}
|
|
4932
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
4968
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4933
4969
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4934
4970
|
return { isExternal: true, isDiFramework: true };
|
|
4935
4971
|
}
|
|
4936
|
-
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)) {
|
|
4937
4986
|
return { isExternal: true, isDiFramework: false };
|
|
4938
4987
|
}
|
|
4939
|
-
|
|
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;
|
|
4940
4996
|
if (specifier.startsWith(".")) {
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
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 };
|
|
4966
5069
|
}
|
|
4967
|
-
const
|
|
4968
|
-
const
|
|
4969
|
-
|
|
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") {
|
|
4970
5076
|
return {
|
|
4971
5077
|
isExternal: false,
|
|
4972
5078
|
isDiFramework: false,
|
|
@@ -4974,33 +5080,19 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
4974
5080
|
isTargetError: true
|
|
4975
5081
|
};
|
|
4976
5082
|
}
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
const
|
|
4980
|
-
const targetSubfolder =
|
|
4981
|
-
const
|
|
4982
|
-
let targetLayer;
|
|
4983
|
-
if (targetSubfolder === "domain" || targetSubfolder === "errors") {
|
|
4984
|
-
targetLayer = "domain";
|
|
4985
|
-
} else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
|
|
4986
|
-
targetLayer = "useCases";
|
|
4987
|
-
} else if (targetSubfolder === "services") {
|
|
4988
|
-
targetLayer = "services";
|
|
4989
|
-
} else if (targetSubfolder === "infrastructure") {
|
|
4990
|
-
targetLayer = "infrastructure";
|
|
4991
|
-
} else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
|
|
4992
|
-
targetLayer = "presentation";
|
|
4993
|
-
}
|
|
4994
|
-
const basePart = rest ? path2.basename(rest) : targetSubfolder;
|
|
4995
|
-
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)$/, "");
|
|
4996
5088
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
4997
5089
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
4998
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5090
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
4999
5091
|
return {
|
|
5000
5092
|
isExternal: false,
|
|
5001
5093
|
isDiFramework: false,
|
|
5002
|
-
targetFeature,
|
|
5003
|
-
targetLayer,
|
|
5094
|
+
targetFeature: classification.moduleName,
|
|
5095
|
+
targetLayer: classification.layer,
|
|
5004
5096
|
targetSubfolder,
|
|
5005
5097
|
targetFileName,
|
|
5006
5098
|
isTargetPort,
|
|
@@ -5008,25 +5100,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5008
5100
|
isTargetError
|
|
5009
5101
|
};
|
|
5010
5102
|
}
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
const
|
|
5014
|
-
const sharedRest = sharedMatch[2] || "";
|
|
5015
|
-
const isSharedRoot = !sharedRest.includes("/");
|
|
5016
|
-
const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
|
|
5017
|
-
const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
|
|
5018
|
-
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/"));
|
|
5019
5106
|
return {
|
|
5020
5107
|
isExternal: false,
|
|
5021
5108
|
isDiFramework: false,
|
|
5022
|
-
targetSharedModule,
|
|
5023
|
-
isSharedRoot,
|
|
5024
|
-
isSharedPorts,
|
|
5025
|
-
isSharedInfra,
|
|
5026
|
-
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
|
|
5027
5115
|
};
|
|
5028
5116
|
}
|
|
5029
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5117
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
5030
5118
|
}
|
|
5031
5119
|
function extractImportStatements(content) {
|
|
5032
5120
|
const statements = [];
|
|
@@ -5148,22 +5236,50 @@ var FeatureDependencyGraph = class {
|
|
|
5148
5236
|
return dfs(start);
|
|
5149
5237
|
}
|
|
5150
5238
|
};
|
|
5151
|
-
function discoverAllFeatures(
|
|
5152
|
-
const
|
|
5153
|
-
if (!
|
|
5154
|
-
const
|
|
5155
|
-
|
|
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) => ({
|
|
5156
5258
|
name: e.name,
|
|
5157
|
-
dir:
|
|
5259
|
+
dir: path26.join(base, e.name)
|
|
5158
5260
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5159
5261
|
}
|
|
5160
|
-
function discoverAllShared(
|
|
5161
|
-
const
|
|
5162
|
-
if (!
|
|
5163
|
-
const
|
|
5164
|
-
|
|
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) => ({
|
|
5165
5281
|
name: e.name,
|
|
5166
|
-
dir:
|
|
5282
|
+
dir: path26.join(base, e.name)
|
|
5167
5283
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5168
5284
|
}
|
|
5169
5285
|
function extractClassMethods(content, className) {
|
|
@@ -5319,7 +5435,7 @@ function extractClassMethods(content, className) {
|
|
|
5319
5435
|
return methods;
|
|
5320
5436
|
}
|
|
5321
5437
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5322
|
-
const fileName =
|
|
5438
|
+
const fileName = path26.basename(filePath);
|
|
5323
5439
|
const lines = content.split(/\r?\n/);
|
|
5324
5440
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5325
5441
|
if (!classMatch) {
|
|
@@ -5415,7 +5531,7 @@ private ${m.name}(...) { ... }`
|
|
|
5415
5531
|
}
|
|
5416
5532
|
}
|
|
5417
5533
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5418
|
-
const fileName =
|
|
5534
|
+
const fileName = path26.basename(filePath);
|
|
5419
5535
|
const lines = content.split(/\r?\n/);
|
|
5420
5536
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5421
5537
|
if (!classMatch) {
|
|
@@ -5515,18 +5631,11 @@ function lintCodebase(options = {}) {
|
|
|
5515
5631
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5516
5632
|
const failFast = !options.complete;
|
|
5517
5633
|
const lintConfig = loadLintConfig(projectRoot);
|
|
5518
|
-
const
|
|
5519
|
-
const baseErrorAbs = path2.resolve(projectRoot, baseErrorRelative);
|
|
5520
|
-
const featuresBase = resolveFeaturesDir(projectRoot);
|
|
5521
|
-
const sharedBase = resolveSharedDir(projectRoot);
|
|
5522
|
-
let errorsBase = path2.join(projectRoot, "src/errors");
|
|
5523
|
-
if (!fs14.existsSync(errorsBase) && fs14.existsSync(path2.join(projectRoot, "errors"))) {
|
|
5524
|
-
errorsBase = path2.join(projectRoot, "errors");
|
|
5525
|
-
}
|
|
5634
|
+
const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
|
|
5526
5635
|
const missingFoundations = [];
|
|
5527
|
-
if (!
|
|
5528
|
-
if (!
|
|
5529
|
-
if (!
|
|
5636
|
+
if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5637
|
+
if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5638
|
+
if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5530
5639
|
if (missingFoundations.length > 0) {
|
|
5531
5640
|
throw new DependencyRuleError(
|
|
5532
5641
|
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
@@ -5534,20 +5643,22 @@ function lintCodebase(options = {}) {
|
|
|
5534
5643
|
true
|
|
5535
5644
|
);
|
|
5536
5645
|
}
|
|
5537
|
-
|
|
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)) {
|
|
5538
5649
|
throw new DependencyRuleError(
|
|
5539
5650
|
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5540
5651
|
[],
|
|
5541
5652
|
true
|
|
5542
5653
|
);
|
|
5543
5654
|
}
|
|
5544
|
-
const baseErrorContent =
|
|
5655
|
+
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5545
5656
|
const baseClassMatch = baseErrorContent.match(
|
|
5546
5657
|
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5547
5658
|
);
|
|
5548
5659
|
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5549
|
-
const features = discoverAllFeatures(
|
|
5550
|
-
const sharedModules = discoverAllShared(
|
|
5660
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5661
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5551
5662
|
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5552
5663
|
const allModules = [
|
|
5553
5664
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
@@ -5590,12 +5701,12 @@ Code: ${v.code}`;
|
|
|
5590
5701
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5591
5702
|
modResult.scannedFilesCount = allFiles.length;
|
|
5592
5703
|
for (const file of allFiles) {
|
|
5593
|
-
const relPath =
|
|
5594
|
-
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);
|
|
5595
5706
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5596
|
-
const content =
|
|
5707
|
+
const content = fs17.readFileSync(file, "utf-8");
|
|
5597
5708
|
const statements = extractImportStatements(content);
|
|
5598
|
-
const baseName =
|
|
5709
|
+
const baseName = path26.basename(file);
|
|
5599
5710
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5600
5711
|
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5601
5712
|
let throwMatch;
|
|
@@ -5622,7 +5733,7 @@ Code: ${v.code}`;
|
|
|
5622
5733
|
});
|
|
5623
5734
|
}
|
|
5624
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");
|
|
5625
|
-
const isBaseErrorFile =
|
|
5736
|
+
const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5626
5737
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5627
5738
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5628
5739
|
let classMatch;
|
|
@@ -5672,7 +5783,7 @@ Code: ${v.code}`;
|
|
|
5672
5783
|
}
|
|
5673
5784
|
if (moduleType === "error") {
|
|
5674
5785
|
for (const stmt of statements) {
|
|
5675
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5786
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5676
5787
|
if (target.targetFeature) {
|
|
5677
5788
|
addViolation({
|
|
5678
5789
|
feature: "global-errors",
|
|
@@ -5738,7 +5849,7 @@ Code: ${v.code}`;
|
|
|
5738
5849
|
}
|
|
5739
5850
|
}
|
|
5740
5851
|
for (const stmt of statements) {
|
|
5741
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5852
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5742
5853
|
if (target.targetFeature) {
|
|
5743
5854
|
addViolation({
|
|
5744
5855
|
feature: moduleName,
|
|
@@ -5868,7 +5979,7 @@ Code: ${v.code}`;
|
|
|
5868
5979
|
}
|
|
5869
5980
|
}
|
|
5870
5981
|
for (const stmt of statements) {
|
|
5871
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5982
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5872
5983
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5873
5984
|
const evidence = {
|
|
5874
5985
|
fromFeature: feat.name,
|