@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/index.js
CHANGED
|
@@ -5659,42 +5659,108 @@ private ${m.name}(...) { ... }`
|
|
|
5659
5659
|
}
|
|
5660
5660
|
}
|
|
5661
5661
|
}
|
|
5662
|
+
function resolveRegistryModuleError(modDir, modName) {
|
|
5663
|
+
const errorsDir = path26.join(modDir, "errors");
|
|
5664
|
+
if (!fs17.existsSync(errorsDir) || !fs17.statSync(errorsDir).isDirectory()) {
|
|
5665
|
+
return { errorsDir, hasErrorsDir: false };
|
|
5666
|
+
}
|
|
5667
|
+
const entries = fs17.readdirSync(errorsDir);
|
|
5668
|
+
const errorFiles = entries.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
|
|
5669
|
+
const nameNormalized = modName.toLowerCase().replace(/[-_]/g, "");
|
|
5670
|
+
const singularNormalized = toSingular(modName).toLowerCase().replace(/[-_]/g, "");
|
|
5671
|
+
let baseErrorFileName = errorFiles.find((f) => {
|
|
5672
|
+
const baseNoExt = f.replace(/\.(ts|js)$/, "").toLowerCase().replace(/[-_]/g, "");
|
|
5673
|
+
return baseNoExt === `${nameNormalized}error` || baseNoExt === `${singularNormalized}error` || baseNoExt === `${nameNormalized}errors` || baseNoExt === `${singularNormalized}errors`;
|
|
5674
|
+
});
|
|
5675
|
+
if (!baseErrorFileName) {
|
|
5676
|
+
return { errorsDir, hasErrorsDir: true };
|
|
5677
|
+
}
|
|
5678
|
+
const baseErrorFileAbs = path26.join(errorsDir, baseErrorFileName);
|
|
5679
|
+
const content = fs17.readFileSync(baseErrorFileAbs, "utf-8");
|
|
5680
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5681
|
+
let match;
|
|
5682
|
+
const classes = [];
|
|
5683
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
5684
|
+
classes.push({
|
|
5685
|
+
name: match[1],
|
|
5686
|
+
extendsName: match[2],
|
|
5687
|
+
raw: match[0]
|
|
5688
|
+
});
|
|
5689
|
+
}
|
|
5690
|
+
const expectedNames = [
|
|
5691
|
+
`${toPascalCase(modName)}Error`,
|
|
5692
|
+
`${toPascalCase(toSingular(modName))}Error`,
|
|
5693
|
+
`${toPascalCase(modName)}Errors`,
|
|
5694
|
+
`${toPascalCase(toSingular(modName))}Errors`
|
|
5695
|
+
].map((n) => n.toLowerCase());
|
|
5696
|
+
const targetClass = classes.find((c) => expectedNames.includes(c.name.toLowerCase())) || classes.find((c) => c.raw.includes("export")) || classes[0];
|
|
5697
|
+
return {
|
|
5698
|
+
errorsDir,
|
|
5699
|
+
hasErrorsDir: true,
|
|
5700
|
+
baseErrorFileAbs,
|
|
5701
|
+
baseErrorClassName: targetClass ? targetClass.name : void 0,
|
|
5702
|
+
baseErrorExtendsName: targetClass ? targetClass.extendsName : void 0,
|
|
5703
|
+
baseErrorRawClassCode: targetClass ? targetClass.raw : void 0
|
|
5704
|
+
};
|
|
5705
|
+
}
|
|
5662
5706
|
function lintCodebase(options = {}) {
|
|
5663
5707
|
const startTime = Date.now();
|
|
5664
5708
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5665
5709
|
const failFast = !options.complete;
|
|
5710
|
+
const effectiveMode = options.mode || getEffectiveMode(projectRoot, options.registry !== void 0 ? { registry: options.registry } : void 0);
|
|
5711
|
+
const isRegistryMode = effectiveMode === "registry";
|
|
5666
5712
|
const { featuresBase, sharedBase, errorsBase, baseErrorAbs } = resolveLintEnvironment(projectRoot);
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5713
|
+
if (!isRegistryMode) {
|
|
5714
|
+
const missingFoundations = [];
|
|
5715
|
+
if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5716
|
+
if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5717
|
+
if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5718
|
+
if (missingFoundations.length > 0) {
|
|
5719
|
+
throw new DependencyRuleError(
|
|
5720
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every architecture must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5721
|
+
[],
|
|
5722
|
+
true
|
|
5723
|
+
);
|
|
5724
|
+
}
|
|
5725
|
+
if (!fs17.existsSync(baseErrorAbs)) {
|
|
5726
|
+
throw new DependencyRuleError(
|
|
5727
|
+
`Project Structure Violation: Base error class not found at '${path26.relative(projectRoot, baseErrorAbs).replace(/\\/g, "/")}'. Configure paths.baseError in mason.config.json.`,
|
|
5728
|
+
[],
|
|
5729
|
+
true
|
|
5730
|
+
);
|
|
5731
|
+
}
|
|
5732
|
+
} else {
|
|
5733
|
+
if (!fs17.existsSync(featuresBase) && !fs17.existsSync(sharedBase)) {
|
|
5734
|
+
throw new DependencyRuleError(
|
|
5735
|
+
`Project Structure Violation: Missing required foundational directories: features/ or shared/. A registry must contain 'features/' or 'shared/'.`,
|
|
5736
|
+
[],
|
|
5737
|
+
true
|
|
5738
|
+
);
|
|
5739
|
+
}
|
|
5677
5740
|
}
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5741
|
+
let baseErrorClassName = "AppError";
|
|
5742
|
+
if (!isRegistryMode && fs17.existsSync(baseErrorAbs)) {
|
|
5743
|
+
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5744
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5745
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5683
5746
|
);
|
|
5747
|
+
baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5684
5748
|
}
|
|
5685
|
-
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5686
|
-
const baseClassMatch = baseErrorContent.match(
|
|
5687
|
-
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5688
|
-
);
|
|
5689
|
-
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5690
5749
|
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5691
5750
|
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5692
|
-
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5693
5751
|
const allModules = [
|
|
5694
5752
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5695
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5696
|
-
errorModule
|
|
5753
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5697
5754
|
];
|
|
5755
|
+
if (!isRegistryMode) {
|
|
5756
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5757
|
+
allModules.push(errorModule);
|
|
5758
|
+
} else if (fs17.existsSync(errorsBase)) {
|
|
5759
|
+
const errorFiles = scanDirFiles(errorsBase, projectRoot);
|
|
5760
|
+
if (errorFiles.length > 0) {
|
|
5761
|
+
allModules.push({ name: "global-errors", dir: errorsBase, type: "error" });
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5698
5764
|
const graph = new FeatureDependencyGraph();
|
|
5699
5765
|
const allViolations = [];
|
|
5700
5766
|
const results = [];
|
|
@@ -5728,6 +5794,77 @@ Code: ${v.code}`;
|
|
|
5728
5794
|
}
|
|
5729
5795
|
for (const mod of allModules) {
|
|
5730
5796
|
const modResult = results.find((r) => r.featureName === mod.name);
|
|
5797
|
+
let modBaseErrorClass = baseErrorClassName;
|
|
5798
|
+
let modBaseErrorAbs = !isRegistryMode ? baseErrorAbs : void 0;
|
|
5799
|
+
if (isRegistryMode && (mod.type === "feature" || mod.type === "shared")) {
|
|
5800
|
+
const errorInfo = resolveRegistryModuleError(mod.dir, mod.name);
|
|
5801
|
+
if (!errorInfo.hasErrorsDir) {
|
|
5802
|
+
addViolation({
|
|
5803
|
+
feature: mod.name,
|
|
5804
|
+
layer: "domain",
|
|
5805
|
+
file: mod.dir,
|
|
5806
|
+
relativePath: path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/"),
|
|
5807
|
+
line: 1,
|
|
5808
|
+
category: "Project Structure Violation",
|
|
5809
|
+
code: `Missing errors/ directory in registry ${mod.type} '${mod.name}'`,
|
|
5810
|
+
ruleId: "REG_ERRORS_DIR_REQUIRED",
|
|
5811
|
+
severity: "error",
|
|
5812
|
+
advice: `Registry ${mod.type} '${mod.name}' must implement its own 'errors/' directory at '${path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/")}'.`,
|
|
5813
|
+
fixExample: {
|
|
5814
|
+
bad: "// Missing errors directory",
|
|
5815
|
+
good: `mkdir -p ${path26.relative(projectRoot, errorInfo.errorsDir).replace(/\\/g, "/")}`
|
|
5816
|
+
}
|
|
5817
|
+
});
|
|
5818
|
+
} else if (!errorInfo.baseErrorFileAbs) {
|
|
5819
|
+
const expectedFileName = `${toPascalCase(mod.name)}Error.ts`;
|
|
5820
|
+
const expectedRel = path26.relative(projectRoot, path26.join(errorInfo.errorsDir, expectedFileName)).replace(/\\/g, "/");
|
|
5821
|
+
addViolation({
|
|
5822
|
+
feature: mod.name,
|
|
5823
|
+
layer: "domain",
|
|
5824
|
+
file: errorInfo.errorsDir,
|
|
5825
|
+
relativePath: expectedRel,
|
|
5826
|
+
line: 1,
|
|
5827
|
+
category: "Project Structure Violation",
|
|
5828
|
+
code: `Missing base error file in '${mod.name}/errors'`,
|
|
5829
|
+
ruleId: "REG_BASE_ERROR_REQUIRED",
|
|
5830
|
+
severity: "error",
|
|
5831
|
+
advice: `Registry ${mod.type} '${mod.name}' must have a base error file at '${expectedRel}'.`,
|
|
5832
|
+
fixExample: {
|
|
5833
|
+
bad: "// Missing base error file",
|
|
5834
|
+
good: `export class ${toPascalCase(mod.name)}Error extends Error {
|
|
5835
|
+
constructor(message: string) {
|
|
5836
|
+
super(message);
|
|
5837
|
+
}
|
|
5838
|
+
}`
|
|
5839
|
+
}
|
|
5840
|
+
});
|
|
5841
|
+
} else {
|
|
5842
|
+
modBaseErrorAbs = errorInfo.baseErrorFileAbs;
|
|
5843
|
+
modBaseErrorClass = errorInfo.baseErrorClassName || `${toPascalCase(mod.name)}Error`;
|
|
5844
|
+
if (!errorInfo.baseErrorClassName || errorInfo.baseErrorExtendsName !== "Error") {
|
|
5845
|
+
addViolation({
|
|
5846
|
+
feature: mod.name,
|
|
5847
|
+
layer: "domain",
|
|
5848
|
+
file: errorInfo.baseErrorFileAbs,
|
|
5849
|
+
relativePath: path26.relative(projectRoot, errorInfo.baseErrorFileAbs).replace(/\\/g, "/"),
|
|
5850
|
+
line: 1,
|
|
5851
|
+
category: "Error Inheritance Violation",
|
|
5852
|
+
code: errorInfo.baseErrorRawClassCode || `class ${modBaseErrorClass}`,
|
|
5853
|
+
ruleId: "REG_BASE_ERROR_MUST_EXTEND_ERROR",
|
|
5854
|
+
severity: "error",
|
|
5855
|
+
advice: `Registry ${mod.type} base error class '${modBaseErrorClass}' in '${mod.name}' must extend native 'Error'.`,
|
|
5856
|
+
fixExample: {
|
|
5857
|
+
bad: errorInfo.baseErrorRawClassCode || `class ${modBaseErrorClass}`,
|
|
5858
|
+
good: `export class ${modBaseErrorClass} extends Error {
|
|
5859
|
+
constructor(message: string) {
|
|
5860
|
+
super(message);
|
|
5861
|
+
}
|
|
5862
|
+
}`
|
|
5863
|
+
}
|
|
5864
|
+
});
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
}
|
|
5731
5868
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5732
5869
|
modResult.scannedFilesCount = allFiles.length;
|
|
5733
5870
|
for (const file of allFiles) {
|
|
@@ -5755,15 +5892,15 @@ Code: ${v.code}`;
|
|
|
5755
5892
|
code: lineCode,
|
|
5756
5893
|
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5757
5894
|
severity: "error",
|
|
5758
|
-
advice: `Throwing plain 'Error' is prohibited. You must throw '${
|
|
5895
|
+
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.`,
|
|
5759
5896
|
fixExample: {
|
|
5760
5897
|
bad: lineCode,
|
|
5761
|
-
good: `throw new ${
|
|
5898
|
+
good: `throw new ${modBaseErrorClass}("Something went wrong");`
|
|
5762
5899
|
}
|
|
5763
5900
|
});
|
|
5764
5901
|
}
|
|
5765
5902
|
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");
|
|
5766
|
-
const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5903
|
+
const isBaseErrorFile = modBaseErrorAbs ? path26.resolve(file) === path26.resolve(modBaseErrorAbs) : !isRegistryMode && path26.resolve(file) === path26.resolve(baseErrorAbs);
|
|
5767
5904
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5768
5905
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5769
5906
|
let classMatch;
|
|
@@ -5785,10 +5922,10 @@ Code: ${v.code}`;
|
|
|
5785
5922
|
code: lineCode,
|
|
5786
5923
|
ruleId: "E4_MISSING_INHERITANCE",
|
|
5787
5924
|
severity: "error",
|
|
5788
|
-
advice: `Error class '${className}' must extend '${
|
|
5925
|
+
advice: isRegistryMode ? `Error class '${className}' in registry ${mod.type} '${mod.name}' must extend '${modBaseErrorClass}'.` : `Error class '${className}' must extend '${modBaseErrorClass}'.`,
|
|
5789
5926
|
fixExample: {
|
|
5790
5927
|
bad: lineCode,
|
|
5791
|
-
good: `export class ${className} extends ${
|
|
5928
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5792
5929
|
}
|
|
5793
5930
|
});
|
|
5794
5931
|
} else if (extendsName === "Error") {
|
|
@@ -5802,10 +5939,10 @@ Code: ${v.code}`;
|
|
|
5802
5939
|
code: lineCode,
|
|
5803
5940
|
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5804
5941
|
severity: "error",
|
|
5805
|
-
advice: `Error class '${className}' extends native 'Error'. It must extend your configured base error class ('${
|
|
5942
|
+
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}').`,
|
|
5806
5943
|
fixExample: {
|
|
5807
5944
|
bad: lineCode,
|
|
5808
|
-
good: `export class ${className} extends ${
|
|
5945
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5809
5946
|
}
|
|
5810
5947
|
});
|
|
5811
5948
|
}
|
|
@@ -6565,7 +6702,9 @@ async function handleLint(flags = {}) {
|
|
|
6565
6702
|
targetDir: flags.targetDir || flags.target,
|
|
6566
6703
|
projectRoot: flags.projectRoot,
|
|
6567
6704
|
fixHint: flags.fixHint !== false,
|
|
6568
|
-
throwOnError: false
|
|
6705
|
+
throwOnError: false,
|
|
6706
|
+
mode: flags.mode || (flags.registry ? "registry" : void 0),
|
|
6707
|
+
registry: Boolean(flags.registry)
|
|
6569
6708
|
};
|
|
6570
6709
|
try {
|
|
6571
6710
|
const summary = lintCodebase(options);
|