ts-openapi-codegen 2.1.0-beta.6 → 2.1.0-beta.7
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/cli/analyzeDiff/__tests__/analyzeDiff.cli.test.js +9 -0
- package/dist/cli/analyzeDiff/__tests__/analyzeDiffRenameAndInvalidRegex.test.js +9 -0
- package/dist/cli/analyzeDiff/__tests__/analyzeDiffTypeCoercion.test.js +9 -0
- package/dist/cli/analyzeUsage/analyzeUsage.d.ts +4 -0
- package/dist/cli/analyzeUsage/analyzeUsage.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/analyzeUsage.js +62 -0
- package/dist/cli/analyzeUsage/core/Analyzer.d.ts +9 -0
- package/dist/cli/analyzeUsage/core/Analyzer.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/core/Analyzer.js +29 -0
- package/dist/cli/analyzeUsage/core/ProjectContext.d.ts +10 -0
- package/dist/cli/analyzeUsage/core/ProjectContext.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/core/ProjectContext.js +63 -0
- package/dist/cli/analyzeUsage/core/Scanner.d.ts +15 -0
- package/dist/cli/analyzeUsage/core/Scanner.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/core/Scanner.js +115 -0
- package/dist/cli/analyzeUsage/rules/ClientRule.d.ts +6 -0
- package/dist/cli/analyzeUsage/rules/ClientRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/ClientRule.js +78 -0
- package/dist/cli/analyzeUsage/rules/CoverageRule.d.ts +7 -0
- package/dist/cli/analyzeUsage/rules/CoverageRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/CoverageRule.js +63 -0
- package/dist/cli/analyzeUsage/rules/DiagnosticsRule.d.ts +6 -0
- package/dist/cli/analyzeUsage/rules/DiagnosticsRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/DiagnosticsRule.js +60 -0
- package/dist/cli/analyzeUsage/rules/ImportRule.d.ts +6 -0
- package/dist/cli/analyzeUsage/rules/ImportRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/ImportRule.js +52 -0
- package/dist/cli/analyzeUsage/rules/ModelRule.d.ts +6 -0
- package/dist/cli/analyzeUsage/rules/ModelRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/ModelRule.js +38 -0
- package/dist/cli/analyzeUsage/rules/SchemaRule.d.ts +6 -0
- package/dist/cli/analyzeUsage/rules/SchemaRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/SchemaRule.js +48 -0
- package/dist/cli/analyzeUsage/rules/ServiceRule.d.ts +7 -0
- package/dist/cli/analyzeUsage/rules/ServiceRule.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/rules/ServiceRule.js +92 -0
- package/dist/cli/analyzeUsage/types.d.ts +52 -0
- package/dist/cli/analyzeUsage/types.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/types.js +2 -0
- package/dist/cli/analyzeUsage/utils/fuzzy.d.ts +5 -0
- package/dist/cli/analyzeUsage/utils/fuzzy.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/utils/fuzzy.js +38 -0
- package/dist/cli/analyzeUsage/utils/report.d.ts +16 -0
- package/dist/cli/analyzeUsage/utils/report.d.ts.map +1 -0
- package/dist/cli/analyzeUsage/utils/report.js +85 -0
- package/dist/cli/checkAndUpdateConfig/__tests__/checkConfig.test.js +10 -20
- package/dist/cli/checkAndUpdateConfig/__tests__/updateConfig.test.js +10 -20
- package/dist/cli/checkAndUpdateConfig/checkConfig.d.ts +2 -1
- package/dist/cli/checkAndUpdateConfig/checkConfig.d.ts.map +1 -1
- package/dist/cli/checkAndUpdateConfig/checkConfig.js +8 -4
- package/dist/cli/checkAndUpdateConfig/updateConfig.d.ts +2 -1
- package/dist/cli/checkAndUpdateConfig/updateConfig.d.ts.map +1 -1
- package/dist/cli/checkAndUpdateConfig/updateConfig.js +6 -3
- package/dist/cli/checkAndUpdateConfig/utils/validateAndMigrateConfigData.d.ts.map +1 -1
- package/dist/cli/checkAndUpdateConfig/utils/validateAndMigrateConfigData.js +19 -1
- package/dist/cli/generateOpenApiClient/__tests__/generateOpenApiClient.strict.test.js +18 -9
- package/dist/cli/generateOpenApiClient/generateOpenApiClient.d.ts +2 -5
- package/dist/cli/generateOpenApiClient/generateOpenApiClient.d.ts.map +1 -1
- package/dist/cli/generateOpenApiClient/generateOpenApiClient.js +12 -16
- package/dist/cli/index.js +41 -9
- package/dist/cli/initOpenApiConfig/__tests__/init.test.js +4 -14
- package/dist/cli/initOpenApiConfig/__tests__/initConfig.test.js +3 -1
- package/dist/cli/initOpenApiConfig/init.d.ts +2 -1
- package/dist/cli/initOpenApiConfig/init.d.ts.map +1 -1
- package/dist/cli/initOpenApiConfig/init.js +3 -3
- package/dist/cli/initOpenApiConfig/initCustomRequest.d.ts +0 -1
- package/dist/cli/initOpenApiConfig/initCustomRequest.d.ts.map +1 -1
- package/dist/cli/initOpenApiConfig/initCustomRequest.js +1 -2
- package/dist/cli/initOpenApiConfig/utils/__tests__/validateSpecFiles.test.js +9 -0
- package/dist/cli/initOpenApiConfig/utils/buildConfig.d.ts.map +1 -1
- package/dist/cli/initOpenApiConfig/utils/buildConfig.js +0 -4
- package/dist/cli/previewChanges/previewChanges.d.ts +2 -1
- package/dist/cli/previewChanges/previewChanges.d.ts.map +1 -1
- package/dist/cli/previewChanges/previewChanges.js +6 -6
- package/dist/cli/schemas/analyzeUsage.d.ts +13 -0
- package/dist/cli/schemas/analyzeUsage.d.ts.map +1 -0
- package/dist/cli/schemas/analyzeUsage.js +38 -0
- package/dist/cli/schemas/generate.d.ts +7 -0
- package/dist/cli/schemas/generate.d.ts.map +1 -1
- package/dist/cli/schemas/generate.js +4 -0
- package/dist/cli/schemas/index.d.ts +2 -2
- package/dist/cli/schemas/index.d.ts.map +1 -1
- package/dist/cli/schemas/index.js +1 -1
- package/dist/cli/schemas/init.d.ts +0 -1
- package/dist/cli/schemas/init.d.ts.map +1 -1
- package/dist/cli/schemas/init.js +0 -1
- package/dist/cli/types.d.ts +6 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/common/Consts.d.ts +1 -0
- package/dist/common/Consts.d.ts.map +1 -1
- package/dist/common/Consts.js +6 -2
- package/dist/common/Logger.js +2 -2
- package/dist/common/LoggerMessages.d.ts +59 -12
- package/dist/common/LoggerMessages.d.ts.map +1 -1
- package/dist/common/LoggerMessages.js +62 -15
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedBase.d.ts +0 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedBase.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV1.d.ts +0 -2
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV1.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV2.d.ts +0 -2
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV2.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV3.d.ts +0 -2
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV3.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV4.d.ts +0 -2
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV4.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.d.ts +7 -2
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.d.ts.map +1 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.js +5 -1
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedVersionedSchemas.d.ts +14 -11
- package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedVersionedSchemas.d.ts.map +1 -1
- package/dist/common/VersionedSchema/CommonSchemas.d.ts +0 -2
- package/dist/common/VersionedSchema/CommonSchemas.d.ts.map +1 -1
- package/dist/common/VersionedSchema/CommonSchemas.js +0 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsMigrationPlan.d.ts.map +1 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsMigrationPlan.js +0 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV3.d.ts +0 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV3.d.ts.map +1 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV4.d.ts +0 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV4.d.ts.map +1 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV5.d.ts +0 -1
- package/dist/common/VersionedSchema/MultiOptionsVersioned/MultiOptionsSchemaV5.d.ts.map +1 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsMigrationPlans.d.ts.map +1 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsMigrationPlans.js +0 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsSchemaV3.d.ts +0 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsSchemaV3.d.ts.map +1 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsSchemaV4.d.ts +0 -1
- package/dist/common/VersionedSchema/OptionsVersioned/OptionsSchemaV4.d.ts.map +1 -1
- package/dist/common/VersionedSchema/Utils/__mocks__/compatibilityCases.d.ts.map +1 -1
- package/dist/common/VersionedSchema/Utils/__mocks__/compatibilityCases.js +8 -9
- package/dist/common/VersionedSchema/Utils/migrateDataToLatestSchemaVersion.d.ts.map +1 -1
- package/dist/common/VersionedSchema/Utils/migrateDataToLatestSchemaVersion.js +1 -1
- package/dist/common/utils/__tests__/convertArrayToObject.test.js +0 -3
- package/dist/common/utils/convertArrayToObject.d.ts.map +1 -1
- package/dist/common/utils/convertArrayToObject.js +0 -2
- package/dist/core/OpenApiClient.d.ts +12 -1
- package/dist/core/OpenApiClient.d.ts.map +1 -1
- package/dist/core/OpenApiClient.js +193 -16
- package/dist/core/WriteClient.d.ts +12 -3
- package/dist/core/WriteClient.d.ts.map +1 -1
- package/dist/core/WriteClient.js +25 -5
- package/dist/core/plugins/getBuiltinPlugins.d.ts.map +1 -1
- package/dist/core/plugins/loadGeneratorPlugins.d.ts.map +1 -1
- package/dist/core/plugins/loadGeneratorPlugins.js +1 -1
- package/dist/core/types/shared/Client.model.d.ts +1 -1
- package/dist/core/types/shared/Client.model.d.ts.map +1 -1
- package/dist/core/types/shared/Model.model.d.ts +1 -1
- package/dist/core/types/shared/Model.model.d.ts.map +1 -1
- package/dist/core/utils/__tests__/prepareDtoModels.test.js +95 -0
- package/dist/core/utils/__tests__/writeClientServices.test.js +0 -1
- package/dist/core/utils/loadDiffReport.d.ts.map +1 -1
- package/dist/core/utils/loadDiffReport.js +5 -4
- package/dist/core/utils/precompileTemplates.js +3 -2
- package/dist/core/utils/prepareDtoModels.d.ts.map +1 -1
- package/dist/core/utils/prepareDtoModels.js +1 -7
- package/dist/core/utils/writeClientCore.d.ts +0 -2
- package/dist/core/utils/writeClientCore.d.ts.map +1 -1
- package/dist/core/utils/writeClientCore.js +19 -18
- package/dist/core/utils/writeClientCoreIndex.d.ts.map +1 -1
- package/dist/core/utils/writeClientCoreIndex.js +14 -4
- package/dist/core/utils/writeClientExecutor.d.ts.map +1 -1
- package/dist/core/utils/writeClientExecutor.js +4 -4
- package/dist/core/utils/writeClientFullIndex.js +4 -4
- package/dist/core/utils/writeClientModels.d.ts.map +1 -1
- package/dist/core/utils/writeClientModels.js +10 -10
- package/dist/core/utils/writeClientModelsIndex.d.ts +1 -1
- package/dist/core/utils/writeClientModelsIndex.d.ts.map +1 -1
- package/dist/core/utils/writeClientModelsIndex.js +14 -4
- package/dist/core/utils/writeClientSchemas.d.ts.map +1 -1
- package/dist/core/utils/writeClientSchemas.js +7 -7
- package/dist/core/utils/writeClientSchemasIndex.d.ts.map +1 -1
- package/dist/core/utils/writeClientSchemasIndex.js +14 -4
- package/dist/core/utils/writeClientServices.d.ts +0 -2
- package/dist/core/utils/writeClientServices.d.ts.map +1 -1
- package/dist/core/utils/writeClientServices.js +7 -8
- package/dist/core/utils/writeClientServicesIndex.d.ts.map +1 -1
- package/dist/core/utils/writeClientServicesIndex.js +14 -4
- package/dist/core/utils/writeClientSimpleIndex.js +4 -4
- package/dist/templatesCompiled/cli/customRequest.d.ts +2 -6
- package/dist/templatesCompiled/cli/customRequest.d.ts.map +1 -1
- package/dist/templatesCompiled/cli/customRequest.js +8 -28
- package/dist/templatesCompiled/cli/customRequestExecutor.d.ts +0 -3
- package/dist/templatesCompiled/cli/customRequestExecutor.d.ts.map +1 -1
- package/dist/templatesCompiled/cli/customRequestExecutor.js +5 -28
- package/dist/templatesCompiled/client/core/axios/request.d.ts +2 -6
- package/dist/templatesCompiled/client/core/axios/request.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/axios/request.js +2 -22
- package/dist/templatesCompiled/client/core/executor/createExecutorAdapter.d.ts +2 -5
- package/dist/templatesCompiled/client/core/executor/createExecutorAdapter.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/executor/createExecutorAdapter.js +12 -42
- package/dist/templatesCompiled/client/core/executor/requestExecutor.d.ts +0 -2
- package/dist/templatesCompiled/client/core/executor/requestExecutor.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/executor/requestExecutor.js +3 -11
- package/dist/templatesCompiled/client/core/fetch/request.d.ts +2 -6
- package/dist/templatesCompiled/client/core/fetch/request.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/fetch/request.js +2 -22
- package/dist/templatesCompiled/client/core/interceptors/withInterceptors.d.ts +0 -2
- package/dist/templatesCompiled/client/core/interceptors/withInterceptors.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/interceptors/withInterceptors.js +3 -11
- package/dist/templatesCompiled/client/core/node/request.d.ts +2 -6
- package/dist/templatesCompiled/client/core/node/request.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/node/request.js +2 -22
- package/dist/templatesCompiled/client/core/xhr/request.d.ts +2 -6
- package/dist/templatesCompiled/client/core/xhr/request.d.ts.map +1 -1
- package/dist/templatesCompiled/client/core/xhr/request.js +2 -22
- package/dist/templatesCompiled/client/exportService.d.ts +9 -11
- package/dist/templatesCompiled/client/exportService.d.ts.map +1 -1
- package/dist/templatesCompiled/client/exportService.js +64 -82
- package/dist/test/helpers/silenceLoggers.d.ts +11 -0
- package/dist/test/helpers/silenceLoggers.d.ts.map +1 -0
- package/dist/test/helpers/silenceLoggers.js +88 -0
- package/package.json +4 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImportRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ImportRule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,UAAW,YAAW,IAAI;IAC/B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAElB,MAAM,EAAE,KAAK,GACZ,OAAO,CAAC,OAAO,EAAE,CAAC;CAiDtB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImportRule = void 0;
|
|
4
|
+
const fuzzy_1 = require("../utils/fuzzy");
|
|
5
|
+
class ImportRule {
|
|
6
|
+
async check(context, contract,
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
|
+
_stats) {
|
|
9
|
+
const findings = [];
|
|
10
|
+
const rootExports = new Set(contract.sourceFile.getExportedDeclarations().keys());
|
|
11
|
+
for (const file of context.getConsumerSourceFiles()) {
|
|
12
|
+
for (const imp of file.getImportDeclarations()) {
|
|
13
|
+
const moduleName = imp.getModuleSpecifierValue();
|
|
14
|
+
if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const importedSource = imp.getModuleSpecifierSourceFile();
|
|
18
|
+
if (!importedSource) {
|
|
19
|
+
findings.push({
|
|
20
|
+
id: "INVALID_IMPORT_PATH",
|
|
21
|
+
category: "INVALID_IMPORT",
|
|
22
|
+
severity: "ERROR",
|
|
23
|
+
message: `Import "${moduleName}" could not be resolved.`,
|
|
24
|
+
file: file.getFilePath(),
|
|
25
|
+
line: imp.getStartLineNumber(),
|
|
26
|
+
});
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const allowedExports = moduleName === "@lom-api"
|
|
30
|
+
? rootExports
|
|
31
|
+
: new Set(importedSource.getExportedDeclarations().keys());
|
|
32
|
+
for (const namedImport of imp.getNamedImports()) {
|
|
33
|
+
const importedName = namedImport.getName();
|
|
34
|
+
if (allowedExports.has(importedName))
|
|
35
|
+
continue;
|
|
36
|
+
const suggestion = (0, fuzzy_1.findBestMatch)(importedName, [...allowedExports]);
|
|
37
|
+
findings.push({
|
|
38
|
+
id: "INVALID_IMPORT_NAME",
|
|
39
|
+
category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
|
|
40
|
+
severity: "ERROR",
|
|
41
|
+
message: `Import "${importedName}" is not exported by "${moduleName}".${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
|
|
42
|
+
file: file.getFilePath(),
|
|
43
|
+
line: namedImport.getStartLineNumber(),
|
|
44
|
+
context: { suggestion },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return findings;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.ImportRule = ImportRule;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProjectContext } from "../core/ProjectContext";
|
|
2
|
+
import type { Contract, Finding, Rule, Stats } from "../types";
|
|
3
|
+
export declare class ModelRule implements Rule {
|
|
4
|
+
check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ModelRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ModelRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE/D,qBAAa,SAAU,YAAW,IAAI;IAC9B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;CAuCtB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModelRule = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
class ModelRule {
|
|
6
|
+
async check(context, contract, stats) {
|
|
7
|
+
const findings = [];
|
|
8
|
+
const knownModels = new Set(contract.models);
|
|
9
|
+
for (const file of context.getConsumerSourceFiles()) {
|
|
10
|
+
for (const imp of file.getImportDeclarations()) {
|
|
11
|
+
const moduleName = imp.getModuleSpecifierValue();
|
|
12
|
+
if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
for (const namedImport of imp.getNamedImports()) {
|
|
16
|
+
const importedName = namedImport.getName();
|
|
17
|
+
if (importedName.endsWith("Schema") ||
|
|
18
|
+
importedName.endsWith("Service") ||
|
|
19
|
+
importedName === "createClient") {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const localName = namedImport.getAliasNode()?.getText() || importedName;
|
|
23
|
+
if (!knownModels.has(importedName))
|
|
24
|
+
continue;
|
|
25
|
+
const identifiers = file
|
|
26
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.Identifier)
|
|
27
|
+
.filter((id) => id.getText() === localName);
|
|
28
|
+
const usedOutsideImport = identifiers.some((id) => id.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration) == null);
|
|
29
|
+
if (usedOutsideImport) {
|
|
30
|
+
stats.usedModels.add(importedName);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return findings;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.ModelRule = ModelRule;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProjectContext } from "../core/ProjectContext";
|
|
2
|
+
import type { Contract, Finding, Rule, Stats } from "../types";
|
|
3
|
+
export declare class SchemaRule implements Rule {
|
|
4
|
+
check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=SchemaRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemaRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/SchemaRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,UAAW,YAAW,IAAI;IAC/B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;CAgDtB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaRule = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
const fuzzy_1 = require("../utils/fuzzy");
|
|
6
|
+
class SchemaRule {
|
|
7
|
+
async check(context, contract, stats) {
|
|
8
|
+
const findings = [];
|
|
9
|
+
const knownSchemas = new Set(contract.schemas);
|
|
10
|
+
for (const file of context.getConsumerSourceFiles()) {
|
|
11
|
+
const imports = file.getImportDeclarations();
|
|
12
|
+
for (const imp of imports) {
|
|
13
|
+
const moduleName = imp.getModuleSpecifierValue();
|
|
14
|
+
if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
for (const namedImport of imp.getNamedImports()) {
|
|
18
|
+
const importedName = namedImport.getName();
|
|
19
|
+
if (!importedName.endsWith("Schema"))
|
|
20
|
+
continue;
|
|
21
|
+
const localName = namedImport.getAliasNode()?.getText() || importedName;
|
|
22
|
+
if (!knownSchemas.has(importedName)) {
|
|
23
|
+
const suggestion = (0, fuzzy_1.findBestMatch)(importedName, contract.schemas);
|
|
24
|
+
findings.push({
|
|
25
|
+
id: "SCHEMA_NOT_FOUND",
|
|
26
|
+
category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
|
|
27
|
+
severity: "ERROR",
|
|
28
|
+
message: `Schema "${importedName}" was not found in the API.${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
|
|
29
|
+
file: file.getFilePath(),
|
|
30
|
+
line: namedImport.getStartLineNumber(),
|
|
31
|
+
context: { suggestion },
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const identifiers = file
|
|
36
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.Identifier)
|
|
37
|
+
.filter((id) => id.getText() === localName);
|
|
38
|
+
const usedOutsideImport = identifiers.some((id) => id.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration) == null);
|
|
39
|
+
if (usedOutsideImport) {
|
|
40
|
+
stats.usedSchemas.add(importedName);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.SchemaRule = SchemaRule;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProjectContext } from "../core/ProjectContext";
|
|
2
|
+
import type { Contract, Finding, Rule, Stats } from "../types";
|
|
3
|
+
export declare class ServiceRule implements Rule {
|
|
4
|
+
check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
|
|
5
|
+
private getExpectedParameterType;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=ServiceRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServiceRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ServiceRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,WAAY,YAAW,IAAI;IAChC,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;IA0FrB,OAAO,CAAC,wBAAwB;CAiBjC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceRule = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
const fuzzy_1 = require("../utils/fuzzy");
|
|
6
|
+
class ServiceRule {
|
|
7
|
+
async check(context, contract, stats) {
|
|
8
|
+
const findings = [];
|
|
9
|
+
const checker = context.getTypeChecker();
|
|
10
|
+
for (const file of context.getConsumerSourceFiles()) {
|
|
11
|
+
const calls = file.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
|
|
12
|
+
for (const call of calls) {
|
|
13
|
+
const expression = call.getExpression();
|
|
14
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expression))
|
|
15
|
+
continue;
|
|
16
|
+
const serviceAccess = expression.getExpression();
|
|
17
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(serviceAccess))
|
|
18
|
+
continue;
|
|
19
|
+
const serviceName = serviceAccess.getName();
|
|
20
|
+
const methodName = expression.getName();
|
|
21
|
+
if (!contract.services[serviceName])
|
|
22
|
+
continue;
|
|
23
|
+
stats.usedMethods.add(`${serviceName}.${methodName}`);
|
|
24
|
+
const methodContract = contract.services[serviceName].find((m) => m.name === methodName);
|
|
25
|
+
if (!methodContract) {
|
|
26
|
+
const suggestion = (0, fuzzy_1.findBestMatch)(methodName, contract.services[serviceName].map((m) => m.name));
|
|
27
|
+
findings.push({
|
|
28
|
+
id: "SERVICE_METHOD_NOT_FOUND",
|
|
29
|
+
category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
|
|
30
|
+
severity: "ERROR",
|
|
31
|
+
message: `Method "${serviceName}.${methodName}" was not found in the generated API.${suggestion ? ` Did you mean "${serviceName}.${suggestion}"?` : ""}`,
|
|
32
|
+
file: file.getFilePath(),
|
|
33
|
+
line: call.getStartLineNumber(),
|
|
34
|
+
context: { suggestion },
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const args = call.getArguments();
|
|
39
|
+
const serviceDecl = contract.sourceFile
|
|
40
|
+
.getExportedDeclarations()
|
|
41
|
+
.get(serviceName)
|
|
42
|
+
?.find((d) => ts_morph_1.Node.isClassDeclaration(d));
|
|
43
|
+
const methodDecl = serviceDecl && ts_morph_1.Node.isClassDeclaration(serviceDecl)
|
|
44
|
+
? serviceDecl.getMethod(methodName)
|
|
45
|
+
: undefined;
|
|
46
|
+
const requiredParams = methodDecl
|
|
47
|
+
? methodDecl.getParameters().filter((p) => !p.isOptional())
|
|
48
|
+
: methodContract.params.filter((p) => !p.isOptional);
|
|
49
|
+
if (args.length < requiredParams.length) {
|
|
50
|
+
findings.push({
|
|
51
|
+
id: "SERVICE_ARGUMENTS_COUNT_MISMATCH",
|
|
52
|
+
category: "TYPE_MISMATCH",
|
|
53
|
+
severity: "ERROR",
|
|
54
|
+
message: `Method ${serviceName}.${methodName} expects at least ${requiredParams.length} arguments, but got ${args.length}.`,
|
|
55
|
+
file: file.getFilePath(),
|
|
56
|
+
line: call.getStartLineNumber(),
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
for (let i = 0; i < Math.min(args.length, methodContract.params.length); i++) {
|
|
61
|
+
const expectedType = this.getExpectedParameterType(contract, serviceName, methodName, i);
|
|
62
|
+
if (!expectedType)
|
|
63
|
+
continue;
|
|
64
|
+
const providedType = checker.getTypeAtLocation(args[i]);
|
|
65
|
+
if (!providedType.isAssignableTo(expectedType)) {
|
|
66
|
+
findings.push({
|
|
67
|
+
id: "SERVICE_ARGUMENT_TYPE_MISMATCH",
|
|
68
|
+
category: "TYPE_MISMATCH",
|
|
69
|
+
severity: "ERROR",
|
|
70
|
+
message: `Argument #${i + 1} type mismatch in ${serviceName}.${methodName}. Expected ${expectedType.getText()}, got ${providedType.getText()}.`,
|
|
71
|
+
file: file.getFilePath(),
|
|
72
|
+
line: args[i].getStartLineNumber(),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return findings;
|
|
79
|
+
}
|
|
80
|
+
getExpectedParameterType(contract, serviceName, methodName, paramIndex) {
|
|
81
|
+
const exportedDecl = contract.sourceFile
|
|
82
|
+
.getExportedDeclarations()
|
|
83
|
+
.get(serviceName)
|
|
84
|
+
?.find((d) => ts_morph_1.Node.isClassDeclaration(d));
|
|
85
|
+
const method = exportedDecl && ts_morph_1.Node.isClassDeclaration(exportedDecl)
|
|
86
|
+
? exportedDecl.getMethod(methodName)
|
|
87
|
+
: undefined;
|
|
88
|
+
const param = method?.getParameters()[paramIndex];
|
|
89
|
+
return param?.getType();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.ServiceRule = ServiceRule;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { SourceFile } from "ts-morph";
|
|
2
|
+
import type { ProjectContext } from "./core/ProjectContext";
|
|
3
|
+
export interface MethodMetadata {
|
|
4
|
+
name: string;
|
|
5
|
+
params: {
|
|
6
|
+
name: string;
|
|
7
|
+
type: string;
|
|
8
|
+
isOptional: boolean;
|
|
9
|
+
}[];
|
|
10
|
+
returnType: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Contract {
|
|
13
|
+
services: Record<string, MethodMetadata[]>;
|
|
14
|
+
schemas: string[];
|
|
15
|
+
models: string[];
|
|
16
|
+
sourceFile: SourceFile;
|
|
17
|
+
}
|
|
18
|
+
export interface Finding {
|
|
19
|
+
id: string;
|
|
20
|
+
severity: "ERROR" | "WARNING";
|
|
21
|
+
category: "INVALID_IMPORT" | "MISSING_EXPORT" | "RENAMED_SYMBOL" | "TYPE_MISMATCH" | "UNUSED" | "CONFIG" | "USAGE";
|
|
22
|
+
message: string;
|
|
23
|
+
file: string;
|
|
24
|
+
line: number;
|
|
25
|
+
context?: any;
|
|
26
|
+
}
|
|
27
|
+
export interface Stats {
|
|
28
|
+
usedMethods: Set<string>;
|
|
29
|
+
usedSchemas: Set<string>;
|
|
30
|
+
usedModels: Set<string>;
|
|
31
|
+
}
|
|
32
|
+
export interface CoverageReport {
|
|
33
|
+
methods: {
|
|
34
|
+
total: number;
|
|
35
|
+
used: number;
|
|
36
|
+
percent: string;
|
|
37
|
+
};
|
|
38
|
+
schemas: {
|
|
39
|
+
total: number;
|
|
40
|
+
used: number;
|
|
41
|
+
percent: string;
|
|
42
|
+
};
|
|
43
|
+
models: {
|
|
44
|
+
total: number;
|
|
45
|
+
used: number;
|
|
46
|
+
percent: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface Rule {
|
|
50
|
+
check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/cli/analyzeUsage/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,QAAQ,EACJ,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,eAAe,GACf,QAAQ,GACR,QAAQ,GACR,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AAED,MAAM,WAAW,IAAI;IACjB,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACxF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/utils/fuzzy.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,SAAI,GACd,MAAM,GAAG,IAAI,CAaf"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findBestMatch = findBestMatch;
|
|
4
|
+
/**
|
|
5
|
+
* Вычисляет расстояние Левенштейна между двумя строками.
|
|
6
|
+
*/
|
|
7
|
+
function getLevenshteinDistance(a, b) {
|
|
8
|
+
const matrix = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
9
|
+
for (let i = 0; i <= a.length; i++)
|
|
10
|
+
matrix[i][0] = i;
|
|
11
|
+
for (let j = 0; j <= b.length; j++)
|
|
12
|
+
matrix[0][j] = j;
|
|
13
|
+
for (let i = 1; i <= a.length; i++) {
|
|
14
|
+
for (let j = 1; j <= b.length; j++) {
|
|
15
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
16
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // удаление
|
|
17
|
+
matrix[i][j - 1] + 1, // вставка
|
|
18
|
+
matrix[i - 1][j - 1] + cost // замена
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return matrix[a.length][b.length];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Ищет наиболее подходящую замену из списка доступных опций.
|
|
26
|
+
*/
|
|
27
|
+
function findBestMatch(target, options, threshold = 3) {
|
|
28
|
+
let bestMatch = null;
|
|
29
|
+
let minDistance = Infinity;
|
|
30
|
+
for (const option of options) {
|
|
31
|
+
const distance = getLevenshteinDistance(target, option);
|
|
32
|
+
if (distance < minDistance && distance <= threshold) {
|
|
33
|
+
minDistance = distance;
|
|
34
|
+
bestMatch = option;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return bestMatch;
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CoverageReport, Finding, Stats } from "../types";
|
|
2
|
+
export declare class Reporter {
|
|
3
|
+
/**
|
|
4
|
+
* Выводит сводную таблицу и результаты в консоль
|
|
5
|
+
*/
|
|
6
|
+
static renderConsole(findings: Finding[], coverage: CoverageReport): void;
|
|
7
|
+
/**
|
|
8
|
+
* Сохраняет отчет в формате JSON для CI/CD инструментов
|
|
9
|
+
*/
|
|
10
|
+
static saveJsonReport(filePath: string, findings: Finding[], coverage: CoverageReport): void;
|
|
11
|
+
/**
|
|
12
|
+
* Вычисляет процент покрытия на основе статистики
|
|
13
|
+
*/
|
|
14
|
+
static calculateCoverage(stats: Stats, contract: any): CoverageReport;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/utils/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE9D,qBAAa,QAAQ;IACnB;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IA8BzE;;OAEG;IACH,MAAM,CAAC,cAAc,CACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,cAAc,GACvB,IAAI;IAwBP;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,cAAc;CA0BtE"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Reporter = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
class Reporter {
|
|
10
|
+
/**
|
|
11
|
+
* Выводит сводную таблицу и результаты в консоль
|
|
12
|
+
*/
|
|
13
|
+
static renderConsole(findings, coverage) {
|
|
14
|
+
console.log("\n📊 API Coverage:");
|
|
15
|
+
console.log(` Methods: ${coverage.methods.used}/${coverage.methods.total} (${coverage.methods.percent})`);
|
|
16
|
+
console.log(` Schemas: ${coverage.schemas.used}/${coverage.schemas.total} (${coverage.schemas.percent})`);
|
|
17
|
+
console.log(` Models: ${coverage.models.used}/${coverage.models.total} (${coverage.models.percent})`);
|
|
18
|
+
if (findings.length === 0) {
|
|
19
|
+
console.log("\n✅ No mismatches found. Project is in sync.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log("\n⚠️ Findings:");
|
|
23
|
+
// Форматируем данные для console.table
|
|
24
|
+
const tableData = findings.map((f) => ({
|
|
25
|
+
Status: f.severity === "ERROR" ? "🔴 ERROR" : "🟡 WARN",
|
|
26
|
+
Category: f.category,
|
|
27
|
+
Location: `${path_1.default.basename(f.file)}:${f.line}`,
|
|
28
|
+
Message: f.message,
|
|
29
|
+
}));
|
|
30
|
+
console.table(tableData);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Сохраняет отчет в формате JSON для CI/CD инструментов
|
|
34
|
+
*/
|
|
35
|
+
static saveJsonReport(filePath, findings, coverage) {
|
|
36
|
+
const report = {
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
summary: {
|
|
39
|
+
totalIssues: findings.length,
|
|
40
|
+
errors: findings.filter((f) => f.severity === "ERROR").length,
|
|
41
|
+
warnings: findings.filter((f) => f.severity === "WARNING").length,
|
|
42
|
+
},
|
|
43
|
+
categories: findings.reduce((acc, finding) => {
|
|
44
|
+
acc[finding.category] = (acc[finding.category] || 0) + 1;
|
|
45
|
+
return acc;
|
|
46
|
+
}, {}),
|
|
47
|
+
coverage,
|
|
48
|
+
findings,
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(report, null, 2));
|
|
52
|
+
console.log(`\n💾 Report saved: ${path_1.default.resolve(filePath)}`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(`\n❌ Failed to save JSON report: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Вычисляет процент покрытия на основе статистики
|
|
60
|
+
*/
|
|
61
|
+
static calculateCoverage(stats, contract) {
|
|
62
|
+
const totalMethods = Object.values(contract.services).flat().length;
|
|
63
|
+
const totalSchemas = contract.schemas.length;
|
|
64
|
+
const totalModels = contract.models.length;
|
|
65
|
+
const getPercent = (used, total) => total > 0 ? ((used / total) * 100).toFixed(2) + "%" : "0%";
|
|
66
|
+
return {
|
|
67
|
+
methods: {
|
|
68
|
+
total: totalMethods,
|
|
69
|
+
used: stats.usedMethods.size,
|
|
70
|
+
percent: getPercent(stats.usedMethods.size, totalMethods),
|
|
71
|
+
},
|
|
72
|
+
schemas: {
|
|
73
|
+
total: totalSchemas,
|
|
74
|
+
used: stats.usedSchemas.size,
|
|
75
|
+
percent: getPercent(stats.usedSchemas.size, totalSchemas),
|
|
76
|
+
},
|
|
77
|
+
models: {
|
|
78
|
+
total: totalModels,
|
|
79
|
+
used: stats.usedModels.size,
|
|
80
|
+
percent: getPercent(stats.usedModels.size, totalModels),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.Reporter = Reporter;
|
|
@@ -11,52 +11,42 @@ const node_test_1 = require("node:test");
|
|
|
11
11
|
const Consts_1 = require("../../../common/Consts");
|
|
12
12
|
const HttpClient_enum_1 = require("../../../core/types/enums/HttpClient.enum");
|
|
13
13
|
const checkConfig_1 = require("../checkConfig");
|
|
14
|
-
class ProcessExitError extends Error {
|
|
15
|
-
exitCode;
|
|
16
|
-
constructor(exitCode) {
|
|
17
|
-
super(`process.exit(${exitCode})`);
|
|
18
|
-
this.exitCode = exitCode;
|
|
19
|
-
this.name = 'ProcessExitError';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
14
|
/** Config without schema-default fields so checkConfig does not open enquirer. */
|
|
23
15
|
const flatConfig = {
|
|
24
16
|
input: './test/spec/v3.json',
|
|
25
17
|
output: './test/generated',
|
|
26
18
|
httpClient: HttpClient_enum_1.HttpClient.AXIOS,
|
|
27
19
|
};
|
|
28
|
-
const mockProcessExit = () => node_test_1.mock.method(process, 'exit', (code) => {
|
|
29
|
-
throw new ProcessExitError(Number(code ?? 0));
|
|
30
|
-
});
|
|
31
20
|
async function writeConfig(dir, content) {
|
|
32
21
|
const configPath = (0, node_path_1.join)(dir, 'openapi.config.json');
|
|
33
22
|
await (0, promises_1.writeFile)(configPath, JSON.stringify(content, null, 2), 'utf8');
|
|
34
23
|
return configPath;
|
|
35
24
|
}
|
|
36
25
|
(0, node_test_1.describe)('@unit: checkConfig', () => {
|
|
37
|
-
(0, node_test_1.test)('
|
|
38
|
-
const exitMock = mockProcessExit();
|
|
26
|
+
(0, node_test_1.test)('returns failure when options fail schema validation', async () => {
|
|
39
27
|
const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
|
|
40
28
|
const errorMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'error', () => undefined);
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: 123 });
|
|
30
|
+
node_assert_1.default.strictEqual(result.success, false);
|
|
31
|
+
node_assert_1.default.ok(result.error);
|
|
43
32
|
shutdownMock.mock.restore();
|
|
44
33
|
errorMock.mock.restore();
|
|
45
34
|
});
|
|
46
|
-
(0, node_test_1.test)('
|
|
35
|
+
(0, node_test_1.test)('returns failure when config file is missing', async () => {
|
|
47
36
|
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'check-config-'));
|
|
48
37
|
const configPath = (0, node_path_1.join)(dir, 'missing.json');
|
|
49
|
-
const exitMock = mockProcessExit();
|
|
50
38
|
const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: configPath });
|
|
40
|
+
node_assert_1.default.strictEqual(result.success, false);
|
|
41
|
+
node_assert_1.default.ok(result.error);
|
|
53
42
|
shutdownMock.mock.restore();
|
|
54
43
|
});
|
|
55
44
|
(0, node_test_1.test)('reports valid config without prompting when up to date', async () => {
|
|
56
45
|
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'check-config-'));
|
|
57
46
|
const configPath = await writeConfig(dir, flatConfig);
|
|
58
47
|
const infoMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'info', () => undefined);
|
|
59
|
-
await (0, checkConfig_1.checkConfig)({ openapiConfig: configPath });
|
|
48
|
+
const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: configPath });
|
|
49
|
+
node_assert_1.default.strictEqual(result.success, true);
|
|
60
50
|
node_assert_1.default.strictEqual(infoMock.mock.callCount(), 1);
|
|
61
51
|
infoMock.mock.restore();
|
|
62
52
|
});
|
|
@@ -11,51 +11,41 @@ const node_test_1 = require("node:test");
|
|
|
11
11
|
const Consts_1 = require("../../../common/Consts");
|
|
12
12
|
const HttpClient_enum_1 = require("../../../core/types/enums/HttpClient.enum");
|
|
13
13
|
const updateConfig_1 = require("../updateConfig");
|
|
14
|
-
class ProcessExitError extends Error {
|
|
15
|
-
exitCode;
|
|
16
|
-
constructor(exitCode) {
|
|
17
|
-
super(`process.exit(${exitCode})`);
|
|
18
|
-
this.exitCode = exitCode;
|
|
19
|
-
this.name = 'ProcessExitError';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
14
|
const flatConfig = {
|
|
23
15
|
input: './test/spec/v3.json',
|
|
24
16
|
output: './test/generated',
|
|
25
17
|
httpClient: HttpClient_enum_1.HttpClient.FETCH,
|
|
26
18
|
};
|
|
27
|
-
const mockProcessExit = () => node_test_1.mock.method(process, 'exit', (code) => {
|
|
28
|
-
throw new ProcessExitError(Number(code ?? 0));
|
|
29
|
-
});
|
|
30
19
|
async function writeConfig(dir, content) {
|
|
31
20
|
const configPath = (0, node_path_1.join)(dir, 'openapi.config.json');
|
|
32
21
|
await (0, promises_1.writeFile)(configPath, JSON.stringify(content, null, 2), 'utf8');
|
|
33
22
|
return configPath;
|
|
34
23
|
}
|
|
35
24
|
(0, node_test_1.describe)('@unit: updateConfig', () => {
|
|
36
|
-
(0, node_test_1.test)('
|
|
37
|
-
const exitMock = mockProcessExit();
|
|
25
|
+
(0, node_test_1.test)('returns failure when options fail schema validation', async () => {
|
|
38
26
|
const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
|
|
39
27
|
const errorMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'error', () => undefined);
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
const result = await (0, updateConfig_1.updateConfig)({ openapiConfig: 123 });
|
|
29
|
+
node_assert_1.default.strictEqual(result.success, false);
|
|
30
|
+
node_assert_1.default.ok(result.error);
|
|
42
31
|
shutdownMock.mock.restore();
|
|
43
32
|
errorMock.mock.restore();
|
|
44
33
|
});
|
|
45
|
-
(0, node_test_1.test)('
|
|
34
|
+
(0, node_test_1.test)('returns failure when config file is missing', async () => {
|
|
46
35
|
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'update-config-'));
|
|
47
36
|
const configPath = (0, node_path_1.join)(dir, 'missing.json');
|
|
48
|
-
const exitMock = mockProcessExit();
|
|
49
37
|
const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
const result = await (0, updateConfig_1.updateConfig)({ openapiConfig: configPath });
|
|
39
|
+
node_assert_1.default.strictEqual(result.success, false);
|
|
40
|
+
node_assert_1.default.ok(result.error);
|
|
52
41
|
shutdownMock.mock.restore();
|
|
53
42
|
});
|
|
54
43
|
(0, node_test_1.test)('logs up to date when config is current', async () => {
|
|
55
44
|
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'update-config-'));
|
|
56
45
|
const configPath = await writeConfig(dir, flatConfig);
|
|
57
46
|
const infoMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'info', () => undefined);
|
|
58
|
-
await (0, updateConfig_1.updateConfig)({ openapiConfig: configPath });
|
|
47
|
+
const result = await (0, updateConfig_1.updateConfig)({ openapiConfig: configPath });
|
|
48
|
+
node_assert_1.default.strictEqual(result.success, true);
|
|
59
49
|
node_assert_1.default.strictEqual(infoMock.mock.callCount(), 1);
|
|
60
50
|
infoMock.mock.restore();
|
|
61
51
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OptionValues } from 'commander';
|
|
2
|
+
import { CLICommandResult } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* Проверяет конфигурационный файл на корректность и актуальность.
|
|
4
5
|
* Если обнаружены проблемы, предлагает действия для их исправления.
|
|
@@ -9,5 +10,5 @@ import { OptionValues } from 'commander';
|
|
|
9
10
|
* @example
|
|
10
11
|
* await checkConfig('./openapi-config.json');
|
|
11
12
|
*/
|
|
12
|
-
export declare function checkConfig(options: OptionValues): Promise<
|
|
13
|
+
export declare function checkConfig(options: OptionValues): Promise<CLICommandResult>;
|
|
13
14
|
//# sourceMappingURL=checkConfig.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkConfig.d.ts","sourceRoot":"","sources":["../../../src/cli/checkAndUpdateConfig/checkConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"checkConfig.d.ts","sourceRoot":"","sources":["../../../src/cli/checkAndUpdateConfig/checkConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAOzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAK5C;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoDlF"}
|