@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/index.js
CHANGED
|
@@ -191,20 +191,20 @@ function getConfiguredSharedDir(projectRoot, flags) {
|
|
|
191
191
|
return path26.join(projectRoot, "shared");
|
|
192
192
|
}
|
|
193
193
|
function loadLintConfig(projectRoot) {
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
194
|
+
const masonConfig = loadMasonConfig(projectRoot);
|
|
195
|
+
if (masonConfig) {
|
|
196
|
+
return {
|
|
197
|
+
baseErrorPath: masonConfig.paths?.baseError,
|
|
198
|
+
paths: masonConfig.paths
|
|
199
|
+
};
|
|
200
200
|
}
|
|
201
201
|
return {};
|
|
202
202
|
}
|
|
203
203
|
function getResolvedLintPaths(projectRoot, config) {
|
|
204
204
|
const masonConfig = loadMasonConfig(projectRoot);
|
|
205
|
-
const configuredFeatures = config
|
|
206
|
-
const configuredShared = config
|
|
207
|
-
const configuredErrors = config
|
|
205
|
+
const configuredFeatures = config?.paths?.features ?? masonConfig?.paths?.features;
|
|
206
|
+
const configuredShared = config?.paths?.shared ?? masonConfig?.paths?.shared;
|
|
207
|
+
const configuredErrors = config?.paths?.errors ?? masonConfig?.paths?.errors;
|
|
208
208
|
const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
|
|
209
209
|
if (configuredPath !== void 0) {
|
|
210
210
|
return path26.resolve(projectRoot, configuredPath);
|
|
@@ -4914,19 +4914,43 @@ function scanDirFiles(dir, projectRoot) {
|
|
|
4914
4914
|
}
|
|
4915
4915
|
return files;
|
|
4916
4916
|
}
|
|
4917
|
+
function resolveLintEnvironment(projectRoot) {
|
|
4918
|
+
const config = loadMasonConfig(projectRoot);
|
|
4919
|
+
const resolveDir = (configured, defaultSrc, defaultRoot) => {
|
|
4920
|
+
if (configured !== void 0) {
|
|
4921
|
+
return path26.resolve(projectRoot, configured);
|
|
4922
|
+
}
|
|
4923
|
+
const srcPath = path26.join(projectRoot, defaultSrc);
|
|
4924
|
+
if (fs17.existsSync(srcPath)) return srcPath;
|
|
4925
|
+
const rootPath = path26.join(projectRoot, defaultRoot);
|
|
4926
|
+
if (fs17.existsSync(rootPath)) return rootPath;
|
|
4927
|
+
return srcPath;
|
|
4928
|
+
};
|
|
4929
|
+
const featuresBase = resolveDir(config?.paths?.features, "src/features", "features");
|
|
4930
|
+
const sharedBase = resolveDir(config?.paths?.shared, "src/shared", "shared");
|
|
4931
|
+
const errorsBase = resolveDir(config?.paths?.errors, "src/errors", "errors");
|
|
4932
|
+
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";
|
|
4933
|
+
const baseErrorAbs = path26.resolve(
|
|
4934
|
+
projectRoot,
|
|
4935
|
+
config?.paths?.baseError || defaultBaseErrorRelative
|
|
4936
|
+
);
|
|
4937
|
+
return { featuresBase, sharedBase, errorsBase, baseErrorAbs };
|
|
4938
|
+
}
|
|
4917
4939
|
function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
|
|
4918
4940
|
if (!sharedBaseAbs || !errorsBaseAbs) {
|
|
4919
|
-
const
|
|
4920
|
-
featuresBaseAbs =
|
|
4921
|
-
sharedBaseAbs =
|
|
4922
|
-
errorsBaseAbs =
|
|
4941
|
+
const env = resolveLintEnvironment(featuresBaseAbs);
|
|
4942
|
+
featuresBaseAbs = env.featuresBase;
|
|
4943
|
+
sharedBaseAbs = env.sharedBase;
|
|
4944
|
+
errorsBaseAbs = env.errorsBase;
|
|
4923
4945
|
}
|
|
4924
4946
|
const normFullPath = path26.resolve(fullPathAbs);
|
|
4925
4947
|
const normErrorsBase = path26.resolve(errorsBaseAbs);
|
|
4926
4948
|
const normSharedBase = path26.resolve(sharedBaseAbs);
|
|
4927
4949
|
const normFeaturesBase = path26.resolve(featuresBaseAbs);
|
|
4928
|
-
const
|
|
4929
|
-
|
|
4950
|
+
const isUnder = (base, target) => {
|
|
4951
|
+
return target === base || target.startsWith(base + path26.sep);
|
|
4952
|
+
};
|
|
4953
|
+
if (isUnder(normErrorsBase, normFullPath)) {
|
|
4930
4954
|
return {
|
|
4931
4955
|
moduleType: "error",
|
|
4932
4956
|
moduleName: "global-errors",
|
|
@@ -4934,26 +4958,26 @@ function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs
|
|
|
4934
4958
|
layer: "domain"
|
|
4935
4959
|
};
|
|
4936
4960
|
}
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
const parts =
|
|
4961
|
+
if (isUnder(normSharedBase, normFullPath)) {
|
|
4962
|
+
const rel = path26.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
|
|
4963
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4940
4964
|
if (parts.length >= 1) {
|
|
4941
4965
|
const moduleName = parts[0];
|
|
4942
|
-
const
|
|
4966
|
+
const subfolder = parts[1];
|
|
4943
4967
|
let layer = "root";
|
|
4944
|
-
if (
|
|
4968
|
+
if (subfolder === "domain" || subfolder === "errors") {
|
|
4945
4969
|
layer = "domain";
|
|
4946
|
-
} else if (
|
|
4970
|
+
} else if (subfolder === "ports") {
|
|
4947
4971
|
layer = "ports";
|
|
4948
|
-
} else if (
|
|
4972
|
+
} else if (subfolder === "infrastructure") {
|
|
4949
4973
|
layer = "infrastructure";
|
|
4950
4974
|
}
|
|
4951
4975
|
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4952
4976
|
}
|
|
4953
4977
|
}
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
const parts =
|
|
4978
|
+
if (isUnder(normFeaturesBase, normFullPath)) {
|
|
4979
|
+
const rel = path26.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
|
|
4980
|
+
const parts = rel.split("/").filter(Boolean);
|
|
4957
4981
|
if (parts.length >= 2) {
|
|
4958
4982
|
const moduleName = parts[0];
|
|
4959
4983
|
const subfolder = parts[1];
|
|
@@ -4966,7 +4990,7 @@ function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs
|
|
|
4966
4990
|
layer = "services";
|
|
4967
4991
|
} else if (subfolder === "infrastructure") {
|
|
4968
4992
|
layer = "infrastructure";
|
|
4969
|
-
} else if (subfolder
|
|
4993
|
+
} else if (subfolder.startsWith("px") || subfolder === "presentation" || subfolder === "handlers" || subfolder === "transformers") {
|
|
4970
4994
|
layer = "presentation";
|
|
4971
4995
|
}
|
|
4972
4996
|
return { moduleType: "feature", moduleName, feature: moduleName, layer };
|
|
@@ -4995,10 +5019,10 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs,
|
|
|
4995
5019
|
return { isExternal: true, isDiFramework: false };
|
|
4996
5020
|
}
|
|
4997
5021
|
if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
|
|
4998
|
-
const
|
|
4999
|
-
featuresBaseAbs = featuresBaseAbs ||
|
|
5000
|
-
sharedBaseAbs = sharedBaseAbs ||
|
|
5001
|
-
errorsBaseAbs = errorsBaseAbs ||
|
|
5022
|
+
const env = resolveLintEnvironment(projectRoot);
|
|
5023
|
+
featuresBaseAbs = featuresBaseAbs || env.featuresBase;
|
|
5024
|
+
sharedBaseAbs = sharedBaseAbs || env.sharedBase;
|
|
5025
|
+
errorsBaseAbs = errorsBaseAbs || env.errorsBase;
|
|
5002
5026
|
}
|
|
5003
5027
|
const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
|
|
5004
5028
|
let targetAbs = null;
|
|
@@ -5635,45 +5659,108 @@ private ${m.name}(...) { ... }`
|
|
|
5635
5659
|
}
|
|
5636
5660
|
}
|
|
5637
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
|
+
}
|
|
5638
5706
|
function lintCodebase(options = {}) {
|
|
5639
5707
|
const startTime = Date.now();
|
|
5640
5708
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5641
5709
|
const failFast = !options.complete;
|
|
5642
|
-
const
|
|
5643
|
-
const
|
|
5644
|
-
const
|
|
5645
|
-
if (!
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5710
|
+
const effectiveMode = options.mode || getEffectiveMode(projectRoot, options.registry !== void 0 ? { registry: options.registry } : void 0);
|
|
5711
|
+
const isRegistryMode = effectiveMode === "registry";
|
|
5712
|
+
const { featuresBase, sharedBase, errorsBase, baseErrorAbs } = resolveLintEnvironment(projectRoot);
|
|
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
|
+
}
|
|
5654
5740
|
}
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
[],
|
|
5661
|
-
true
|
|
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_]+)/
|
|
5662
5746
|
);
|
|
5747
|
+
baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5663
5748
|
}
|
|
5664
|
-
const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
|
|
5665
|
-
const baseClassMatch = baseErrorContent.match(
|
|
5666
|
-
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5667
|
-
);
|
|
5668
|
-
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5669
5749
|
const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
|
|
5670
5750
|
const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
|
|
5671
|
-
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5672
5751
|
const allModules = [
|
|
5673
5752
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5674
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5675
|
-
errorModule
|
|
5753
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5676
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
|
+
}
|
|
5677
5764
|
const graph = new FeatureDependencyGraph();
|
|
5678
5765
|
const allViolations = [];
|
|
5679
5766
|
const results = [];
|
|
@@ -5707,6 +5794,77 @@ Code: ${v.code}`;
|
|
|
5707
5794
|
}
|
|
5708
5795
|
for (const mod of allModules) {
|
|
5709
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
|
+
}
|
|
5710
5868
|
const allFiles = scanDirFiles(mod.dir, projectRoot);
|
|
5711
5869
|
modResult.scannedFilesCount = allFiles.length;
|
|
5712
5870
|
for (const file of allFiles) {
|
|
@@ -5734,15 +5892,15 @@ Code: ${v.code}`;
|
|
|
5734
5892
|
code: lineCode,
|
|
5735
5893
|
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5736
5894
|
severity: "error",
|
|
5737
|
-
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.`,
|
|
5738
5896
|
fixExample: {
|
|
5739
5897
|
bad: lineCode,
|
|
5740
|
-
good: `throw new ${
|
|
5898
|
+
good: `throw new ${modBaseErrorClass}("Something went wrong");`
|
|
5741
5899
|
}
|
|
5742
5900
|
});
|
|
5743
5901
|
}
|
|
5744
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");
|
|
5745
|
-
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);
|
|
5746
5904
|
if (isErrorFile && !isBaseErrorFile) {
|
|
5747
5905
|
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5748
5906
|
let classMatch;
|
|
@@ -5764,10 +5922,10 @@ Code: ${v.code}`;
|
|
|
5764
5922
|
code: lineCode,
|
|
5765
5923
|
ruleId: "E4_MISSING_INHERITANCE",
|
|
5766
5924
|
severity: "error",
|
|
5767
|
-
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}'.`,
|
|
5768
5926
|
fixExample: {
|
|
5769
5927
|
bad: lineCode,
|
|
5770
|
-
good: `export class ${className} extends ${
|
|
5928
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5771
5929
|
}
|
|
5772
5930
|
});
|
|
5773
5931
|
} else if (extendsName === "Error") {
|
|
@@ -5781,10 +5939,10 @@ Code: ${v.code}`;
|
|
|
5781
5939
|
code: lineCode,
|
|
5782
5940
|
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5783
5941
|
severity: "error",
|
|
5784
|
-
advice: `Error class '${className}' extends
|
|
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}').`,
|
|
5785
5943
|
fixExample: {
|
|
5786
5944
|
bad: lineCode,
|
|
5787
|
-
good: `export class ${className} extends ${
|
|
5945
|
+
good: `export class ${className} extends ${modBaseErrorClass} { ... }`
|
|
5788
5946
|
}
|
|
5789
5947
|
});
|
|
5790
5948
|
}
|
|
@@ -6544,7 +6702,9 @@ async function handleLint(flags = {}) {
|
|
|
6544
6702
|
targetDir: flags.targetDir || flags.target,
|
|
6545
6703
|
projectRoot: flags.projectRoot,
|
|
6546
6704
|
fixHint: flags.fixHint !== false,
|
|
6547
|
-
throwOnError: false
|
|
6705
|
+
throwOnError: false,
|
|
6706
|
+
mode: flags.mode || (flags.registry ? "registry" : void 0),
|
|
6707
|
+
registry: Boolean(flags.registry)
|
|
6548
6708
|
};
|
|
6549
6709
|
try {
|
|
6550
6710
|
const summary = lintCodebase(options);
|