@solid-stack/mason 1.0.34 → 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 +216 -87
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +216 -87
- package/dist/bin.js.map +1 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/core/lintEngine.d.ts +15 -0
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +225 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +225 -65
- 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 +8 -5
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +10 -8
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
- package/schema.json +7 -13
package/dist/bin.js
CHANGED
|
@@ -189,37 +189,6 @@ function getConfiguredSharedDir(projectRoot, flags) {
|
|
|
189
189
|
}
|
|
190
190
|
return path26.join(projectRoot, "shared");
|
|
191
191
|
}
|
|
192
|
-
function loadLintConfig(projectRoot) {
|
|
193
|
-
const configPath = path26.join(projectRoot, "mason-lint.config.json");
|
|
194
|
-
if (fs17.existsSync(configPath)) {
|
|
195
|
-
try {
|
|
196
|
-
return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return {};
|
|
201
|
-
}
|
|
202
|
-
function getResolvedLintPaths(projectRoot, config) {
|
|
203
|
-
const masonConfig = loadMasonConfig(projectRoot);
|
|
204
|
-
const configuredFeatures = config.paths?.features ?? masonConfig?.paths?.features;
|
|
205
|
-
const configuredShared = config.paths?.shared ?? masonConfig?.paths?.shared;
|
|
206
|
-
const configuredErrors = config.paths?.errors;
|
|
207
|
-
const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
|
|
208
|
-
if (configuredPath !== void 0) {
|
|
209
|
-
return path26.resolve(projectRoot, configuredPath);
|
|
210
|
-
}
|
|
211
|
-
const srcPath = path26.join(projectRoot, defaultSrc);
|
|
212
|
-
if (fs17.existsSync(srcPath)) return srcPath;
|
|
213
|
-
const rootPath = path26.join(projectRoot, defaultRoot);
|
|
214
|
-
if (fs17.existsSync(rootPath)) return rootPath;
|
|
215
|
-
return srcPath;
|
|
216
|
-
};
|
|
217
|
-
return {
|
|
218
|
-
featuresBase: resolveBase(configuredFeatures, "src/features", "features"),
|
|
219
|
-
sharedBase: resolveBase(configuredShared, "src/shared", "shared"),
|
|
220
|
-
errorsBase: resolveBase(configuredErrors, "src/errors", "errors")
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
192
|
|
|
224
193
|
// src/utils/project.ts
|
|
225
194
|
function findProjectRoot(startDir = process.cwd()) {
|
|
@@ -4905,19 +4874,43 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4905
4874
|
}
|
|
4906
4875
|
return files;
|
|
4907
4876
|
}
|
|
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
|
+
}
|
|
4908
4899
|
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4909
4900
|
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4910
|
-
const
|
|
4911
|
-
featuresBaseAbs =
|
|
4912
|
-
sharedBaseAbs =
|
|
4913
|
-
errorsBaseAbs =
|
|
4901
|
+
const env = resolveLintEnvironment(featuresBaseAbs);
|
|
4902
|
+
featuresBaseAbs = env.featuresBase;
|
|
4903
|
+
sharedBaseAbs = env.sharedBase;
|
|
4904
|
+
errorsBaseAbs = env.errorsBase;
|
|
4914
4905
|
}
|
|
4915
4906
|
const normFullPath = path26.resolve(fullPathAbs);
|
|
4916
4907
|
const normErrorsBase = path26.resolve(errorsBaseAbs);
|
|
4917
4908
|
const normSharedBase = path26.resolve(sharedBaseAbs);
|
|
4918
4909
|
const normFeaturesBase = path26.resolve(featuresBaseAbs);
|
|
4919
|
-
const
|
|
4920
|
-
|
|
4910
|
+
const isUnder = (base, target) => {
|
|
4911
|
+
return target === base || target.startsWith(base + path26.sep);
|
|
4912
|
+
};
|
|
4913
|
+
if (isUnder(normErrorsBase, normFullPath)) {
|
|
4921
4914
|
return {
|
|
4922
4915
|
moduleType: "error",
|
|
4923
4916
|
moduleName: "global-errors",
|
|
@@ -4925,26 +4918,26 @@ function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs
|
|
|
4925
4918
|
layer: "domain"
|
|
4926
4919
|
};
|
|
4927
4920
|
}
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
const parts =
|
|
4921
|
+
if (isUnder(normSharedBase, normFullPath)) {
|
|
4922
|
+
const rel = path26.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4923
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4931
4924
|
if (parts.length >= 1) {
|
|
4932
4925
|
const moduleName = parts[0];
|
|
4933
|
-
const
|
|
4926
|
+
const subfolder = parts[1];
|
|
4934
4927
|
let layer = "root";
|
|
4935
|
-
if (
|
|
4928
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4936
4929
|
layer = "domain";
|
|
4937
|
-
} else if (
|
|
4930
|
+
} else if (subfolder === "ports") {
|
|
4938
4931
|
layer = "ports";
|
|
4939
|
-
} else if (
|
|
4932
|
+
} else if (subfolder === "infrastructure") {
|
|
4940
4933
|
layer = "infrastructure";
|
|
4941
4934
|
}
|
|
4942
4935
|
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4943
4936
|
}
|
|
4944
4937
|
}
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
const parts =
|
|
4938
|
+
if (isUnder(normFeaturesBase, normFullPath)) {
|
|
4939
|
+
const rel = path26.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4940
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4948
4941
|
if (parts.length >= 2) {
|
|
4949
4942
|
const moduleName = parts[0];
|
|
4950
4943
|
const subfolder = parts[1];
|
|
@@ -4957,7 +4950,7 @@ function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs
|
|
|
4957
4950
|
layer = "services";
|
|
4958
4951
|
} else if (subfolder === "infrastructure") {
|
|
4959
4952
|
layer = "infrastructure";
|
|
4960
|
-
} else if (subfolder
|
|
4953
|
+
} else if (subfolder.startsWith("px") || subfolder === "presentation" || subfolder === "handlers" || subfolder === "transformers") {
|
|
4961
4954
|
layer = "presentation";
|
|
4962
4955
|
}
|
|
4963
4956
|
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
@@ -4986,10 +4979,10 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs,
|
|
|
4986
4979
|
return { isExternal: true, isDiFramework: false };
|
|
4987
4980
|
}
|
|
4988
4981
|
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
4989
|
-
const
|
|
4990
|
-
featuresBaseAbs = featuresBaseAbs ||
|
|
4991
|
-
sharedBaseAbs = sharedBaseAbs ||
|
|
4992
|
-
errorsBaseAbs = errorsBaseAbs ||
|
|
4982
|
+
const env = resolveLintEnvironment(projectRoot);
|
|
4983
|
+
featuresBaseAbs = featuresBaseAbs || env.featuresBase;
|
|
4984
|
+
sharedBaseAbs = sharedBaseAbs || env.sharedBase;
|
|
4985
|
+
errorsBaseAbs = errorsBaseAbs || env.errorsBase;
|
|
4993
4986
|
}
|
|
4994
4987
|
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
4995
4988
|
let targetAbs = null;
|
|
@@ -5626,45 +5619,108 @@ private ${m.name}(...) { ... }`
|
|
|
5626
5619
|
}
|
|
5627
5620
|
}
|
|
5628
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
|
+
}
|
|
5629
5666
|
function lintCodebase(options = {}) {
|
|
5630
5667
|
const startTime = Date.now();
|
|
5631
5668
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5632
5669
|
const failFast = !options.complete;
|
|
5633
|
-
const
|
|
5634
|
-
const
|
|
5635
|
-
const
|
|
5636
|
-
if (!
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5670
|
+
const effectiveMode = options.mode || getEffectiveMode(projectRoot, options.registry !== void 0 ? { registry: options.registry } : void 0);
|
|
5671
|
+
const isRegistryMode = effectiveMode === "registry";
|
|
5672
|
+
const { featuresBase, sharedBase, errorsBase, baseErrorAbs } = resolveLintEnvironment(projectRoot);
|
|
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
|
+
}
|
|
5645
5700
|
}
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
[],
|
|
5652
|
-
true
|
|
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_]+)/
|
|
5653
5706
|
);
|
|
5707
|
+
baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5654
5708
|
}
|
|
5655
|
-
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5656
|
-
const baseClassMatch = baseErrorContent.match(
|
|
5657
|
-
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5658
|
-
);
|
|
5659
|
-
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5660
5709
|
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5661
5710
|
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5662
|
-
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5663
5711
|
const allModules = [
|
|
5664
5712
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5665
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5666
|
-
errorModule
|
|
5713
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5667
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
|
+
}
|
|
5668
5724
|
const graph = new FeatureDependencyGraph();
|
|
5669
5725
|
const allViolations = [];
|
|
5670
5726
|
const results = [];
|
|
@@ -5698,6 +5754,77 @@ Code: ${v.code}`;
|
|
|
5698
5754
|
}
|
|
5699
5755
|
for (const mod of allModules) {
|
|
5700
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
|
+
}
|
|
5701
5828
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5702
5829
|
modResult.scannedFilesCount = allFiles.length;
|
|
5703
5830
|
for (const file of allFiles) {
|
|
@@ -5725,15 +5852,15 @@ Code: ${v.code}`;
|
|
|
5725
5852
|
code: lineCode,
|
|
5726
5853
|
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5727
5854
|
severity: "error",
|
|
5728
|
-
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.`,
|
|
5729
5856
|
fixExample: {
|
|
5730
5857
|
bad: lineCode,
|
|
5731
|
-
good: `throw new ${
|
|
5858
|
+
good: `throw new ${modBaseErrorClass}("Something went wrong");`
|
|
5732
5859
|
}
|
|
5733
5860
|
});
|
|
5734
5861
|
}
|
|
5735
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");
|
|
5736
|
-
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);
|
|
5737
5864
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5738
5865
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5739
5866
|
let classMatch;
|
|
@@ -5755,10 +5882,10 @@ Code: ${v.code}`;
|
|
|
5755
5882
|
code: lineCode,
|
|
5756
5883
|
ruleId: "E4_MISSING_INHERITANCE",
|
|
5757
5884
|
severity: "error",
|
|
5758
|
-
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}'.`,
|
|
5759
5886
|
fixExample: {
|
|
5760
5887
|
bad: lineCode,
|
|
5761
|
-
good: `export class ${className} extends ${
|
|
5888
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5762
5889
|
}
|
|
5763
5890
|
});
|
|
5764
5891
|
} else if (extendsName === "Error") {
|
|
@@ -5772,10 +5899,10 @@ Code: ${v.code}`;
|
|
|
5772
5899
|
code: lineCode,
|
|
5773
5900
|
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5774
5901
|
severity: "error",
|
|
5775
|
-
advice: `Error class '${className}' extends
|
|
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}').`,
|
|
5776
5903
|
fixExample: {
|
|
5777
5904
|
bad: lineCode,
|
|
5778
|
-
good: `export class ${className} extends ${
|
|
5905
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5779
5906
|
}
|
|
5780
5907
|
});
|
|
5781
5908
|
}
|
|
@@ -6535,7 +6662,9 @@ async function handleLint(flags = {}) {
|
|
|
6535
6662
|
targetDir: flags.targetDir || flags.target,
|
|
6536
6663
|
projectRoot: flags.projectRoot,
|
|
6537
6664
|
fixHint: flags.fixHint !== false,
|
|
6538
|
-
throwOnError: false
|
|
6665
|
+
throwOnError: false,
|
|
6666
|
+
mode: flags.mode || (flags.registry ? "registry" : void 0),
|
|
6667
|
+
registry: Boolean(flags.registry)
|
|
6539
6668
|
};
|
|
6540
6669
|
try {
|
|
6541
6670
|
const summary = lintCodebase(options);
|