@solid-stack/mason 1.0.33 → 1.0.35
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 +495 -394
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +493 -392
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +10 -4
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +526 -393
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +524 -392
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lint.d.ts +7 -1
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +16 -6
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
- package/schema.json +7 -13
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,60 @@ 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
|
|
191
|
-
}
|
|
192
|
-
function loadLintConfig(projectRoot) {
|
|
193
|
-
const configPath = path2.join(projectRoot, "mason-lint.config.json");
|
|
194
|
-
if (fs14.existsSync(configPath)) {
|
|
195
|
-
try {
|
|
196
|
-
return JSON.parse(fs14.readFileSync(configPath, "utf-8"));
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return { baseErrorPath: "src/errors/AppError.ts" };
|
|
190
|
+
return path26.join(projectRoot, "shared");
|
|
201
191
|
}
|
|
202
192
|
|
|
203
193
|
// src/utils/project.ts
|
|
204
194
|
function findProjectRoot(startDir = process.cwd()) {
|
|
205
|
-
let current =
|
|
195
|
+
let current = path26.resolve(startDir);
|
|
206
196
|
while (true) {
|
|
207
|
-
if (
|
|
197
|
+
if (fs17.existsSync(path26.join(current, "package.json"))) {
|
|
208
198
|
return current;
|
|
209
199
|
}
|
|
210
|
-
const parent =
|
|
200
|
+
const parent = path26.dirname(current);
|
|
211
201
|
if (parent === current) {
|
|
212
|
-
return
|
|
202
|
+
return path26.resolve(startDir);
|
|
213
203
|
}
|
|
214
204
|
current = parent;
|
|
215
205
|
}
|
|
@@ -222,9 +212,9 @@ function resolveSharedDir(projectRoot, flags) {
|
|
|
222
212
|
}
|
|
223
213
|
function listExistingFeatures(projectRoot) {
|
|
224
214
|
const featuresDir = resolveFeaturesDir(projectRoot);
|
|
225
|
-
if (!
|
|
215
|
+
if (!fs17.existsSync(featuresDir)) return [];
|
|
226
216
|
try {
|
|
227
|
-
const entries =
|
|
217
|
+
const entries = fs17.readdirSync(featuresDir, { withFileTypes: true });
|
|
228
218
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
229
219
|
} catch {
|
|
230
220
|
return [];
|
|
@@ -234,14 +224,14 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
234
224
|
const results = [];
|
|
235
225
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
236
226
|
for (const feat of features) {
|
|
237
|
-
const domainDir =
|
|
238
|
-
if (!
|
|
227
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
228
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
239
229
|
try {
|
|
240
|
-
const files =
|
|
230
|
+
const files = fs17.readdirSync(domainDir);
|
|
241
231
|
for (const file of files) {
|
|
242
232
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
243
233
|
const name = file.replace(/\.ts$/, "");
|
|
244
|
-
const fullPath =
|
|
234
|
+
const fullPath = path26.join(domainDir, file);
|
|
245
235
|
if (/^I[A-Z]/.test(name)) {
|
|
246
236
|
results.push({
|
|
247
237
|
name,
|
|
@@ -250,7 +240,7 @@ function listDomainInterfaces(projectRoot, featureName) {
|
|
|
250
240
|
});
|
|
251
241
|
} else {
|
|
252
242
|
try {
|
|
253
|
-
const content =
|
|
243
|
+
const content = fs17.readFileSync(fullPath, "utf-8");
|
|
254
244
|
if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
|
|
255
245
|
results.push({
|
|
256
246
|
name,
|
|
@@ -272,17 +262,17 @@ function listDomainEntities(projectRoot, featureName) {
|
|
|
272
262
|
const results = [];
|
|
273
263
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
274
264
|
for (const feat of features) {
|
|
275
|
-
const domainDir =
|
|
276
|
-
if (!
|
|
265
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
266
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
277
267
|
try {
|
|
278
|
-
const files =
|
|
268
|
+
const files = fs17.readdirSync(domainDir);
|
|
279
269
|
for (const file of files) {
|
|
280
270
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
281
271
|
const name = file.replace(/\.ts$/, "");
|
|
282
272
|
if (!/^I[A-Z]/.test(name)) {
|
|
283
273
|
results.push({
|
|
284
274
|
name,
|
|
285
|
-
fullPath:
|
|
275
|
+
fullPath: path26.join(domainDir, file),
|
|
286
276
|
feature: feat
|
|
287
277
|
});
|
|
288
278
|
}
|
|
@@ -297,16 +287,16 @@ function listUseCases(projectRoot, featureName) {
|
|
|
297
287
|
const results = [];
|
|
298
288
|
const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
|
|
299
289
|
for (const feat of features) {
|
|
300
|
-
const useCasesDir =
|
|
301
|
-
if (!
|
|
290
|
+
const useCasesDir = path26.join(resolveFeaturesDir(projectRoot), feat, "useCases");
|
|
291
|
+
if (!fs17.existsSync(useCasesDir)) continue;
|
|
302
292
|
try {
|
|
303
|
-
const files =
|
|
293
|
+
const files = fs17.readdirSync(useCasesDir);
|
|
304
294
|
for (const file of files) {
|
|
305
295
|
if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
|
|
306
296
|
const name = file.replace(/\.ts$/, "");
|
|
307
297
|
results.push({
|
|
308
298
|
name,
|
|
309
|
-
fullPath:
|
|
299
|
+
fullPath: path26.join(useCasesDir, file),
|
|
310
300
|
feature: feat
|
|
311
301
|
});
|
|
312
302
|
}
|
|
@@ -332,33 +322,33 @@ hbs.registerHelper("join", (arr, sep) => {
|
|
|
332
322
|
});
|
|
333
323
|
var cachedTemplatesDir = null;
|
|
334
324
|
function getTemplatesDir() {
|
|
335
|
-
if (cachedTemplatesDir &&
|
|
325
|
+
if (cachedTemplatesDir && fs17.existsSync(cachedTemplatesDir)) {
|
|
336
326
|
return cachedTemplatesDir;
|
|
337
327
|
}
|
|
338
|
-
const currentDir = typeof __dirname !== "undefined" ? __dirname :
|
|
339
|
-
const cand1 =
|
|
340
|
-
if (
|
|
328
|
+
const currentDir = typeof __dirname !== "undefined" ? __dirname : path26.dirname(fileURLToPath(import.meta.url));
|
|
329
|
+
const cand1 = path26.resolve(currentDir, "../../templates");
|
|
330
|
+
if (fs17.existsSync(cand1)) {
|
|
341
331
|
cachedTemplatesDir = cand1;
|
|
342
332
|
return cand1;
|
|
343
333
|
}
|
|
344
|
-
const cand2 =
|
|
345
|
-
if (
|
|
334
|
+
const cand2 = path26.resolve(currentDir, "../templates");
|
|
335
|
+
if (fs17.existsSync(cand2)) {
|
|
346
336
|
cachedTemplatesDir = cand2;
|
|
347
337
|
return cand2;
|
|
348
338
|
}
|
|
349
|
-
const cand3 =
|
|
350
|
-
if (
|
|
339
|
+
const cand3 = path26.resolve(currentDir, "templates");
|
|
340
|
+
if (fs17.existsSync(cand3)) {
|
|
351
341
|
cachedTemplatesDir = cand3;
|
|
352
342
|
return cand3;
|
|
353
343
|
}
|
|
354
344
|
let curr = currentDir;
|
|
355
345
|
while (true) {
|
|
356
|
-
const candTemplates =
|
|
357
|
-
if (
|
|
346
|
+
const candTemplates = path26.join(curr, "templates");
|
|
347
|
+
if (fs17.existsSync(candTemplates)) {
|
|
358
348
|
cachedTemplatesDir = candTemplates;
|
|
359
349
|
return candTemplates;
|
|
360
350
|
}
|
|
361
|
-
const parent =
|
|
351
|
+
const parent = path26.dirname(curr);
|
|
362
352
|
if (parent === curr) break;
|
|
363
353
|
curr = parent;
|
|
364
354
|
}
|
|
@@ -366,8 +356,8 @@ function getTemplatesDir() {
|
|
|
366
356
|
}
|
|
367
357
|
var templateCache = /* @__PURE__ */ new Map();
|
|
368
358
|
function loadTemplate(relativePath) {
|
|
369
|
-
const fullPath =
|
|
370
|
-
return
|
|
359
|
+
const fullPath = path26.isAbsolute(relativePath) ? relativePath : path26.join(getTemplatesDir(), relativePath);
|
|
360
|
+
return fs17.readFileSync(fullPath, "utf-8");
|
|
371
361
|
}
|
|
372
362
|
function renderTemplateFile(relativePath, context) {
|
|
373
363
|
let compiled = templateCache.get(relativePath);
|
|
@@ -454,8 +444,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
|
|
|
454
444
|
function generateDomain(options) {
|
|
455
445
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
456
446
|
const entityName = toPascalCase(options.name);
|
|
457
|
-
const targetDir = options.targetDir ||
|
|
458
|
-
const filePath =
|
|
447
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
448
|
+
const filePath = path26.join(targetDir, `${entityName}.ts`);
|
|
459
449
|
const code = generateDomainEntityCode(
|
|
460
450
|
entityName,
|
|
461
451
|
options.kind || "interface",
|
|
@@ -504,8 +494,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
504
494
|
function generateInterface(options) {
|
|
505
495
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
506
496
|
const ifaceName = ensureInterfacePrefix(options.name);
|
|
507
|
-
const targetDir = options.targetDir ||
|
|
508
|
-
const filePath =
|
|
497
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
|
|
498
|
+
const filePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
509
499
|
const code = generateInterfaceCode(
|
|
510
500
|
ifaceName,
|
|
511
501
|
options.kind || "abstractClass",
|
|
@@ -740,7 +730,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
|
|
|
740
730
|
};
|
|
741
731
|
}
|
|
742
732
|
function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
743
|
-
if (customPath &&
|
|
733
|
+
if (customPath && fs17.existsSync(customPath)) {
|
|
744
734
|
return { filePath: customPath, ifaceName };
|
|
745
735
|
}
|
|
746
736
|
const candidateNames = [
|
|
@@ -750,22 +740,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
|
|
|
750
740
|
];
|
|
751
741
|
const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
|
|
752
742
|
for (const feat of features) {
|
|
753
|
-
const domainDir =
|
|
754
|
-
if (!
|
|
743
|
+
const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
|
|
744
|
+
if (!fs17.existsSync(domainDir)) continue;
|
|
755
745
|
for (const cand of candidateNames) {
|
|
756
|
-
const p1 =
|
|
757
|
-
if (
|
|
746
|
+
const p1 = path26.join(domainDir, `${cand}.ts`);
|
|
747
|
+
if (fs17.existsSync(p1)) {
|
|
758
748
|
return { filePath: p1, ifaceName: cand, feature: feat };
|
|
759
749
|
}
|
|
760
750
|
}
|
|
761
751
|
}
|
|
762
752
|
for (const cand of candidateNames) {
|
|
763
|
-
const p1 =
|
|
764
|
-
if (
|
|
765
|
-
const p22 =
|
|
766
|
-
if (
|
|
767
|
-
const p32 =
|
|
768
|
-
if (
|
|
753
|
+
const p1 = path26.join(projectRoot, "domain", `${cand}.ts`);
|
|
754
|
+
if (fs17.existsSync(p1)) return { filePath: p1, ifaceName: cand };
|
|
755
|
+
const p22 = path26.join(projectRoot, "src", "domain", `${cand}.ts`);
|
|
756
|
+
if (fs17.existsSync(p22)) return { filePath: p22, ifaceName: cand };
|
|
757
|
+
const p32 = path26.join(projectRoot, "interfaces", `${cand}.ts`);
|
|
758
|
+
if (fs17.existsSync(p32)) return { filePath: p32, ifaceName: cand };
|
|
769
759
|
}
|
|
770
760
|
return null;
|
|
771
761
|
}
|
|
@@ -788,7 +778,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
|
|
|
788
778
|
while (currentFile && !visited.has(currentFile)) {
|
|
789
779
|
visited.add(currentFile);
|
|
790
780
|
try {
|
|
791
|
-
const content =
|
|
781
|
+
const content = fs17.readFileSync(currentFile, "utf-8");
|
|
792
782
|
const parsed = parseInterfaceMethodsAndImports(content, currentName);
|
|
793
783
|
for (const m of parsed.methods) {
|
|
794
784
|
if (!allMethods.some((existing) => existing.name === m.name)) {
|
|
@@ -864,7 +854,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
|
|
|
864
854
|
const entitiesVar = `${entityVar}s`;
|
|
865
855
|
const idKey = `${entityVar}Id`;
|
|
866
856
|
const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
|
|
867
|
-
const entityImportPath = ifaceImportPath ? `${
|
|
857
|
+
const entityImportPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
868
858
|
const importsByFrom = /* @__PURE__ */ new Map();
|
|
869
859
|
for (const imp of domainImports) {
|
|
870
860
|
if (imp.name === ifaceName || imp.name === baseEntity) continue;
|
|
@@ -913,7 +903,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
|
|
|
913
903
|
if (baseEntity) {
|
|
914
904
|
finalMethods = synthesizeCrudMethods(baseEntity);
|
|
915
905
|
if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
|
|
916
|
-
const importPath = ifaceImportPath ? `${
|
|
906
|
+
const importPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
|
|
917
907
|
finalDomainImports.push({
|
|
918
908
|
name: baseEntity,
|
|
919
909
|
from: importPath,
|
|
@@ -958,8 +948,8 @@ function generateInfrastructure(options) {
|
|
|
958
948
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
959
949
|
const className = toPascalCase(options.name);
|
|
960
950
|
const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
|
|
961
|
-
const targetDir = options.targetDir || (isFeatureScope ?
|
|
962
|
-
const filePath =
|
|
951
|
+
const targetDir = options.targetDir || (isFeatureScope ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path26.join(projectRoot, "infrastructure"));
|
|
952
|
+
const filePath = path26.join(targetDir, `${className}.ts`);
|
|
963
953
|
let code;
|
|
964
954
|
if (options.implementsInterface) {
|
|
965
955
|
const rawIfaceName = options.implementsInterface;
|
|
@@ -973,7 +963,7 @@ function generateInfrastructure(options) {
|
|
|
973
963
|
const ifaceFilePath = hierarchy.ifaceFilePath;
|
|
974
964
|
let ifaceImportPath;
|
|
975
965
|
if (ifaceFilePath) {
|
|
976
|
-
let rel =
|
|
966
|
+
let rel = path26.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
977
967
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
978
968
|
ifaceImportPath = rel;
|
|
979
969
|
} else if (isFeatureScope) {
|
|
@@ -986,8 +976,8 @@ function generateInfrastructure(options) {
|
|
|
986
976
|
if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
|
|
987
977
|
let fromPath;
|
|
988
978
|
if (imp.specifier.startsWith(".") && ifaceFilePath) {
|
|
989
|
-
const absTarget =
|
|
990
|
-
let rel =
|
|
979
|
+
const absTarget = path26.resolve(path26.dirname(ifaceFilePath), imp.specifier);
|
|
980
|
+
let rel = path26.relative(targetDir, absTarget).replace(/\\/g, "/");
|
|
991
981
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
992
982
|
fromPath = rel;
|
|
993
983
|
} else {
|
|
@@ -1174,8 +1164,8 @@ function generateUseCase(options) {
|
|
|
1174
1164
|
const files = [];
|
|
1175
1165
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1176
1166
|
const ucName = toPascalCase(options.name);
|
|
1177
|
-
const targetDir = options.targetDir ||
|
|
1178
|
-
const filePath =
|
|
1167
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
|
|
1168
|
+
const filePath = path26.join(targetDir, `${ucName}.ts`);
|
|
1179
1169
|
const code = generateUseCaseCode(
|
|
1180
1170
|
ucName,
|
|
1181
1171
|
options.dependencies || [],
|
|
@@ -1191,7 +1181,7 @@ function generateUseCase(options) {
|
|
|
1191
1181
|
})
|
|
1192
1182
|
);
|
|
1193
1183
|
if (options.includeTest) {
|
|
1194
|
-
const testPath =
|
|
1184
|
+
const testPath = path26.join(targetDir, `${ucName}.test.ts`);
|
|
1195
1185
|
const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
|
|
1196
1186
|
files.push(
|
|
1197
1187
|
writeFileSafely(testPath, testCode, {
|
|
@@ -1259,10 +1249,10 @@ function generateHttpHandler(options) {
|
|
|
1259
1249
|
const files = [];
|
|
1260
1250
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1261
1251
|
const className = toPascalCase(options.name);
|
|
1262
|
-
const pxDir =
|
|
1263
|
-
const handlersDir =
|
|
1264
|
-
const pxIndexPath =
|
|
1265
|
-
if (!
|
|
1252
|
+
const pxDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
|
|
1253
|
+
const handlersDir = path26.join(pxDir, "handlers");
|
|
1254
|
+
const pxIndexPath = path26.join(pxDir, "index.ts");
|
|
1255
|
+
if (!fs17.existsSync(pxIndexPath)) {
|
|
1266
1256
|
const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
|
|
1267
1257
|
const pxIndexContent = renderTemplate(expressIndexTemplate, {
|
|
1268
1258
|
routePath: featurePath
|
|
@@ -1275,7 +1265,7 @@ function generateHttpHandler(options) {
|
|
|
1275
1265
|
})
|
|
1276
1266
|
);
|
|
1277
1267
|
}
|
|
1278
|
-
const handlerFilePath =
|
|
1268
|
+
const handlerFilePath = path26.join(handlersDir, `${className}.ts`);
|
|
1279
1269
|
const code = generateHttpHandlerCode(
|
|
1280
1270
|
className,
|
|
1281
1271
|
options.method || "get",
|
|
@@ -1296,11 +1286,11 @@ function generateHttpHandler(options) {
|
|
|
1296
1286
|
return files;
|
|
1297
1287
|
}
|
|
1298
1288
|
function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
|
|
1299
|
-
const providerPath =
|
|
1289
|
+
const providerPath = path26.join(featureDir, "diProvider.ts");
|
|
1300
1290
|
const pascalFeature = toPascalCase(featureName);
|
|
1301
1291
|
const providerModuleName = `${pascalFeature}Provider`;
|
|
1302
|
-
if (
|
|
1303
|
-
let content =
|
|
1292
|
+
if (fs17.existsSync(providerPath) && !options.overwrite) {
|
|
1293
|
+
let content = fs17.readFileSync(providerPath, "utf-8");
|
|
1304
1294
|
content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
|
|
1305
1295
|
for (const b of bindings) {
|
|
1306
1296
|
const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
|
|
@@ -1359,7 +1349,7 @@ function generateFeature(options) {
|
|
|
1359
1349
|
const projectRoot = options.projectRoot || findProjectRoot();
|
|
1360
1350
|
const rawName = options.name.trim();
|
|
1361
1351
|
const featureName = toCamelCase(rawName);
|
|
1362
|
-
const targetDir = options.targetDir ||
|
|
1352
|
+
const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), featureName);
|
|
1363
1353
|
const routePath = options.routePath || `/${featureName}`;
|
|
1364
1354
|
const rawEntities = [];
|
|
1365
1355
|
if (options.entityNames && options.entityNames.length > 0) {
|
|
@@ -1380,7 +1370,7 @@ function generateFeature(options) {
|
|
|
1380
1370
|
const domainFile = generateDomain({
|
|
1381
1371
|
name: ent,
|
|
1382
1372
|
feature: featureName,
|
|
1383
|
-
targetDir:
|
|
1373
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1384
1374
|
projectRoot,
|
|
1385
1375
|
dryRun: options.dryRun,
|
|
1386
1376
|
overwrite: options.overwrite,
|
|
@@ -1400,7 +1390,7 @@ function generateFeature(options) {
|
|
|
1400
1390
|
const ifaceFile = generateInterface({
|
|
1401
1391
|
name: ifaceName,
|
|
1402
1392
|
feature: featureName,
|
|
1403
|
-
targetDir:
|
|
1393
|
+
targetDir: path26.join(targetDir, "domain"),
|
|
1404
1394
|
entityName: ent,
|
|
1405
1395
|
projectRoot,
|
|
1406
1396
|
dryRun: options.dryRun,
|
|
@@ -1417,7 +1407,7 @@ function generateFeature(options) {
|
|
|
1417
1407
|
const stubFile = generateInfrastructure({
|
|
1418
1408
|
name: stubName,
|
|
1419
1409
|
feature: featureName,
|
|
1420
|
-
targetDir:
|
|
1410
|
+
targetDir: path26.join(targetDir, "infrastructure"),
|
|
1421
1411
|
kind: "stub",
|
|
1422
1412
|
implementsInterface: ifaceName,
|
|
1423
1413
|
entityName: ent,
|
|
@@ -1459,7 +1449,7 @@ function generateFeature(options) {
|
|
|
1459
1449
|
const ucFiles = generateUseCase({
|
|
1460
1450
|
name: ucName,
|
|
1461
1451
|
feature: featureName,
|
|
1462
|
-
targetDir:
|
|
1452
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1463
1453
|
dependencies: options.includeInterface !== false ? [ifaceName] : [],
|
|
1464
1454
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1465
1455
|
outputProperties: [{ name: entVar, type: primaryEntity }],
|
|
@@ -1504,7 +1494,7 @@ function generateFeature(options) {
|
|
|
1504
1494
|
const ucFiles = generateUseCase({
|
|
1505
1495
|
name: ucName,
|
|
1506
1496
|
feature: featureName,
|
|
1507
|
-
targetDir:
|
|
1497
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1508
1498
|
dependencies: deps,
|
|
1509
1499
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1510
1500
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1537,7 +1527,7 @@ function generateFeature(options) {
|
|
|
1537
1527
|
const ucFiles = generateUseCase({
|
|
1538
1528
|
name: ucName,
|
|
1539
1529
|
feature: featureName,
|
|
1540
|
-
targetDir:
|
|
1530
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1541
1531
|
dependencies: deps,
|
|
1542
1532
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1543
1533
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1570,7 +1560,7 @@ function generateFeature(options) {
|
|
|
1570
1560
|
const ucFiles = generateUseCase({
|
|
1571
1561
|
name: ucName,
|
|
1572
1562
|
feature: featureName,
|
|
1573
|
-
targetDir:
|
|
1563
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1574
1564
|
dependencies: deps,
|
|
1575
1565
|
inputProperties: [
|
|
1576
1566
|
{ name: "page", type: "number", optional: true },
|
|
@@ -1614,7 +1604,7 @@ function generateFeature(options) {
|
|
|
1614
1604
|
const ucFiles = generateUseCase({
|
|
1615
1605
|
name: ucName,
|
|
1616
1606
|
feature: featureName,
|
|
1617
|
-
targetDir:
|
|
1607
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1618
1608
|
dependencies: deps,
|
|
1619
1609
|
inputProperties: [{ name: entVar, type: ent }],
|
|
1620
1610
|
outputProperties: [{ name: entVar, type: ent }],
|
|
@@ -1647,7 +1637,7 @@ function generateFeature(options) {
|
|
|
1647
1637
|
const ucFiles = generateUseCase({
|
|
1648
1638
|
name: ucName,
|
|
1649
1639
|
feature: featureName,
|
|
1650
|
-
targetDir:
|
|
1640
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1651
1641
|
dependencies: deps,
|
|
1652
1642
|
inputProperties: [{ name: idParam, type: "string" }],
|
|
1653
1643
|
outputProperties: [{ name: "success", type: "boolean" }],
|
|
@@ -1684,7 +1674,7 @@ function generateFeature(options) {
|
|
|
1684
1674
|
const ucFiles = generateUseCase({
|
|
1685
1675
|
name: ucName,
|
|
1686
1676
|
feature: featureName,
|
|
1687
|
-
targetDir:
|
|
1677
|
+
targetDir: path26.join(targetDir, "useCases"),
|
|
1688
1678
|
dependencies: customUc.dependencies || [],
|
|
1689
1679
|
inputProperties: customUc.inputProperties || [],
|
|
1690
1680
|
outputProperties: customUc.outputProperties || [],
|
|
@@ -1716,8 +1706,8 @@ function generateFeature(options) {
|
|
|
1716
1706
|
}
|
|
1717
1707
|
}
|
|
1718
1708
|
if (options.includeExpress !== false) {
|
|
1719
|
-
const pxDir =
|
|
1720
|
-
const pxIndexPath =
|
|
1709
|
+
const pxDir = path26.join(targetDir, "pxExpress");
|
|
1710
|
+
const pxIndexPath = path26.join(pxDir, "index.ts");
|
|
1721
1711
|
if (!files.some((f) => f.absolutePath === pxIndexPath)) {
|
|
1722
1712
|
const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
|
|
1723
1713
|
files.push(
|
|
@@ -1847,7 +1837,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1847
1837
|
}
|
|
1848
1838
|
const normalizedName = toCamelCase(name);
|
|
1849
1839
|
const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
|
|
1850
|
-
const defaultDir =
|
|
1840
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseFeaturesDir, normalizedName));
|
|
1851
1841
|
let targetDirRel = defaultDir;
|
|
1852
1842
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
1853
1843
|
targetDirRel = handleCancel(
|
|
@@ -1858,7 +1848,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
1858
1848
|
})
|
|
1859
1849
|
).trim();
|
|
1860
1850
|
}
|
|
1861
|
-
const targetDir = flags.dest ?
|
|
1851
|
+
const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
|
|
1862
1852
|
let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
|
|
1863
1853
|
let routePath = `/${normalizedName}`;
|
|
1864
1854
|
let includeDomain = true;
|
|
@@ -2100,8 +2090,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2100
2090
|
};
|
|
2101
2091
|
const files = generateFeature(options);
|
|
2102
2092
|
if (isRegistry) {
|
|
2103
|
-
const registryPath =
|
|
2104
|
-
if (!
|
|
2093
|
+
const registryPath = path26.join(targetDir, "registry.json");
|
|
2094
|
+
if (!fs17.existsSync(registryPath)) {
|
|
2105
2095
|
const manifest = {
|
|
2106
2096
|
name: toKebabCase(normalizedName),
|
|
2107
2097
|
type: "feature",
|
|
@@ -2127,7 +2117,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2127
2117
|
printGeneratedFiles(files, options.dryRun);
|
|
2128
2118
|
p19.outro(
|
|
2129
2119
|
pc13.green(
|
|
2130
|
-
`\u2713 Feature '${normalizedName}' initialized in ${
|
|
2120
|
+
`\u2713 Feature '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2131
2121
|
)
|
|
2132
2122
|
);
|
|
2133
2123
|
}
|
|
@@ -2138,7 +2128,7 @@ function generateShared(options) {
|
|
|
2138
2128
|
const pascalName = toPascalCase(rawName);
|
|
2139
2129
|
const camelName = toCamelCase(rawName);
|
|
2140
2130
|
const kebabName = toKebabCase(rawName);
|
|
2141
|
-
const targetDir = options.targetDir ||
|
|
2131
|
+
const targetDir = options.targetDir || path26.join(resolveSharedDir(projectRoot), kebabName);
|
|
2142
2132
|
const fileDefs = [];
|
|
2143
2133
|
const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
|
|
2144
2134
|
import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
|
|
@@ -2150,7 +2140,7 @@ export class ${pascalName} {
|
|
|
2150
2140
|
}
|
|
2151
2141
|
`;
|
|
2152
2142
|
fileDefs.push({
|
|
2153
|
-
absolutePath:
|
|
2143
|
+
absolutePath: path26.join(targetDir, `${pascalName}.ts`),
|
|
2154
2144
|
content: facadeContent
|
|
2155
2145
|
});
|
|
2156
2146
|
const portContent = `export abstract class I${pascalName}Engine {
|
|
@@ -2158,7 +2148,7 @@ export class ${pascalName} {
|
|
|
2158
2148
|
}
|
|
2159
2149
|
`;
|
|
2160
2150
|
fileDefs.push({
|
|
2161
|
-
absolutePath:
|
|
2151
|
+
absolutePath: path26.join(targetDir, "ports", `I${pascalName}Engine.ts`),
|
|
2162
2152
|
content: portContent
|
|
2163
2153
|
});
|
|
2164
2154
|
const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
|
|
@@ -2170,7 +2160,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2170
2160
|
}
|
|
2171
2161
|
`;
|
|
2172
2162
|
fileDefs.push({
|
|
2173
|
-
absolutePath:
|
|
2163
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
|
|
2174
2164
|
content: concreteContent
|
|
2175
2165
|
});
|
|
2176
2166
|
if (options.includeStub !== false) {
|
|
@@ -2183,7 +2173,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
|
|
|
2183
2173
|
}
|
|
2184
2174
|
`;
|
|
2185
2175
|
fileDefs.push({
|
|
2186
|
-
absolutePath:
|
|
2176
|
+
absolutePath: path26.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
|
|
2187
2177
|
content: stubContent
|
|
2188
2178
|
});
|
|
2189
2179
|
}
|
|
@@ -2198,7 +2188,7 @@ export const diProvider = (c: Container): void => {
|
|
|
2198
2188
|
export default diProvider;
|
|
2199
2189
|
`;
|
|
2200
2190
|
fileDefs.push({
|
|
2201
|
-
absolutePath:
|
|
2191
|
+
absolutePath: path26.join(targetDir, "diProvider.ts"),
|
|
2202
2192
|
content: providerContent
|
|
2203
2193
|
});
|
|
2204
2194
|
if (options.isRegistry) {
|
|
@@ -2215,7 +2205,7 @@ export default diProvider;
|
|
|
2215
2205
|
files: []
|
|
2216
2206
|
};
|
|
2217
2207
|
fileDefs.push({
|
|
2218
|
-
absolutePath:
|
|
2208
|
+
absolutePath: path26.join(targetDir, "registry.json"),
|
|
2219
2209
|
content: JSON.stringify(registryManifest, null, 2) + "\n"
|
|
2220
2210
|
});
|
|
2221
2211
|
}
|
|
@@ -2252,7 +2242,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2252
2242
|
}
|
|
2253
2243
|
const normalizedName = toKebabCase(name);
|
|
2254
2244
|
const baseSharedDir = resolveSharedDir(projectRoot, flags);
|
|
2255
|
-
const defaultDir =
|
|
2245
|
+
const defaultDir = path26.relative(projectRoot, path26.join(baseSharedDir, normalizedName));
|
|
2256
2246
|
let targetDirRel = defaultDir;
|
|
2257
2247
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2258
2248
|
targetDirRel = handleCancel(
|
|
@@ -2263,7 +2253,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2263
2253
|
})
|
|
2264
2254
|
).trim();
|
|
2265
2255
|
}
|
|
2266
|
-
const targetDir = flags.dest ?
|
|
2256
|
+
const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
|
|
2267
2257
|
let description = flags.description || "";
|
|
2268
2258
|
let includeStub = true;
|
|
2269
2259
|
if (!flags.yes) {
|
|
@@ -2297,7 +2287,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2297
2287
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2298
2288
|
p19.outro(
|
|
2299
2289
|
pc13.green(
|
|
2300
|
-
`\u2713 Shared module '${normalizedName}' initialized in ${
|
|
2290
|
+
`\u2713 Shared module '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2301
2291
|
)
|
|
2302
2292
|
);
|
|
2303
2293
|
}
|
|
@@ -2724,7 +2714,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2724
2714
|
const infraFile = generateInfrastructure(options);
|
|
2725
2715
|
files.push(infraFile);
|
|
2726
2716
|
if (registerInProvider && scope === "feature" && feature && implementsInterface) {
|
|
2727
|
-
const featureDir =
|
|
2717
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2728
2718
|
const providerFile = updateOrGenerateDiProvider(
|
|
2729
2719
|
featureDir,
|
|
2730
2720
|
feature,
|
|
@@ -2886,7 +2876,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
2886
2876
|
files.push(stubFile);
|
|
2887
2877
|
}
|
|
2888
2878
|
if (registerInProvider && generateStub) {
|
|
2889
|
-
const featureDir =
|
|
2879
|
+
const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
|
|
2890
2880
|
const providerFile = updateOrGenerateDiProvider(
|
|
2891
2881
|
featureDir,
|
|
2892
2882
|
feature,
|
|
@@ -3074,21 +3064,21 @@ function generateInjectable(options) {
|
|
|
3074
3064
|
const className = toPascalCase(options.name);
|
|
3075
3065
|
let targetDir = "";
|
|
3076
3066
|
if (options.customPath) {
|
|
3077
|
-
targetDir =
|
|
3067
|
+
targetDir = path26.isAbsolute(options.customPath) ? options.customPath : path26.join(projectRoot, options.customPath);
|
|
3078
3068
|
} else if (options.location === "feature-service" && options.feature) {
|
|
3079
|
-
targetDir =
|
|
3069
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "services");
|
|
3080
3070
|
} else if (options.location === "feature-transformer" && options.feature) {
|
|
3081
|
-
targetDir =
|
|
3071
|
+
targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
|
|
3082
3072
|
} else if (options.location === "module") {
|
|
3083
|
-
targetDir =
|
|
3073
|
+
targetDir = path26.join(projectRoot, "modules", className);
|
|
3084
3074
|
} else {
|
|
3085
|
-
targetDir = options.feature ?
|
|
3075
|
+
targetDir = options.feature ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26.join(projectRoot, "shared", className.toLowerCase());
|
|
3086
3076
|
}
|
|
3087
|
-
const filePath =
|
|
3077
|
+
const filePath = path26.join(targetDir, `${className}.ts`);
|
|
3088
3078
|
let ifaceName;
|
|
3089
3079
|
if (options.generateInterface) {
|
|
3090
3080
|
ifaceName = `I${className}`;
|
|
3091
|
-
const ifacePath =
|
|
3081
|
+
const ifacePath = path26.join(targetDir, `${ifaceName}.ts`);
|
|
3092
3082
|
const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
|
|
3093
3083
|
files.push(
|
|
3094
3084
|
writeFileSafely(ifacePath, ifaceContent, {
|
|
@@ -3217,7 +3207,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3217
3207
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3218
3208
|
const s = p19.spinner();
|
|
3219
3209
|
s.start("Scanning for .example environment files...");
|
|
3220
|
-
const files =
|
|
3210
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3221
3211
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3222
3212
|
if (exampleFiles.length === 0) {
|
|
3223
3213
|
s.stop("No .example files found.");
|
|
@@ -3227,11 +3217,11 @@ async function runEnvInit(flags = {}) {
|
|
|
3227
3217
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
3228
3218
|
const generatedFiles = [];
|
|
3229
3219
|
for (const exampleFile of exampleFiles) {
|
|
3230
|
-
const sourcePath =
|
|
3220
|
+
const sourcePath = path26.join(projectRoot, exampleFile);
|
|
3231
3221
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3232
|
-
const targetPath =
|
|
3222
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3233
3223
|
let overwrite = flags.overwrite;
|
|
3234
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3224
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
|
|
3235
3225
|
overwrite = await p19.confirm({
|
|
3236
3226
|
message: `${targetFile} already exists. Overwrite?`,
|
|
3237
3227
|
initialValue: false
|
|
@@ -3241,7 +3231,7 @@ async function runEnvInit(flags = {}) {
|
|
|
3241
3231
|
process.exit(0);
|
|
3242
3232
|
}
|
|
3243
3233
|
}
|
|
3244
|
-
const content =
|
|
3234
|
+
const content = fs17.readFileSync(sourcePath, "utf-8");
|
|
3245
3235
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3246
3236
|
projectRoot,
|
|
3247
3237
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -3259,7 +3249,7 @@ var EnvEditor = class {
|
|
|
3259
3249
|
filePath;
|
|
3260
3250
|
constructor(filePath) {
|
|
3261
3251
|
this.filePath = filePath;
|
|
3262
|
-
this.lines =
|
|
3252
|
+
this.lines = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3263
3253
|
}
|
|
3264
3254
|
getLines() {
|
|
3265
3255
|
return [...this.lines];
|
|
@@ -3323,18 +3313,18 @@ var EnvEditor = class {
|
|
|
3323
3313
|
});
|
|
3324
3314
|
}
|
|
3325
3315
|
save() {
|
|
3326
|
-
const dir =
|
|
3327
|
-
if (!
|
|
3328
|
-
|
|
3316
|
+
const dir = path26.dirname(this.filePath);
|
|
3317
|
+
if (!fs17.existsSync(dir)) {
|
|
3318
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
3329
3319
|
}
|
|
3330
|
-
|
|
3320
|
+
fs17.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3331
3321
|
}
|
|
3332
3322
|
};
|
|
3333
3323
|
function discoverEnvironments(projectRoot) {
|
|
3334
|
-
if (!
|
|
3324
|
+
if (!fs17.existsSync(projectRoot)) {
|
|
3335
3325
|
return ["local", "dev", "prod"];
|
|
3336
3326
|
}
|
|
3337
|
-
const files =
|
|
3327
|
+
const files = fs17.readdirSync(projectRoot);
|
|
3338
3328
|
const envs = /* @__PURE__ */ new Set();
|
|
3339
3329
|
for (const file of files) {
|
|
3340
3330
|
if (file.startsWith(".env")) {
|
|
@@ -3359,7 +3349,7 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3359
3349
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3360
3350
|
const s = p19.spinner();
|
|
3361
3351
|
s.start("Scanning for local environment files...");
|
|
3362
|
-
const files =
|
|
3352
|
+
const files = fs17.readdirSync(projectRoot, { withFileTypes: true });
|
|
3363
3353
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3364
3354
|
if (envFiles.length === 0) {
|
|
3365
3355
|
s.stop("No local .env files found.");
|
|
@@ -3369,11 +3359,11 @@ async function runEnvInitExamples(flags = {}) {
|
|
|
3369
3359
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
3370
3360
|
const generatedFiles = [];
|
|
3371
3361
|
for (const envFile of envFiles) {
|
|
3372
|
-
const sourcePath =
|
|
3362
|
+
const sourcePath = path26.join(projectRoot, envFile);
|
|
3373
3363
|
const targetFile = `${envFile}.example`;
|
|
3374
|
-
const targetPath =
|
|
3364
|
+
const targetPath = path26.join(projectRoot, targetFile);
|
|
3375
3365
|
let overwrite = flags.overwrite;
|
|
3376
|
-
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY &&
|
|
3366
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
|
|
3377
3367
|
overwrite = await p19.confirm({
|
|
3378
3368
|
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3379
3369
|
initialValue: true
|
|
@@ -3406,12 +3396,12 @@ function isInteractive(flags) {
|
|
|
3406
3396
|
function getAllExistingKeys(projectRoot, envs) {
|
|
3407
3397
|
const keys = /* @__PURE__ */ new Set();
|
|
3408
3398
|
for (const env of envs) {
|
|
3409
|
-
const localPath =
|
|
3410
|
-
const examplePath =
|
|
3411
|
-
if (
|
|
3399
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3400
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3401
|
+
if (fs17.existsSync(localPath)) {
|
|
3412
3402
|
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3413
3403
|
}
|
|
3414
|
-
if (
|
|
3404
|
+
if (fs17.existsSync(examplePath)) {
|
|
3415
3405
|
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3416
3406
|
}
|
|
3417
3407
|
}
|
|
@@ -3483,8 +3473,8 @@ async function runEnvAdd(key, value, flags = {}) {
|
|
|
3483
3473
|
})
|
|
3484
3474
|
);
|
|
3485
3475
|
}
|
|
3486
|
-
const localPath =
|
|
3487
|
-
const examplePath =
|
|
3476
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3477
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3488
3478
|
if (flags.dryRun) {
|
|
3489
3479
|
p19.log.info(
|
|
3490
3480
|
pc13.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
@@ -3531,8 +3521,8 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3531
3521
|
);
|
|
3532
3522
|
}
|
|
3533
3523
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3534
|
-
const localPath =
|
|
3535
|
-
return
|
|
3524
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3525
|
+
return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3536
3526
|
});
|
|
3537
3527
|
let targetEnvs = [];
|
|
3538
3528
|
if (flags.env) {
|
|
@@ -3558,7 +3548,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3558
3548
|
return { key: targetKey, environments: [] };
|
|
3559
3549
|
}
|
|
3560
3550
|
for (const env of targetEnvs) {
|
|
3561
|
-
const localPath =
|
|
3551
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3562
3552
|
const localEditor = new EnvEditor(localPath);
|
|
3563
3553
|
const currentVal = localEditor.getValue(targetKey);
|
|
3564
3554
|
let newVal = value;
|
|
@@ -3588,7 +3578,7 @@ async function runEnvUpdate(key, value, flags = {}) {
|
|
|
3588
3578
|
} else {
|
|
3589
3579
|
localEditor.set(targetKey, newVal);
|
|
3590
3580
|
localEditor.save();
|
|
3591
|
-
const examplePath =
|
|
3581
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3592
3582
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3593
3583
|
if (!exampleEditor.has(targetKey)) {
|
|
3594
3584
|
exampleEditor.set(targetKey, "");
|
|
@@ -3624,9 +3614,9 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3624
3614
|
);
|
|
3625
3615
|
}
|
|
3626
3616
|
const envsWithKey = allEnvs.filter((env) => {
|
|
3627
|
-
const localPath =
|
|
3628
|
-
const examplePath =
|
|
3629
|
-
return
|
|
3617
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3618
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3619
|
+
return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs17.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
|
|
3630
3620
|
});
|
|
3631
3621
|
let targetEnvs = [];
|
|
3632
3622
|
if (flags.env) {
|
|
@@ -3664,19 +3654,19 @@ async function runEnvDelete(key, flags = {}) {
|
|
|
3664
3654
|
}
|
|
3665
3655
|
}
|
|
3666
3656
|
for (const env of targetEnvs) {
|
|
3667
|
-
const localPath =
|
|
3668
|
-
const examplePath =
|
|
3657
|
+
const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
|
|
3658
|
+
const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
|
|
3669
3659
|
if (flags.dryRun) {
|
|
3670
3660
|
p19.log.info(
|
|
3671
3661
|
pc13.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3672
3662
|
);
|
|
3673
3663
|
} else {
|
|
3674
|
-
if (
|
|
3664
|
+
if (fs17.existsSync(localPath)) {
|
|
3675
3665
|
const localEditor = new EnvEditor(localPath);
|
|
3676
3666
|
localEditor.delete(targetKey);
|
|
3677
3667
|
localEditor.save();
|
|
3678
3668
|
}
|
|
3679
|
-
if (
|
|
3669
|
+
if (fs17.existsSync(examplePath)) {
|
|
3680
3670
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3681
3671
|
exampleEditor.delete(targetKey);
|
|
3682
3672
|
exampleEditor.save();
|
|
@@ -3701,10 +3691,10 @@ async function runEnvAudit(flags = {}) {
|
|
|
3701
3691
|
for (const env of envs) {
|
|
3702
3692
|
const localFile = resolveEnvFilename(env, false);
|
|
3703
3693
|
const exampleFile = resolveEnvFilename(env, true);
|
|
3704
|
-
const localPath =
|
|
3705
|
-
const examplePath =
|
|
3706
|
-
const localExists =
|
|
3707
|
-
const exampleExists =
|
|
3694
|
+
const localPath = path26.join(projectRoot, localFile);
|
|
3695
|
+
const examplePath = path26.join(projectRoot, exampleFile);
|
|
3696
|
+
const localExists = fs17.existsSync(localPath);
|
|
3697
|
+
const exampleExists = fs17.existsSync(examplePath);
|
|
3708
3698
|
const localEditor = new EnvEditor(localPath);
|
|
3709
3699
|
const exampleEditor = new EnvEditor(examplePath);
|
|
3710
3700
|
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
@@ -4031,25 +4021,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
|
|
|
4031
4021
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
4032
4022
|
}
|
|
4033
4023
|
if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
|
|
4034
|
-
return
|
|
4024
|
+
return path26.resolve(baseDir, trimmed);
|
|
4035
4025
|
}
|
|
4036
4026
|
return trimmed;
|
|
4037
4027
|
}
|
|
4038
4028
|
function findLocalRegistry(startDir = process.cwd()) {
|
|
4039
|
-
let current =
|
|
4029
|
+
let current = path26.resolve(startDir);
|
|
4040
4030
|
while (true) {
|
|
4041
|
-
const candidate =
|
|
4042
|
-
if (
|
|
4031
|
+
const candidate = path26.join(current, "mason-registry");
|
|
4032
|
+
if (fs17.existsSync(candidate) && fs17.existsSync(path26.join(candidate, "index.json"))) {
|
|
4043
4033
|
return candidate;
|
|
4044
4034
|
}
|
|
4045
|
-
const parent =
|
|
4035
|
+
const parent = path26.dirname(current);
|
|
4046
4036
|
if (parent === current) break;
|
|
4047
4037
|
current = parent;
|
|
4048
4038
|
}
|
|
4049
4039
|
return null;
|
|
4050
4040
|
}
|
|
4051
4041
|
function getRegistryBase(projectRoot, override) {
|
|
4052
|
-
const rootDir = projectRoot ?
|
|
4042
|
+
const rootDir = projectRoot ? path26.resolve(projectRoot) : process.cwd();
|
|
4053
4043
|
if (override && override.trim()) {
|
|
4054
4044
|
return normalizeRegistryUrl(override.trim(), rootDir);
|
|
4055
4045
|
}
|
|
@@ -4057,30 +4047,30 @@ function getRegistryBase(projectRoot, override) {
|
|
|
4057
4047
|
return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
|
|
4058
4048
|
}
|
|
4059
4049
|
const configLocations = [
|
|
4060
|
-
|
|
4061
|
-
|
|
4050
|
+
path26.join(rootDir, "mason.config.json"),
|
|
4051
|
+
path26.join(process.cwd(), "mason.config.json")
|
|
4062
4052
|
];
|
|
4063
4053
|
for (const configPath of configLocations) {
|
|
4064
|
-
if (
|
|
4054
|
+
if (fs17.existsSync(configPath)) {
|
|
4065
4055
|
try {
|
|
4066
|
-
const config = JSON.parse(
|
|
4056
|
+
const config = JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
4067
4057
|
if (config.registry && typeof config.registry === "string") {
|
|
4068
|
-
return normalizeRegistryUrl(config.registry.trim(),
|
|
4058
|
+
return normalizeRegistryUrl(config.registry.trim(), path26.dirname(configPath));
|
|
4069
4059
|
}
|
|
4070
4060
|
} catch {
|
|
4071
4061
|
}
|
|
4072
4062
|
}
|
|
4073
4063
|
}
|
|
4074
4064
|
const pkgLocations = [
|
|
4075
|
-
|
|
4076
|
-
|
|
4065
|
+
path26.join(rootDir, "package.json"),
|
|
4066
|
+
path26.join(process.cwd(), "package.json")
|
|
4077
4067
|
];
|
|
4078
4068
|
for (const pkgPath of pkgLocations) {
|
|
4079
|
-
if (
|
|
4069
|
+
if (fs17.existsSync(pkgPath)) {
|
|
4080
4070
|
try {
|
|
4081
|
-
const pkg = JSON.parse(
|
|
4071
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
|
|
4082
4072
|
if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
|
|
4083
|
-
return normalizeRegistryUrl(pkg.mason.registry.trim(),
|
|
4073
|
+
return normalizeRegistryUrl(pkg.mason.registry.trim(), path26.dirname(pkgPath));
|
|
4084
4074
|
}
|
|
4085
4075
|
} catch {
|
|
4086
4076
|
}
|
|
@@ -4128,16 +4118,16 @@ async function fetchRegistryFile(registryBase, relPath) {
|
|
|
4128
4118
|
if (baseDir.startsWith("file://")) {
|
|
4129
4119
|
baseDir = fileURLToPath(baseDir);
|
|
4130
4120
|
}
|
|
4131
|
-
let filePath =
|
|
4132
|
-
if (!
|
|
4133
|
-
const inSrcPath =
|
|
4134
|
-
if (
|
|
4121
|
+
let filePath = path26.resolve(baseDir, cleanRelPath);
|
|
4122
|
+
if (!fs17.existsSync(filePath)) {
|
|
4123
|
+
const inSrcPath = path26.resolve(baseDir, "src", cleanRelPath);
|
|
4124
|
+
if (fs17.existsSync(inSrcPath)) {
|
|
4135
4125
|
filePath = inSrcPath;
|
|
4136
4126
|
} else {
|
|
4137
4127
|
throw new Error(`Registry file not found at ${filePath}`);
|
|
4138
4128
|
}
|
|
4139
4129
|
}
|
|
4140
|
-
return
|
|
4130
|
+
return fs17.promises.readFile(filePath, "utf-8");
|
|
4141
4131
|
}
|
|
4142
4132
|
async function fetchRegistryIndex(registryBase) {
|
|
4143
4133
|
const content = await fetchRegistryFile(registryBase, "index.json");
|
|
@@ -4199,19 +4189,19 @@ function findClosestMatch(target, candidates) {
|
|
|
4199
4189
|
return null;
|
|
4200
4190
|
}
|
|
4201
4191
|
function detectPackageManager(projectRoot) {
|
|
4202
|
-
if (
|
|
4203
|
-
if (
|
|
4204
|
-
if (
|
|
4192
|
+
if (fs17.existsSync(path26.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
4193
|
+
if (fs17.existsSync(path26.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
4194
|
+
if (fs17.existsSync(path26.join(projectRoot, "bun.lockb")) || fs17.existsSync(path26.join(projectRoot, "bun.lock"))) return "bun";
|
|
4205
4195
|
return "npm";
|
|
4206
4196
|
}
|
|
4207
4197
|
function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
4208
4198
|
if (!requiredPackages || requiredPackages.length === 0) return [];
|
|
4209
|
-
const pkgJsonPath =
|
|
4210
|
-
if (!
|
|
4199
|
+
const pkgJsonPath = path26.join(projectRoot, "package.json");
|
|
4200
|
+
if (!fs17.existsSync(pkgJsonPath)) {
|
|
4211
4201
|
return requiredPackages;
|
|
4212
4202
|
}
|
|
4213
4203
|
try {
|
|
4214
|
-
const pkg = JSON.parse(
|
|
4204
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgJsonPath, "utf-8"));
|
|
4215
4205
|
const installed = /* @__PURE__ */ new Set([
|
|
4216
4206
|
...Object.keys(pkg.dependencies || {}),
|
|
4217
4207
|
...Object.keys(pkg.devDependencies || {}),
|
|
@@ -4250,12 +4240,12 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
4250
4240
|
});
|
|
4251
4241
|
}
|
|
4252
4242
|
function getAllFiles(dir, baseDir = dir) {
|
|
4253
|
-
if (!
|
|
4254
|
-
const entries =
|
|
4243
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4244
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4255
4245
|
const files = [];
|
|
4256
4246
|
for (const entry of entries) {
|
|
4257
|
-
const fullPath =
|
|
4258
|
-
const relPath =
|
|
4247
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4248
|
+
const relPath = path26.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
4259
4249
|
if (entry.isDirectory()) {
|
|
4260
4250
|
if (entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4261
4251
|
continue;
|
|
@@ -4271,13 +4261,13 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
4271
4261
|
return files.sort();
|
|
4272
4262
|
}
|
|
4273
4263
|
function computeDirectoryHash(dirPath, files) {
|
|
4274
|
-
if (!
|
|
4264
|
+
if (!fs17.existsSync(dirPath)) return "";
|
|
4275
4265
|
const hash = createHash("sha256");
|
|
4276
4266
|
const fileList = (getAllFiles(dirPath)).sort();
|
|
4277
4267
|
for (const relativePath of fileList) {
|
|
4278
|
-
const absolutePath =
|
|
4279
|
-
if (!
|
|
4280
|
-
const content =
|
|
4268
|
+
const absolutePath = path26.join(dirPath, relativePath);
|
|
4269
|
+
if (!fs17.existsSync(absolutePath)) continue;
|
|
4270
|
+
const content = fs17.readFileSync(absolutePath, "utf8");
|
|
4281
4271
|
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
4282
4272
|
hash.update(relativePath);
|
|
4283
4273
|
hash.update(normalizedContent);
|
|
@@ -4315,17 +4305,17 @@ function getModuleBaseDir(projectRoot, modType) {
|
|
|
4315
4305
|
return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4316
4306
|
}
|
|
4317
4307
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4318
|
-
return
|
|
4308
|
+
return path26.join(projectRoot, "src", folder);
|
|
4319
4309
|
}
|
|
4320
4310
|
function getExistingModuleDir(projectRoot, modType, modName) {
|
|
4321
4311
|
const folder = modType === "shared" ? "shared" : "features";
|
|
4322
4312
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4323
|
-
const inConfigured =
|
|
4324
|
-
if (
|
|
4325
|
-
const inSrc =
|
|
4326
|
-
if (
|
|
4327
|
-
const inRoot =
|
|
4328
|
-
if (
|
|
4313
|
+
const inConfigured = path26.join(configuredDir, modName);
|
|
4314
|
+
if (fs17.existsSync(inConfigured)) return inConfigured;
|
|
4315
|
+
const inSrc = path26.join(projectRoot, "src", folder, modName);
|
|
4316
|
+
if (fs17.existsSync(inSrc)) return inSrc;
|
|
4317
|
+
const inRoot = path26.join(projectRoot, folder, modName);
|
|
4318
|
+
if (fs17.existsSync(inRoot)) return inRoot;
|
|
4329
4319
|
return null;
|
|
4330
4320
|
}
|
|
4331
4321
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
@@ -4336,14 +4326,14 @@ function getAvailableLocalModules(projectRoot, modType) {
|
|
|
4336
4326
|
const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
|
|
4337
4327
|
const dirs = [
|
|
4338
4328
|
configuredDir,
|
|
4339
|
-
|
|
4340
|
-
|
|
4329
|
+
path26.join(projectRoot, "src", folder),
|
|
4330
|
+
path26.join(projectRoot, folder)
|
|
4341
4331
|
];
|
|
4342
4332
|
const set = /* @__PURE__ */ new Set();
|
|
4343
4333
|
for (const dir of dirs) {
|
|
4344
|
-
if (
|
|
4334
|
+
if (fs17.existsSync(dir)) {
|
|
4345
4335
|
try {
|
|
4346
|
-
const entries =
|
|
4336
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4347
4337
|
for (const e of entries) {
|
|
4348
4338
|
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
4349
4339
|
set.add(e.name);
|
|
@@ -4545,7 +4535,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4545
4535
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
4546
4536
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
4547
4537
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4548
|
-
const displayDest =
|
|
4538
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, programmaticAlias));
|
|
4549
4539
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4550
4540
|
resolutions.set(key, {
|
|
4551
4541
|
type: modType,
|
|
@@ -4733,7 +4723,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4733
4723
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4734
4724
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4735
4725
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4736
|
-
const displayDest =
|
|
4726
|
+
const displayDest = path26.relative(projectRoot, path26.join(baseDir, newName));
|
|
4737
4727
|
p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4738
4728
|
resolutions.set(key, {
|
|
4739
4729
|
type: modType,
|
|
@@ -4785,7 +4775,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4785
4775
|
continue;
|
|
4786
4776
|
}
|
|
4787
4777
|
const folder = res.type === "shared" ? "shared" : "features";
|
|
4788
|
-
const targetDir =
|
|
4778
|
+
const targetDir = path26.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4789
4779
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4790
4780
|
if (key !== rootKey) {
|
|
4791
4781
|
p19.log.step(
|
|
@@ -4795,30 +4785,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4795
4785
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4796
4786
|
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4797
4787
|
if (existingDir && existingDir !== targetDir) {
|
|
4798
|
-
|
|
4788
|
+
fs17.rmSync(existingDir, { recursive: true, force: true });
|
|
4799
4789
|
}
|
|
4800
|
-
if (
|
|
4801
|
-
|
|
4790
|
+
if (fs17.existsSync(targetDir)) {
|
|
4791
|
+
fs17.rmSync(targetDir, { recursive: true, force: true });
|
|
4802
4792
|
}
|
|
4803
4793
|
}
|
|
4804
4794
|
for (const file of res.manifest.files || []) {
|
|
4805
4795
|
if (file === "registry.json") continue;
|
|
4806
4796
|
if (!includeTests && isTestFile(file)) continue;
|
|
4807
4797
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4808
|
-
const localFilePath =
|
|
4809
|
-
if (
|
|
4798
|
+
const localFilePath = path26.join(targetDir, file);
|
|
4799
|
+
if (fs17.existsSync(localFilePath) && !isOverwrite) {
|
|
4810
4800
|
p19.log.warn(
|
|
4811
|
-
` \u21B3 Skipping existing file: ${pc13.dim(
|
|
4801
|
+
` \u21B3 Skipping existing file: ${pc13.dim(path26.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4812
4802
|
);
|
|
4813
4803
|
continue;
|
|
4814
4804
|
}
|
|
4815
4805
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
4816
4806
|
content = rewriteImports(content, globalAliasMap);
|
|
4817
4807
|
if (!flags.dryRun) {
|
|
4818
|
-
|
|
4819
|
-
|
|
4808
|
+
fs17.mkdirSync(path26.dirname(localFilePath), { recursive: true });
|
|
4809
|
+
fs17.writeFileSync(localFilePath, content, "utf-8");
|
|
4820
4810
|
}
|
|
4821
|
-
const displayPath =
|
|
4811
|
+
const displayPath = path26.relative(projectRoot, localFilePath);
|
|
4822
4812
|
totalWrittenFiles.push(displayPath);
|
|
4823
4813
|
p19.log.message(` ${pc13.green("+")} ${displayPath}`);
|
|
4824
4814
|
}
|
|
@@ -4844,7 +4834,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4844
4834
|
} else {
|
|
4845
4835
|
const rootRes = resolutions.get(rootKey);
|
|
4846
4836
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4847
|
-
const displayTarget =
|
|
4837
|
+
const displayTarget = path26.relative(projectRoot, path26.join(baseDir, rootRes.targetName));
|
|
4848
4838
|
p19.log.success(
|
|
4849
4839
|
pc13.green(
|
|
4850
4840
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
@@ -4863,19 +4853,19 @@ var DependencyRuleError = class extends Error {
|
|
|
4863
4853
|
}
|
|
4864
4854
|
};
|
|
4865
4855
|
function isTestFile2(filePath, projectRoot) {
|
|
4866
|
-
const norm = (projectRoot ?
|
|
4867
|
-
const base =
|
|
4856
|
+
const norm = (projectRoot ? path26.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
|
|
4857
|
+
const base = path26.basename(norm);
|
|
4868
4858
|
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
4859
|
}
|
|
4870
4860
|
function scanDirFiles(dir, projectRoot) {
|
|
4871
|
-
if (!
|
|
4872
|
-
const entries =
|
|
4861
|
+
if (!fs17.existsSync(dir)) return [];
|
|
4862
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4873
4863
|
const files = [];
|
|
4874
4864
|
for (const entry of entries) {
|
|
4875
4865
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
|
|
4876
4866
|
continue;
|
|
4877
4867
|
}
|
|
4878
|
-
const fullPath =
|
|
4868
|
+
const fullPath = path26.join(dir, entry.name);
|
|
4879
4869
|
if (entry.isDirectory()) {
|
|
4880
4870
|
files.push(...scanDirFiles(fullPath, projectRoot));
|
|
4881
4871
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
|
|
@@ -4884,42 +4874,43 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4884
4874
|
}
|
|
4885
4875
|
return files;
|
|
4886
4876
|
}
|
|
4887
|
-
function
|
|
4888
|
-
const
|
|
4889
|
-
const
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
if (
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4877
|
+
function resolveLintEnvironment(projectRoot) {
|
|
4878
|
+
const config = loadMasonConfig(projectRoot);
|
|
4879
|
+
const resolveDir = (configured, defaultSrc, defaultRoot) => {
|
|
4880
|
+
if (configured !== void 0) {
|
|
4881
|
+
return path26.resolve(projectRoot, configured);
|
|
4882
|
+
}
|
|
4883
|
+
const srcPath = path26.join(projectRoot, defaultSrc);
|
|
4884
|
+
if (fs17.existsSync(srcPath)) return srcPath;
|
|
4885
|
+
const rootPath = path26.join(projectRoot, defaultRoot);
|
|
4886
|
+
if (fs17.existsSync(rootPath)) return rootPath;
|
|
4887
|
+
return srcPath;
|
|
4888
|
+
};
|
|
4889
|
+
const featuresBase = resolveDir(config?.paths?.features, "src/features", "features");
|
|
4890
|
+
const sharedBase = resolveDir(config?.paths?.shared, "src/shared", "shared");
|
|
4891
|
+
const errorsBase = resolveDir(config?.paths?.errors, "src/errors", "errors");
|
|
4892
|
+
const defaultBaseErrorRelative = config?.paths?.errors ? path26.join(config.paths.errors, "AppError.ts") : fs17.existsSync(path26.join(projectRoot, "src/errors")) ? "src/errors/AppError.ts" : fs17.existsSync(path26.join(projectRoot, "errors")) ? "errors/AppError.ts" : "src/errors/AppError.ts";
|
|
4893
|
+
const baseErrorAbs = path26.resolve(
|
|
4894
|
+
projectRoot,
|
|
4895
|
+
config?.paths?.baseError || defaultBaseErrorRelative
|
|
4896
|
+
);
|
|
4897
|
+
return { featuresBase, sharedBase, errorsBase, baseErrorAbs };
|
|
4898
|
+
}
|
|
4899
|
+
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4900
|
+
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4901
|
+
const env = resolveLintEnvironment(featuresBaseAbs);
|
|
4902
|
+
featuresBaseAbs = env.featuresBase;
|
|
4903
|
+
sharedBaseAbs = env.sharedBase;
|
|
4904
|
+
errorsBaseAbs = env.errorsBase;
|
|
4905
|
+
}
|
|
4906
|
+
const normFullPath = path26.resolve(fullPathAbs);
|
|
4907
|
+
const normErrorsBase = path26.resolve(errorsBaseAbs);
|
|
4908
|
+
const normSharedBase = path26.resolve(sharedBaseAbs);
|
|
4909
|
+
const normFeaturesBase = path26.resolve(featuresBaseAbs);
|
|
4910
|
+
const isUnder = (base, target) => {
|
|
4911
|
+
return target === base || target.startsWith(base + path26.sep);
|
|
4912
|
+
};
|
|
4913
|
+
if (isUnder(normErrorsBase, normFullPath)) {
|
|
4923
4914
|
return {
|
|
4924
4915
|
moduleType: "error",
|
|
4925
4916
|
moduleName: "global-errors",
|
|
@@ -4927,46 +4918,154 @@ function classifyFile(fullPath, projectRoot) {
|
|
|
4927
4918
|
layer: "domain"
|
|
4928
4919
|
};
|
|
4929
4920
|
}
|
|
4921
|
+
if (isUnder(normSharedBase, normFullPath)) {
|
|
4922
|
+
const rel = path26.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4923
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4924
|
+
if (parts.length >= 1) {
|
|
4925
|
+
const moduleName = parts[0];
|
|
4926
|
+
const subfolder = parts[1];
|
|
4927
|
+
let layer = "root";
|
|
4928
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4929
|
+
layer = "domain";
|
|
4930
|
+
} else if (subfolder === "ports") {
|
|
4931
|
+
layer = "ports";
|
|
4932
|
+
} else if (subfolder === "infrastructure") {
|
|
4933
|
+
layer = "infrastructure";
|
|
4934
|
+
}
|
|
4935
|
+
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
if (isUnder(normFeaturesBase, normFullPath)) {
|
|
4939
|
+
const rel = path26.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4940
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4941
|
+
if (parts.length >= 2) {
|
|
4942
|
+
const moduleName = parts[0];
|
|
4943
|
+
const subfolder = parts[1];
|
|
4944
|
+
let layer;
|
|
4945
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4946
|
+
layer = "domain";
|
|
4947
|
+
} else if (subfolder === "useCases" || subfolder === "usecases") {
|
|
4948
|
+
layer = "useCases";
|
|
4949
|
+
} else if (subfolder === "services") {
|
|
4950
|
+
layer = "services";
|
|
4951
|
+
} else if (subfolder === "infrastructure") {
|
|
4952
|
+
layer = "infrastructure";
|
|
4953
|
+
} else if (subfolder.startsWith("px") || subfolder === "presentation" || subfolder === "handlers" || subfolder === "transformers") {
|
|
4954
|
+
layer = "presentation";
|
|
4955
|
+
}
|
|
4956
|
+
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4930
4959
|
return {};
|
|
4931
4960
|
}
|
|
4932
|
-
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
4961
|
+
function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4933
4962
|
if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
|
|
4934
4963
|
return { isExternal: true, isDiFramework: true };
|
|
4935
4964
|
}
|
|
4936
|
-
if (specifier.startsWith("node:") || [
|
|
4965
|
+
if (specifier.startsWith("node:") || [
|
|
4966
|
+
"crypto",
|
|
4967
|
+
"fs",
|
|
4968
|
+
"path",
|
|
4969
|
+
"http",
|
|
4970
|
+
"https",
|
|
4971
|
+
"url",
|
|
4972
|
+
"os",
|
|
4973
|
+
"child_process",
|
|
4974
|
+
"stream",
|
|
4975
|
+
"express",
|
|
4976
|
+
"cors",
|
|
4977
|
+
"dotenv"
|
|
4978
|
+
].includes(specifier)) {
|
|
4937
4979
|
return { isExternal: true, isDiFramework: false };
|
|
4938
4980
|
}
|
|
4939
|
-
|
|
4981
|
+
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
4982
|
+
const env = resolveLintEnvironment(projectRoot);
|
|
4983
|
+
featuresBaseAbs = featuresBaseAbs || env.featuresBase;
|
|
4984
|
+
sharedBaseAbs = sharedBaseAbs || env.sharedBase;
|
|
4985
|
+
errorsBaseAbs = errorsBaseAbs || env.errorsBase;
|
|
4986
|
+
}
|
|
4987
|
+
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
4988
|
+
let targetAbs = null;
|
|
4940
4989
|
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
|
-
|
|
4990
|
+
targetAbs = path26.resolve(path26.dirname(sourceFileAbs), specifier);
|
|
4991
|
+
} else {
|
|
4992
|
+
const featRel = path26.relative(projectRoot, featuresBaseAbs).replace(/\\/g, "/");
|
|
4993
|
+
const sharedRel = path26.relative(projectRoot, sharedBaseAbs).replace(/\\/g, "/");
|
|
4994
|
+
const errorsRel = path26.relative(projectRoot, errorsBaseAbs).replace(/\\/g, "/");
|
|
4995
|
+
const featPrefixes = ["features"];
|
|
4996
|
+
if (featRel && featRel !== "." && !featRel.startsWith("..")) {
|
|
4997
|
+
featPrefixes.push(featRel);
|
|
4998
|
+
const b = path26.basename(featRel);
|
|
4999
|
+
if (!featPrefixes.includes(b)) featPrefixes.push(b);
|
|
5000
|
+
}
|
|
5001
|
+
const sharedPrefixes = ["shared"];
|
|
5002
|
+
if (sharedRel && sharedRel !== "." && !sharedRel.startsWith("..")) {
|
|
5003
|
+
sharedPrefixes.push(sharedRel);
|
|
5004
|
+
const b = path26.basename(sharedRel);
|
|
5005
|
+
if (!sharedPrefixes.includes(b)) sharedPrefixes.push(b);
|
|
5006
|
+
}
|
|
5007
|
+
const errorsPrefixes = ["errors"];
|
|
5008
|
+
if (errorsRel && errorsRel !== "." && !errorsRel.startsWith("..")) {
|
|
5009
|
+
errorsPrefixes.push(errorsRel);
|
|
5010
|
+
const b = path26.basename(errorsRel);
|
|
5011
|
+
if (!errorsPrefixes.includes(b)) errorsPrefixes.push(b);
|
|
5012
|
+
}
|
|
5013
|
+
for (const prefix of featPrefixes) {
|
|
5014
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5015
|
+
const m = specifier.match(aliasRegex);
|
|
5016
|
+
if (m && m[2]) {
|
|
5017
|
+
targetAbs = path26.resolve(featuresBaseAbs, m[2]);
|
|
5018
|
+
break;
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
if (!targetAbs) {
|
|
5022
|
+
for (const prefix of sharedPrefixes) {
|
|
5023
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
|
|
5024
|
+
const m = specifier.match(aliasRegex);
|
|
5025
|
+
if (m && m[2]) {
|
|
5026
|
+
targetAbs = path26.resolve(sharedBaseAbs, m[2]);
|
|
5027
|
+
break;
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
if (!targetAbs) {
|
|
5032
|
+
for (const prefix of errorsPrefixes) {
|
|
5033
|
+
const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}(?:\\/(.*))?$`);
|
|
5034
|
+
const m = specifier.match(aliasRegex);
|
|
5035
|
+
if (m) {
|
|
5036
|
+
targetAbs = path26.resolve(errorsBaseAbs, m[2] || "");
|
|
5037
|
+
break;
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
if (!targetAbs && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
|
|
5042
|
+
const sub = specifier.slice(2);
|
|
5043
|
+
const srcCandidate = path26.resolve(projectRoot, "src", sub);
|
|
5044
|
+
const rootCandidate = path26.resolve(projectRoot, sub);
|
|
5045
|
+
if (fs17.existsSync(srcCandidate)) {
|
|
5046
|
+
targetAbs = srcCandidate;
|
|
5047
|
+
} else if (fs17.existsSync(rootCandidate)) {
|
|
5048
|
+
targetAbs = rootCandidate;
|
|
5049
|
+
} else {
|
|
5050
|
+
targetAbs = srcCandidate;
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
if (!targetAbs) {
|
|
5054
|
+
const featCandidate = path26.resolve(featuresBaseAbs, specifier);
|
|
5055
|
+
if (fs17.existsSync(featCandidate) || fs17.existsSync(featCandidate + ".ts") || fs17.existsSync(featCandidate + ".js")) {
|
|
5056
|
+
targetAbs = featCandidate;
|
|
5057
|
+
}
|
|
5058
|
+
}
|
|
4966
5059
|
}
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
5060
|
+
if (!targetAbs) {
|
|
5061
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5062
|
+
}
|
|
5063
|
+
const normTarget = path26.resolve(targetAbs);
|
|
5064
|
+
const srcConfig = path26.resolve(projectRoot, "src/configs");
|
|
5065
|
+
const rootConfig = path26.resolve(projectRoot, "configs");
|
|
5066
|
+
const isTargetConfig = isGlobalConfig || normTarget === srcConfig || normTarget.startsWith(srcConfig + path26.sep) || normTarget === rootConfig || normTarget.startsWith(rootConfig + path26.sep);
|
|
5067
|
+
const classification = classifyFile(targetAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs);
|
|
5068
|
+
if (classification.moduleType === "error") {
|
|
4970
5069
|
return {
|
|
4971
5070
|
isExternal: false,
|
|
4972
5071
|
isDiFramework: false,
|
|
@@ -4974,33 +5073,19 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
4974
5073
|
isTargetError: true
|
|
4975
5074
|
};
|
|
4976
5075
|
}
|
|
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)$/, "");
|
|
5076
|
+
if (classification.moduleType === "feature") {
|
|
5077
|
+
const rel = path26.relative(featuresBaseAbs, targetAbs).replace(/\\/g, "/");
|
|
5078
|
+
const parts = rel.split("/").filter(Boolean);
|
|
5079
|
+
const targetSubfolder = parts[1];
|
|
5080
|
+
const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
4996
5081
|
const isTargetPort = /^I[A-Z]/.test(targetFileName);
|
|
4997
5082
|
const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
|
|
4998
|
-
const isTargetError = targetSubfolder === "errors" ||
|
|
5083
|
+
const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
|
|
4999
5084
|
return {
|
|
5000
5085
|
isExternal: false,
|
|
5001
5086
|
isDiFramework: false,
|
|
5002
|
-
targetFeature,
|
|
5003
|
-
targetLayer,
|
|
5087
|
+
targetFeature: classification.moduleName,
|
|
5088
|
+
targetLayer: classification.layer,
|
|
5004
5089
|
targetSubfolder,
|
|
5005
5090
|
targetFileName,
|
|
5006
5091
|
isTargetPort,
|
|
@@ -5008,25 +5093,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
5008
5093
|
isTargetError
|
|
5009
5094
|
};
|
|
5010
5095
|
}
|
|
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";
|
|
5096
|
+
if (classification.moduleType === "shared") {
|
|
5097
|
+
const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
|
|
5098
|
+
const isTargetError = classification.layer === "domain" && (targetFileName.endsWith("Error") || targetFileName.endsWith("Errors") || targetAbs.includes("/errors/"));
|
|
5019
5099
|
return {
|
|
5020
5100
|
isExternal: false,
|
|
5021
5101
|
isDiFramework: false,
|
|
5022
|
-
targetSharedModule,
|
|
5023
|
-
isSharedRoot,
|
|
5024
|
-
isSharedPorts,
|
|
5025
|
-
isSharedInfra,
|
|
5026
|
-
isSharedDomain
|
|
5102
|
+
targetSharedModule: classification.moduleName,
|
|
5103
|
+
isSharedRoot: classification.layer === "root",
|
|
5104
|
+
isSharedPorts: classification.layer === "ports",
|
|
5105
|
+
isSharedInfra: classification.layer === "infrastructure",
|
|
5106
|
+
isSharedDomain: classification.layer === "domain",
|
|
5107
|
+
isTargetError
|
|
5027
5108
|
};
|
|
5028
5109
|
}
|
|
5029
|
-
return { isExternal: true, isDiFramework: false, isGlobalConfig };
|
|
5110
|
+
return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
|
|
5030
5111
|
}
|
|
5031
5112
|
function extractImportStatements(content) {
|
|
5032
5113
|
const statements = [];
|
|
@@ -5148,22 +5229,50 @@ var FeatureDependencyGraph = class {
|
|
|
5148
5229
|
return dfs(start);
|
|
5149
5230
|
}
|
|
5150
5231
|
};
|
|
5151
|
-
function discoverAllFeatures(
|
|
5152
|
-
const
|
|
5153
|
-
if (!
|
|
5154
|
-
const
|
|
5155
|
-
|
|
5232
|
+
function discoverAllFeatures(featuresBaseAbs, excludeDirs = []) {
|
|
5233
|
+
const base = featuresBaseAbs;
|
|
5234
|
+
if (!fs17.existsSync(base)) return [];
|
|
5235
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
|
|
5236
|
+
const entries = fs17.readdirSync(base, { withFileTypes: true });
|
|
5237
|
+
return entries.filter((e) => {
|
|
5238
|
+
if (!e.isDirectory()) return false;
|
|
5239
|
+
if (e.name.startsWith(".")) return false;
|
|
5240
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5241
|
+
return false;
|
|
5242
|
+
}
|
|
5243
|
+
const resolved = path26.resolve(base, e.name);
|
|
5244
|
+
for (const ex of excludeSet) {
|
|
5245
|
+
if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
|
|
5246
|
+
return false;
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
return true;
|
|
5250
|
+
}).map((e) => ({
|
|
5156
5251
|
name: e.name,
|
|
5157
|
-
dir:
|
|
5252
|
+
dir: path26.join(base, e.name)
|
|
5158
5253
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5159
5254
|
}
|
|
5160
|
-
function discoverAllShared(
|
|
5161
|
-
const
|
|
5162
|
-
if (!
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5255
|
+
function discoverAllShared(sharedBaseAbs, excludeDirs = []) {
|
|
5256
|
+
const base = sharedBaseAbs;
|
|
5257
|
+
if (!fs17.existsSync(base)) return [];
|
|
5258
|
+
const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
|
|
5259
|
+
const entries = fs17.readdirSync(base, { withFileTypes: true });
|
|
5260
|
+
return entries.filter((e) => {
|
|
5261
|
+
if (!e.isDirectory()) return false;
|
|
5262
|
+
if (e.name.startsWith(".")) return false;
|
|
5263
|
+
if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
|
|
5264
|
+
return false;
|
|
5265
|
+
}
|
|
5266
|
+
const resolved = path26.resolve(base, e.name);
|
|
5267
|
+
for (const ex of excludeSet) {
|
|
5268
|
+
if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
|
|
5269
|
+
return false;
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
return true;
|
|
5273
|
+
}).map((e) => ({
|
|
5165
5274
|
name: e.name,
|
|
5166
|
-
dir:
|
|
5275
|
+
dir: path26.join(base, e.name)
|
|
5167
5276
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5168
5277
|
}
|
|
5169
5278
|
function extractClassMethods(content, className) {
|
|
@@ -5319,7 +5428,7 @@ function extractClassMethods(content, className) {
|
|
|
5319
5428
|
return methods;
|
|
5320
5429
|
}
|
|
5321
5430
|
function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5322
|
-
const fileName =
|
|
5431
|
+
const fileName = path26.basename(filePath);
|
|
5323
5432
|
const lines = content.split(/\r?\n/);
|
|
5324
5433
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5325
5434
|
if (!classMatch) {
|
|
@@ -5415,7 +5524,7 @@ private ${m.name}(...) { ... }`
|
|
|
5415
5524
|
}
|
|
5416
5525
|
}
|
|
5417
5526
|
function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
|
|
5418
|
-
const fileName =
|
|
5527
|
+
const fileName = path26.basename(filePath);
|
|
5419
5528
|
const lines = content.split(/\r?\n/);
|
|
5420
5529
|
const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
|
|
5421
5530
|
if (!classMatch) {
|
|
@@ -5514,40 +5623,32 @@ function lintCodebase(options = {}) {
|
|
|
5514
5623
|
const startTime = Date.now();
|
|
5515
5624
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5516
5625
|
const failFast = !options.complete;
|
|
5517
|
-
const
|
|
5518
|
-
const baseErrorRelative = lintConfig.baseErrorPath || "src/errors/AppError.ts";
|
|
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
|
-
}
|
|
5626
|
+
const { featuresBase, sharedBase, errorsBase, baseErrorAbs } = resolveLintEnvironment(projectRoot);
|
|
5526
5627
|
const missingFoundations = [];
|
|
5527
|
-
if (!
|
|
5528
|
-
if (!
|
|
5529
|
-
if (!
|
|
5628
|
+
if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5629
|
+
if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5630
|
+
if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5530
5631
|
if (missingFoundations.length > 0) {
|
|
5531
5632
|
throw new DependencyRuleError(
|
|
5532
|
-
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every
|
|
5633
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every architecture must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5533
5634
|
[],
|
|
5534
5635
|
true
|
|
5535
5636
|
);
|
|
5536
5637
|
}
|
|
5537
|
-
if (!
|
|
5638
|
+
if (!fs17.existsSync(baseErrorAbs)) {
|
|
5538
5639
|
throw new DependencyRuleError(
|
|
5539
|
-
`Project Structure Violation: Base error class not found at '${
|
|
5640
|
+
`Project Structure Violation: Base error class not found at '${path26.relative(projectRoot, baseErrorAbs).replace(/\\/g, "/")}'. Configure paths.baseError in mason.config.json.`,
|
|
5540
5641
|
[],
|
|
5541
5642
|
true
|
|
5542
5643
|
);
|
|
5543
5644
|
}
|
|
5544
|
-
const baseErrorContent =
|
|
5645
|
+
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5545
5646
|
const baseClassMatch = baseErrorContent.match(
|
|
5546
5647
|
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5547
5648
|
);
|
|
5548
5649
|
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5549
|
-
const features = discoverAllFeatures(
|
|
5550
|
-
const sharedModules = discoverAllShared(
|
|
5650
|
+
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5651
|
+
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5551
5652
|
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5552
5653
|
const allModules = [
|
|
5553
5654
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
@@ -5590,12 +5691,12 @@ Code: ${v.code}`;
|
|
|
5590
5691
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5591
5692
|
modResult.scannedFilesCount = allFiles.length;
|
|
5592
5693
|
for (const file of allFiles) {
|
|
5593
|
-
const relPath =
|
|
5594
|
-
const { moduleType, moduleName, layer } = classifyFile(file,
|
|
5694
|
+
const relPath = path26.relative(projectRoot, file).replace(/\\/g, "/");
|
|
5695
|
+
const { moduleType, moduleName, layer } = classifyFile(file, featuresBase, sharedBase, errorsBase);
|
|
5595
5696
|
if (!layer || !moduleType || !moduleName) continue;
|
|
5596
|
-
const content =
|
|
5697
|
+
const content = fs17.readFileSync(file, "utf-8");
|
|
5597
5698
|
const statements = extractImportStatements(content);
|
|
5598
|
-
const baseName =
|
|
5699
|
+
const baseName = path26.basename(file);
|
|
5599
5700
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5600
5701
|
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5601
5702
|
let throwMatch;
|
|
@@ -5614,7 +5715,7 @@ Code: ${v.code}`;
|
|
|
5614
5715
|
code: lineCode,
|
|
5615
5716
|
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5616
5717
|
severity: "error",
|
|
5617
|
-
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a
|
|
5718
|
+
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a custom error class that inherits from it.`,
|
|
5618
5719
|
fixExample: {
|
|
5619
5720
|
bad: lineCode,
|
|
5620
5721
|
good: `throw new ${baseErrorClassName}("Something went wrong");`
|
|
@@ -5622,7 +5723,7 @@ Code: ${v.code}`;
|
|
|
5622
5723
|
});
|
|
5623
5724
|
}
|
|
5624
5725
|
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 =
|
|
5726
|
+
const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5626
5727
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5627
5728
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5628
5729
|
let classMatch;
|
|
@@ -5644,7 +5745,7 @@ Code: ${v.code}`;
|
|
|
5644
5745
|
code: lineCode,
|
|
5645
5746
|
ruleId: "E4_MISSING_INHERITANCE",
|
|
5646
5747
|
severity: "error",
|
|
5647
|
-
advice: `Error class '${className}' must extend '${baseErrorClassName}'
|
|
5748
|
+
advice: `Error class '${className}' must extend '${baseErrorClassName}'.`,
|
|
5648
5749
|
fixExample: {
|
|
5649
5750
|
bad: lineCode,
|
|
5650
5751
|
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
@@ -5661,7 +5762,7 @@ Code: ${v.code}`;
|
|
|
5661
5762
|
code: lineCode,
|
|
5662
5763
|
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5663
5764
|
severity: "error",
|
|
5664
|
-
advice: `Error class '${className}' extends
|
|
5765
|
+
advice: `Error class '${className}' extends native 'Error'. It must extend your configured base error class ('${baseErrorClassName}').`,
|
|
5665
5766
|
fixExample: {
|
|
5666
5767
|
bad: lineCode,
|
|
5667
5768
|
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
@@ -5672,7 +5773,7 @@ Code: ${v.code}`;
|
|
|
5672
5773
|
}
|
|
5673
5774
|
if (moduleType === "error") {
|
|
5674
5775
|
for (const stmt of statements) {
|
|
5675
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5776
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5676
5777
|
if (target.targetFeature) {
|
|
5677
5778
|
addViolation({
|
|
5678
5779
|
feature: "global-errors",
|
|
@@ -5738,7 +5839,7 @@ Code: ${v.code}`;
|
|
|
5738
5839
|
}
|
|
5739
5840
|
}
|
|
5740
5841
|
for (const stmt of statements) {
|
|
5741
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5842
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5742
5843
|
if (target.targetFeature) {
|
|
5743
5844
|
addViolation({
|
|
5744
5845
|
feature: moduleName,
|
|
@@ -5868,7 +5969,7 @@ Code: ${v.code}`;
|
|
|
5868
5969
|
}
|
|
5869
5970
|
}
|
|
5870
5971
|
for (const stmt of statements) {
|
|
5871
|
-
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5972
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
|
|
5872
5973
|
if (target.targetFeature && target.targetFeature !== feat.name) {
|
|
5873
5974
|
const evidence = {
|
|
5874
5975
|
fromFeature: feat.name,
|