@solid-stack/mason 1.0.32 → 1.0.33
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 +184 -3
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +184 -3
- package/dist/bin.js.map +1 -1
- package/dist/core/lintEngine.d.ts +1 -0
- package/dist/core/lintEngine.d.ts.map +1 -1
- package/dist/index.cjs +185 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +185 -4
- package/dist/index.js.map +1 -1
- package/dist/types/lint.d.ts +5 -2
- package/dist/types/lint.d.ts.map +1 -1
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -190,6 +190,16 @@ function getConfiguredSharedDir(projectRoot, flags) {
|
|
|
190
190
|
}
|
|
191
191
|
return path2.join(projectRoot, "shared");
|
|
192
192
|
}
|
|
193
|
+
function loadLintConfig(projectRoot) {
|
|
194
|
+
const configPath = path2.join(projectRoot, "mason-lint.config.json");
|
|
195
|
+
if (fs4.existsSync(configPath)) {
|
|
196
|
+
try {
|
|
197
|
+
return JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { baseErrorPath: "src/errors/AppError.ts" };
|
|
202
|
+
}
|
|
193
203
|
|
|
194
204
|
// src/utils/project.ts
|
|
195
205
|
function findProjectRoot(startDir = process.cwd()) {
|
|
@@ -4917,6 +4927,15 @@ function classifyFile(fullPath, projectRoot) {
|
|
|
4917
4927
|
}
|
|
4918
4928
|
return { moduleType: "shared", moduleName, feature: moduleName, layer };
|
|
4919
4929
|
}
|
|
4930
|
+
const errorMatch = rel.match(/(?:^|\/)(?:src\/)?errors(?:\/(.*))?$/);
|
|
4931
|
+
if (errorMatch) {
|
|
4932
|
+
return {
|
|
4933
|
+
moduleType: "error",
|
|
4934
|
+
moduleName: "global-errors",
|
|
4935
|
+
feature: "global-errors",
|
|
4936
|
+
layer: "domain"
|
|
4937
|
+
};
|
|
4938
|
+
}
|
|
4920
4939
|
return {};
|
|
4921
4940
|
}
|
|
4922
4941
|
function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
@@ -4942,15 +4961,28 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
|
|
|
4942
4961
|
resolvedRel = specifier.replace(/^[@~]\//, "src/");
|
|
4943
4962
|
} else if (specifier === "@configs" || specifier.startsWith("@configs/")) {
|
|
4944
4963
|
resolvedRel = "src/" + specifier.slice(1);
|
|
4964
|
+
} else if (specifier.startsWith("@/errors/") || specifier.startsWith("~/errors/") || specifier === "@/errors" || specifier === "~/errors") {
|
|
4965
|
+
resolvedRel = specifier.replace(/^[@~]\//, "src/");
|
|
4966
|
+
} else if (specifier === "@errors" || specifier.startsWith("@errors/")) {
|
|
4967
|
+
resolvedRel = "src/" + specifier.slice(1);
|
|
4945
4968
|
} else if (specifier.startsWith("@/") || specifier.startsWith("~/")) {
|
|
4946
4969
|
resolvedRel = "src/" + specifier.slice(2);
|
|
4947
|
-
} else if (specifier.startsWith("features/") || specifier.startsWith("shared/") || specifier.startsWith("configs/") || specifier === "configs") {
|
|
4970
|
+
} else if (specifier.startsWith("features/") || specifier.startsWith("shared/") || specifier.startsWith("configs/") || specifier === "configs" || specifier.startsWith("errors/") || specifier === "errors") {
|
|
4948
4971
|
resolvedRel = specifier;
|
|
4949
4972
|
}
|
|
4950
4973
|
if (!resolvedRel) {
|
|
4951
4974
|
return { isExternal: true, isDiFramework: false };
|
|
4952
4975
|
}
|
|
4953
4976
|
const isGlobalConfig = resolvedRel.startsWith("src/configs/") || resolvedRel === "src/configs" || resolvedRel.startsWith("configs/") || resolvedRel === "configs";
|
|
4977
|
+
const isGlobalError = resolvedRel.startsWith("src/errors/") || resolvedRel === "src/errors" || resolvedRel.startsWith("errors/") || resolvedRel === "errors";
|
|
4978
|
+
if (isGlobalError) {
|
|
4979
|
+
return {
|
|
4980
|
+
isExternal: false,
|
|
4981
|
+
isDiFramework: false,
|
|
4982
|
+
isGlobalError: true,
|
|
4983
|
+
isTargetError: true
|
|
4984
|
+
};
|
|
4985
|
+
}
|
|
4954
4986
|
const featMatch = resolvedRel.match(/(?:^|\/)(?:src\/)?features\/([^/]+)\/([^/]+)(?:\/(.*))?$/);
|
|
4955
4987
|
if (featMatch && featMatch[1] && featMatch[2]) {
|
|
4956
4988
|
const targetFeature = featMatch[1];
|
|
@@ -5491,11 +5523,45 @@ function lintCodebase(options = {}) {
|
|
|
5491
5523
|
const startTime = Date.now();
|
|
5492
5524
|
const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
|
|
5493
5525
|
const failFast = !options.complete;
|
|
5526
|
+
const lintConfig = loadLintConfig(projectRoot);
|
|
5527
|
+
const baseErrorRelative = lintConfig.baseErrorPath || "src/errors/AppError.ts";
|
|
5528
|
+
const baseErrorAbs = path2.resolve(projectRoot, baseErrorRelative);
|
|
5529
|
+
const featuresBase = resolveFeaturesDir(projectRoot);
|
|
5530
|
+
const sharedBase = resolveSharedDir(projectRoot);
|
|
5531
|
+
let errorsBase = path2.join(projectRoot, "src/errors");
|
|
5532
|
+
if (!fs4.existsSync(errorsBase) && fs4.existsSync(path2.join(projectRoot, "errors"))) {
|
|
5533
|
+
errorsBase = path2.join(projectRoot, "errors");
|
|
5534
|
+
}
|
|
5535
|
+
const missingFoundations = [];
|
|
5536
|
+
if (!fs4.existsSync(featuresBase)) missingFoundations.push("features/");
|
|
5537
|
+
if (!fs4.existsSync(sharedBase)) missingFoundations.push("shared/");
|
|
5538
|
+
if (!fs4.existsSync(errorsBase)) missingFoundations.push("errors/");
|
|
5539
|
+
if (missingFoundations.length > 0) {
|
|
5540
|
+
throw new DependencyRuleError(
|
|
5541
|
+
`Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
|
|
5542
|
+
[],
|
|
5543
|
+
true
|
|
5544
|
+
);
|
|
5545
|
+
}
|
|
5546
|
+
if (!fs4.existsSync(baseErrorAbs)) {
|
|
5547
|
+
throw new DependencyRuleError(
|
|
5548
|
+
`Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
|
|
5549
|
+
[],
|
|
5550
|
+
true
|
|
5551
|
+
);
|
|
5552
|
+
}
|
|
5553
|
+
const baseErrorContent = fs4.readFileSync(baseErrorAbs, "utf-8");
|
|
5554
|
+
const baseClassMatch = baseErrorContent.match(
|
|
5555
|
+
/(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
|
|
5556
|
+
);
|
|
5557
|
+
const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
|
|
5494
5558
|
const features = discoverAllFeatures(projectRoot);
|
|
5495
5559
|
const sharedModules = discoverAllShared(projectRoot);
|
|
5560
|
+
const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
|
|
5496
5561
|
const allModules = [
|
|
5497
5562
|
...features.map((f) => ({ ...f, type: "feature" })),
|
|
5498
|
-
...sharedModules.map((s) => ({ ...s, type: "shared" }))
|
|
5563
|
+
...sharedModules.map((s) => ({ ...s, type: "shared" })),
|
|
5564
|
+
errorModule
|
|
5499
5565
|
];
|
|
5500
5566
|
const graph = new FeatureDependencyGraph();
|
|
5501
5567
|
const allViolations = [];
|
|
@@ -5525,7 +5591,7 @@ Code: ${v.code}`;
|
|
|
5525
5591
|
scannedFilesCount: 0,
|
|
5526
5592
|
passed: true,
|
|
5527
5593
|
violations: [],
|
|
5528
|
-
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : ["domain", "ports", "infrastructure", "root"]
|
|
5594
|
+
layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : mod.type === "shared" ? ["domain", "ports", "infrastructure", "root"] : ["domain"]
|
|
5529
5595
|
});
|
|
5530
5596
|
}
|
|
5531
5597
|
for (const mod of allModules) {
|
|
@@ -5540,6 +5606,121 @@ Code: ${v.code}`;
|
|
|
5540
5606
|
const statements = extractImportStatements(content);
|
|
5541
5607
|
const baseName = path2.basename(file);
|
|
5542
5608
|
const isBarrel = baseName === "index.ts" || baseName === "index.js";
|
|
5609
|
+
const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
|
|
5610
|
+
let throwMatch;
|
|
5611
|
+
while ((throwMatch = throwErrorRegex.exec(content)) !== null) {
|
|
5612
|
+
const charIndex = throwMatch.index;
|
|
5613
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5614
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || throwMatch[0];
|
|
5615
|
+
if (lineCode.startsWith("//")) continue;
|
|
5616
|
+
addViolation({
|
|
5617
|
+
feature: moduleName,
|
|
5618
|
+
layer,
|
|
5619
|
+
file,
|
|
5620
|
+
relativePath: relPath,
|
|
5621
|
+
line: lineNum,
|
|
5622
|
+
category: "Plain Error Throw Violation",
|
|
5623
|
+
code: lineCode,
|
|
5624
|
+
ruleId: "E3_PLAIN_ERROR_THROW",
|
|
5625
|
+
severity: "error",
|
|
5626
|
+
advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a domain-specific error class that inherits from it.`,
|
|
5627
|
+
fixExample: {
|
|
5628
|
+
bad: lineCode,
|
|
5629
|
+
good: `throw new ${baseErrorClassName}("Something went wrong");`
|
|
5630
|
+
}
|
|
5631
|
+
});
|
|
5632
|
+
}
|
|
5633
|
+
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");
|
|
5634
|
+
const isBaseErrorFile = path2.resolve(file) === path2.resolve(baseErrorAbs);
|
|
5635
|
+
if (isErrorFile && !isBaseErrorFile) {
|
|
5636
|
+
const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
|
|
5637
|
+
let classMatch;
|
|
5638
|
+
while ((classMatch = classRegex.exec(content)) !== null) {
|
|
5639
|
+
const className = classMatch[1];
|
|
5640
|
+
const extendsName = classMatch[2];
|
|
5641
|
+
const charIndex = classMatch.index;
|
|
5642
|
+
const lineNum = content.substring(0, charIndex).split("\n").length;
|
|
5643
|
+
const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || classMatch[0];
|
|
5644
|
+
if (lineCode.startsWith("//")) continue;
|
|
5645
|
+
if (!extendsName) {
|
|
5646
|
+
addViolation({
|
|
5647
|
+
feature: moduleName,
|
|
5648
|
+
layer,
|
|
5649
|
+
file,
|
|
5650
|
+
relativePath: relPath,
|
|
5651
|
+
line: lineNum,
|
|
5652
|
+
category: "Error Inheritance Violation",
|
|
5653
|
+
code: lineCode,
|
|
5654
|
+
ruleId: "E4_MISSING_INHERITANCE",
|
|
5655
|
+
severity: "error",
|
|
5656
|
+
advice: `Error class '${className}' must extend '${baseErrorClassName}' (or another class that derives from it).`,
|
|
5657
|
+
fixExample: {
|
|
5658
|
+
bad: lineCode,
|
|
5659
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5660
|
+
}
|
|
5661
|
+
});
|
|
5662
|
+
} else if (extendsName === "Error") {
|
|
5663
|
+
addViolation({
|
|
5664
|
+
feature: moduleName,
|
|
5665
|
+
layer,
|
|
5666
|
+
file,
|
|
5667
|
+
relativePath: relPath,
|
|
5668
|
+
line: lineNum,
|
|
5669
|
+
category: "Error Inheritance Violation",
|
|
5670
|
+
code: lineCode,
|
|
5671
|
+
ruleId: "E5_NATIVE_ERROR_INHERITANCE",
|
|
5672
|
+
severity: "error",
|
|
5673
|
+
advice: `Error class '${className}' extends the native 'Error'. It must extend your base error class ('${baseErrorClassName}').`,
|
|
5674
|
+
fixExample: {
|
|
5675
|
+
bad: lineCode,
|
|
5676
|
+
good: `export class ${className} extends ${baseErrorClassName} { ... }`
|
|
5677
|
+
}
|
|
5678
|
+
});
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
if (moduleType === "error") {
|
|
5683
|
+
for (const stmt of statements) {
|
|
5684
|
+
const target = classifyTarget(stmt.specifier, file, projectRoot);
|
|
5685
|
+
if (target.targetFeature) {
|
|
5686
|
+
addViolation({
|
|
5687
|
+
feature: "global-errors",
|
|
5688
|
+
layer: "domain",
|
|
5689
|
+
file,
|
|
5690
|
+
relativePath: relPath,
|
|
5691
|
+
line: stmt.line,
|
|
5692
|
+
category: "Global Error Purity Violation",
|
|
5693
|
+
code: stmt.rawCode,
|
|
5694
|
+
ruleId: "E1_FEATURE_DEPENDENCY",
|
|
5695
|
+
severity: "error",
|
|
5696
|
+
advice: `Global errors must not depend on features ('${target.targetFeature}'). Global errors represent pure domain or system states.`,
|
|
5697
|
+
fixExample: {
|
|
5698
|
+
bad: stmt.rawCode,
|
|
5699
|
+
good: "// Global errors must not reference specific features. Keep error types pure."
|
|
5700
|
+
}
|
|
5701
|
+
});
|
|
5702
|
+
}
|
|
5703
|
+
if (target.targetSharedModule && !target.isSharedDomain) {
|
|
5704
|
+
addViolation({
|
|
5705
|
+
feature: "global-errors",
|
|
5706
|
+
layer: "domain",
|
|
5707
|
+
file,
|
|
5708
|
+
relativePath: relPath,
|
|
5709
|
+
line: stmt.line,
|
|
5710
|
+
category: "Global Error Purity Violation",
|
|
5711
|
+
code: stmt.rawCode,
|
|
5712
|
+
ruleId: "E2_SHARED_NON_DOMAIN",
|
|
5713
|
+
severity: "error",
|
|
5714
|
+
advice: `Global errors can only depend on shared domain elements (e.g., value objects). They cannot depend on shared ports or infrastructure.`,
|
|
5715
|
+
fixExample: {
|
|
5716
|
+
bad: stmt.rawCode,
|
|
5717
|
+
good: "// Depend only on pure domain types from shared modules."
|
|
5718
|
+
}
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
continue;
|
|
5723
|
+
}
|
|
5543
5724
|
if (moduleType === "shared") {
|
|
5544
5725
|
if (layer === "domain") {
|
|
5545
5726
|
const lines = content.split(/\r?\n/);
|
|
@@ -6562,6 +6743,6 @@ async function runCli(argv = process.argv) {
|
|
|
6562
6743
|
cli.parse(argv, { run: true });
|
|
6563
6744
|
}
|
|
6564
6745
|
|
|
6565
|
-
export { DEFAULT_REGISTRY_BASE, DependencyRuleError, EnvEditor, FeatureDependencyGraph, PromptCancelledError, computeDirectoryHash, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, discoverEnvironments, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateShared, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getAllFiles, getConfiguredFeaturesDir, getConfiguredSharedDir, getEffectiveMode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleEnv, handleImport, handleInit, handleLint, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, lintCodebase, listDomainEntities, listDomainInterfaces, listExistingFeatures, listExistingShared, listUseCases, loadInterfaceHierarchy, loadMasonConfig, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveEnvFilename, resolveFeaturesDir, resolveSharedDir, runCli, runEnvAdd, runEnvAudit, runEnvCommand, runEnvDelete, runEnvExamplesCommand, runEnvInit, runEnvInitExamples, runEnvMenu, runEnvUpdate, runSharedCommand, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
|
|
6746
|
+
export { DEFAULT_REGISTRY_BASE, DependencyRuleError, EnvEditor, FeatureDependencyGraph, PromptCancelledError, computeDirectoryHash, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, discoverEnvironments, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateShared, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getAllFiles, getConfiguredFeaturesDir, getConfiguredSharedDir, getEffectiveMode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleEnv, handleImport, handleInit, handleLint, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, lintCodebase, listDomainEntities, listDomainInterfaces, listExistingFeatures, listExistingShared, listUseCases, loadInterfaceHierarchy, loadLintConfig, loadMasonConfig, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveEnvFilename, resolveFeaturesDir, resolveSharedDir, runCli, runEnvAdd, runEnvAudit, runEnvCommand, runEnvDelete, runEnvExamplesCommand, runEnvInit, runEnvInitExamples, runEnvMenu, runEnvUpdate, runSharedCommand, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
|
|
6566
6747
|
//# sourceMappingURL=index.js.map
|
|
6567
6748
|
//# sourceMappingURL=index.js.map
|