@solid-stack/mason 1.0.35 → 1.0.36
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 +170 -31
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +170 -31
- package/dist/bin.js.map +1 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/core/lintEngine.d.ts +9 -0
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +170 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +170 -31
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +2 -0
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -5619,42 +5619,108 @@ private ${m.name}(...) { ... }`
|
|
|
5619
5619
|
}
|
|
5620
5620
|
}
|
|
5621
5621
|
}
|
|
5622
|
+
function resolveRegistryModuleError(modDir, modName) {
|
|
5623
|
+
const errorsDir = path26.join(modDir, "errors");
|
|
5624
|
+
if (!fs17.existsSync(errorsDir) || !fs17.statSync(errorsDir).isDirectory()) {
|
|
5625
|
+
return { errorsDir, hasErrorsDir: false };
|
|
5626
|
+
}
|
|
5627
|
+
const entries = fs17.readdirSync(errorsDir);
|
|
5628
|
+
const errorFiles = entries.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
|
|
5629
|
+
const nameNormalized = modName.toLowerCase().replace(/[-_]/g, "");
|
|
5630
|
+
const singularNormalized = toSingular(modName).toLowerCase().replace(/[-_]/g, "");
|
|
5631
|
+
let baseErrorFileName = errorFiles.find((f) => {
|
|
5632
|
+
const baseNoExt = f.replace(/\.(ts|js)$/, "").toLowerCase().replace(/[-_]/g, "");
|
|
5633
|
+
return baseNoExt === `${nameNormalized}error` || baseNoExt === `${singularNormalized}error` || baseNoExt === `${nameNormalized}errors` || baseNoExt === `${singularNormalized}errors`;
|
|
5634
|
+
});
|
|
5635
|
+
if (!baseErrorFileName) {
|
|
5636
|
+
return { errorsDir, hasErrorsDir: true };
|
|
5637
|
+
}
|
|
5638
|
+
const baseErrorFileAbs = path26.join(errorsDir, baseErrorFileName);
|
|
5639
|
+
const content = fs17.readFileSync(baseErrorFileAbs, "utf-8");
|
|
5640
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5641
|
+
let match;
|
|
5642
|
+
const classes = [];
|
|
5643
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
5644
|
+
classes.push({
|
|
5645
|
+
name: match[1],
|
|
5646
|
+
extendsName: match[2],
|
|
5647
|
+
raw: match[0]
|
|
5648
|
+
});
|
|
5649
|
+
}
|
|
5650
|
+
const expectedNames = [
|
|
5651
|
+
`${toPascalCase(modName)}Error`,
|
|
5652
|
+
`${toPascalCase(toSingular(modName))}Error`,
|
|
5653
|
+
`${toPascalCase(modName)}Errors`,
|
|
5654
|
+
`${toPascalCase(toSingular(modName))}Errors`
|
|
5655
|
+
].map((n) => n.toLowerCase());
|
|
5656
|
+
const targetClass = classes.find((c) => expectedNames.includes(c.name.toLowerCase())) || classes.find((c) => c.raw.includes("export")) || classes[0];
|
|
5657
|
+
return {
|
|
5658
|
+
errorsDir,
|
|
5659
|
+
hasErrorsDir: true,
|
|
5660
|
+
baseErrorFileAbs,
|
|
5661
|
+
baseErrorClassName: targetClass ? targetClass.name : void 0,
|
|
5662
|
+
baseErrorExtendsName: targetClass ? targetClass.extendsName : void 0,
|
|
5663
|
+
baseErrorRawClassCode: targetClass ? targetClass.raw : void 0
|
|
5664
|
+
};
|
|
5665
|
+
}
|
|
5622
5666
|
function lintCodebase(options = {}) {
|
|
5623
5667
|
const startTime = Date.now();
|
|
5624
5668
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5625
5669
|
const failFast = !options.complete;
|
|
5670
|
+
const effectiveMode = options.mode || getEffectiveMode(projectRoot, options.registry !== void 0 ? { registry: options.registry } : void 0);
|
|
5671
|
+
const isRegistryMode = effectiveMode === "registry";
|
|
5626
5672
|
const { featuresBase, sharedBase, errorsBase, baseErrorAbs } = resolveLintEnvironment(projectRoot);
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5673
|
+
if (!isRegistryMode) {
|
|
5674
|
+
const missingFoundations = [];
|
|
5675
|
+
if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5676
|
+
if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5677
|
+
if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5678
|
+
if (missingFoundations.length > 0) {
|
|
5679
|
+
throw new DependencyRuleError(
|
|
5680
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every architecture must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5681
|
+
[],
|
|
5682
|
+
true
|
|
5683
|
+
);
|
|
5684
|
+
}
|
|
5685
|
+
if (!fs17.existsSync(baseErrorAbs)) {
|
|
5686
|
+
throw new DependencyRuleError(
|
|
5687
|
+
`Project Structure Violation: Base error class not found at '${path26.relative(projectRoot, baseErrorAbs).replace(/\\/g, "/")}'. Configure paths.baseError in mason.config.json.`,
|
|
5688
|
+
[],
|
|
5689
|
+
true
|
|
5690
|
+
);
|
|
5691
|
+
}
|
|
5692
|
+
} else {
|
|
5693
|
+
if (!fs17.existsSync(featuresBase) && !fs17.existsSync(sharedBase)) {
|
|
5694
|
+
throw new DependencyRuleError(
|
|
5695
|
+
`Project Structure Violation: Missing required foundational directories: features/ or shared/. A registry must contain 'features/' or 'shared/'.`,
|
|
5696
|
+
[],
|
|
5697
|
+
true
|
|
5698
|
+
);
|
|
5699
|
+
}
|
|
5637
5700
|
}
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5701
|
+
let baseErrorClassName = "AppError";
|
|
5702
|
+
if (!isRegistryMode && fs17.existsSync(baseErrorAbs)) {
|
|
5703
|
+
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5704
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5705
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5643
5706
|
);
|
|
5707
|
+
baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5644
5708
|
}
|
|
5645
|
-
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5646
|
-
const baseClassMatch = baseErrorContent.match(
|
|
5647
|
-
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5648
|
-
);
|
|
5649
|
-
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5650
5709
|
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5651
5710
|
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5652
|
-
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5653
5711
|
const allModules = [
|
|
5654
5712
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5655
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5656
|
-
errorModule
|
|
5713
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5657
5714
|
];
|
|
5715
|
+
if (!isRegistryMode) {
|
|
5716
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5717
|
+
allModules.push(errorModule);
|
|
5718
|
+
} else if (fs17.existsSync(errorsBase)) {
|
|
5719
|
+
const errorFiles = scanDirFiles(errorsBase, projectRoot);
|
|
5720
|
+
if (errorFiles.length > 0) {
|
|
5721
|
+
allModules.push({ name: "global-errors", dir: errorsBase, type: "error" });
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5658
5724
|
const graph = new FeatureDependencyGraph();
|
|
5659
5725
|
const allViolations = [];
|
|
5660
5726
|
const results = [];
|
|
@@ -5688,6 +5754,77 @@ Code: ${v.code}`;
|
|
|
5688
5754
|
}
|
|
5689
5755
|
for (const mod of allModules) {
|
|
5690
5756
|
const modResult = results.find((r) => r.featureName === mod.name);
|
|
5757
|
+
let modBaseErrorClass = baseErrorClassName;
|
|
5758
|
+
let modBaseErrorAbs = !isRegistryMode ? baseErrorAbs : void 0;
|
|
5759
|
+
if (isRegistryMode && (mod.type === "feature" || mod.type === "shared")) {
|
|
5760
|
+
const errorInfo = resolveRegistryModuleError(mod.dir, mod.name);
|
|
5761
|
+
if (!errorInfo.hasErrorsDir) {
|
|
5762
|
+
addViolation({
|
|
5763
|
+
feature: mod.name,
|
|
5764
|
+
layer: "domain",
|
|
5765
|
+
file: mod.dir,
|
|
5766
|
+
relativePath: path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/"),
|
|
5767
|
+
line: 1,
|
|
5768
|
+
category: "Project Structure Violation",
|
|
5769
|
+
code: `Missing errors/ directory in registry ${mod.type} '${mod.name}'`,
|
|
5770
|
+
ruleId: "REG_ERRORS_DIR_REQUIRED",
|
|
5771
|
+
severity: "error",
|
|
5772
|
+
advice: `Registry ${mod.type} '${mod.name}' must implement its own 'errors/' directory at '${path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/")}'.`,
|
|
5773
|
+
fixExample: {
|
|
5774
|
+
bad: "// Missing errors directory",
|
|
5775
|
+
good: `mkdir -p ${path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/")}`
|
|
5776
|
+
}
|
|
5777
|
+
});
|
|
5778
|
+
} else if (!errorInfo.baseErrorFileAbs) {
|
|
5779
|
+
const expectedFileName = `${toPascalCase(mod.name)}Error.ts`;
|
|
5780
|
+
const expectedRel = path26.relative(projectRoot, path26.join(errorInfo.errorsDir, expectedFileName)).replace(/\\/g, "/");
|
|
5781
|
+
addViolation({
|
|
5782
|
+
feature: mod.name,
|
|
5783
|
+
layer: "domain",
|
|
5784
|
+
file: errorInfo.errorsDir,
|
|
5785
|
+
relativePath: expectedRel,
|
|
5786
|
+
line: 1,
|
|
5787
|
+
category: "Project Structure Violation",
|
|
5788
|
+
code: `Missing base error file in '${mod.name}/errors'`,
|
|
5789
|
+
ruleId: "REG_BASE_ERROR_REQUIRED",
|
|
5790
|
+
severity: "error",
|
|
5791
|
+
advice: `Registry ${mod.type} '${mod.name}' must have a base error file at '${expectedRel}'.`,
|
|
5792
|
+
fixExample: {
|
|
5793
|
+
bad: "// Missing base error file",
|
|
5794
|
+
good: `export class ${toPascalCase(mod.name)}Error extends Error {
|
|
5795
|
+
constructor(message: string) {
|
|
5796
|
+
super(message);
|
|
5797
|
+
}
|
|
5798
|
+
}`
|
|
5799
|
+
}
|
|
5800
|
+
});
|
|
5801
|
+
} else {
|
|
5802
|
+
modBaseErrorAbs = errorInfo.baseErrorFileAbs;
|
|
5803
|
+
modBaseErrorClass = errorInfo.baseErrorClassName || `${toPascalCase(mod.name)}Error`;
|
|
5804
|
+
if (!errorInfo.baseErrorClassName || errorInfo.baseErrorExtendsName !== "Error") {
|
|
5805
|
+
addViolation({
|
|
5806
|
+
feature: mod.name,
|
|
5807
|
+
layer: "domain",
|
|
5808
|
+
file: errorInfo.baseErrorFileAbs,
|
|
5809
|
+
relativePath: path26.relative(projectRoot, errorInfo.baseErrorFileAbs).replace(/\\/g, "/"),
|
|
5810
|
+
line: 1,
|
|
5811
|
+
category: "Error Inheritance Violation",
|
|
5812
|
+
code: errorInfo.baseErrorRawClassCode || `class ${modBaseErrorClass}`,
|
|
5813
|
+
ruleId: "REG_BASE_ERROR_MUST_EXTEND_ERROR",
|
|
5814
|
+
severity: "error",
|
|
5815
|
+
advice: `Registry ${mod.type} base error class '${modBaseErrorClass}' in '${mod.name}' must extend native 'Error'.`,
|
|
5816
|
+
fixExample: {
|
|
5817
|
+
bad: errorInfo.baseErrorRawClassCode || `class ${modBaseErrorClass}`,
|
|
5818
|
+
good: `export class ${modBaseErrorClass} extends Error {
|
|
5819
|
+
constructor(message: string) {
|
|
5820
|
+
super(message);
|
|
5821
|
+
}
|
|
5822
|
+
}`
|
|
5823
|
+
}
|
|
5824
|
+
});
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5691
5828
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5692
5829
|
modResult.scannedFilesCount = allFiles.length;
|
|
5693
5830
|
for (const file of allFiles) {
|
|
@@ -5715,15 +5852,15 @@ Code: ${v.code}`;
|
|
|
5715
5852
|
code: lineCode,
|
|
5716
5853
|
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5717
5854
|
severity: "error",
|
|
5718
|
-
advice: `Throwing plain 'Error' is prohibited. You must throw '${
|
|
5855
|
+
advice: isRegistryMode ? `Throwing plain 'Error' is prohibited in registry ${mod.type} '${mod.name}'. You must throw '${modBaseErrorClass}' or a class that inherits from it.` : `Throwing plain 'Error' is prohibited. You must throw '${modBaseErrorClass}' or a custom error class that inherits from it.`,
|
|
5719
5856
|
fixExample: {
|
|
5720
5857
|
bad: lineCode,
|
|
5721
|
-
good: `throw new ${
|
|
5858
|
+
good: `throw new ${modBaseErrorClass}("Something went wrong");`
|
|
5722
5859
|
}
|
|
5723
5860
|
});
|
|
5724
5861
|
}
|
|
5725
5862
|
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");
|
|
5726
|
-
const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5863
|
+
const isBaseErrorFile = modBaseErrorAbs ? path26.resolve(file) === path26.resolve(modBaseErrorAbs) : !isRegistryMode && path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5727
5864
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5728
5865
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5729
5866
|
let classMatch;
|
|
@@ -5745,10 +5882,10 @@ Code: ${v.code}`;
|
|
|
5745
5882
|
code: lineCode,
|
|
5746
5883
|
ruleId: "E4_MISSING_INHERITANCE",
|
|
5747
5884
|
severity: "error",
|
|
5748
|
-
advice: `Error class '${className}' must extend '${
|
|
5885
|
+
advice: isRegistryMode ? `Error class '${className}' in registry ${mod.type} '${mod.name}' must extend '${modBaseErrorClass}'.` : `Error class '${className}' must extend '${modBaseErrorClass}'.`,
|
|
5749
5886
|
fixExample: {
|
|
5750
5887
|
bad: lineCode,
|
|
5751
|
-
good: `export class ${className} extends ${
|
|
5888
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5752
5889
|
}
|
|
5753
5890
|
});
|
|
5754
5891
|
} else if (extendsName === "Error") {
|
|
@@ -5762,10 +5899,10 @@ Code: ${v.code}`;
|
|
|
5762
5899
|
code: lineCode,
|
|
5763
5900
|
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5764
5901
|
severity: "error",
|
|
5765
|
-
advice: `Error class '${className}' extends native 'Error'. It must extend your configured base error class ('${
|
|
5902
|
+
advice: isRegistryMode ? `Error class '${className}' extends native 'Error'. In registry ${mod.type} '${mod.name}', it must extend your base error class ('${modBaseErrorClass}').` : `Error class '${className}' extends native 'Error'. It must extend your configured base error class ('${modBaseErrorClass}').`,
|
|
5766
5903
|
fixExample: {
|
|
5767
5904
|
bad: lineCode,
|
|
5768
|
-
good: `export class ${className} extends ${
|
|
5905
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5769
5906
|
}
|
|
5770
5907
|
});
|
|
5771
5908
|
}
|
|
@@ -6525,7 +6662,9 @@ async function handleLint(flags = {}) {
|
|
|
6525
6662
|
targetDir: flags.targetDir || flags.target,
|
|
6526
6663
|
projectRoot: flags.projectRoot,
|
|
6527
6664
|
fixHint: flags.fixHint !== false,
|
|
6528
|
-
throwOnError: false
|
|
6665
|
+
throwOnError: false,
|
|
6666
|
+
mode: flags.mode || (flags.registry ? "registry" : void 0),
|
|
6667
|
+
registry: Boolean(flags.registry)
|
|
6529
6668
|
};
|
|
6530
6669
|
try {
|
|
6531
6670
|
const summary = lintCodebase(options);
|