@ankhorage/doctor 0.7.2 → 0.7.3
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/standalone.d.ts.map +1 -1
- package/dist/cli/standalone.js +1 -1
- package/dist/cli/standalone.js.map +1 -1
- package/dist/dependencyPolicyAnalysis.d.ts +4 -0
- package/dist/dependencyPolicyAnalysis.d.ts.map +1 -0
- package/dist/dependencyPolicyAnalysis.js +76 -0
- package/dist/dependencyPolicyAnalysis.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/cli/standalone.ts +1 -1
- package/src/dependencyPolicyAnalysis.ts +114 -0
- package/src/index.ts +4 -2
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# DOCTOR
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Executable doctor provider and standalone CLI for lightweight Ankhorage repo and package compliance diagnostics.
|
|
9
9
|
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEtF,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AASxB,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACpD;AAED,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kCAAuC,GAC/C,0BAA0B,CAgC5B;AAED,QAAA,MAAM,QAAQ,4BAAgC,CAAC;AAE/C,eAAe,QAAQ,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
|
|
2
1
|
import { createProviderCommandDescriptors, DOCTOR_COMMANDS, runDoctorCommand, } from '../commands.js';
|
|
2
|
+
import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
|
|
3
3
|
import { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from '../packageMetadata.js';
|
|
4
4
|
export function createDoctorRuntimeProvider(options = {}) {
|
|
5
5
|
const runCommandImpl = options.runCommandImpl ?? runDoctorCommand;
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAChC,eAAe,EAEf,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,UAAU,2BAA2B,CACzC,UAA8C,EAAE;IAEhD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAClE,MAAM,QAAQ,GAAG;QACf,aAAa,EAAE,gCAAgC;QAC/C,GAAG,OAAO,CAAC,QAAQ;KACY,CAAC;IAElC,OAAO;QACL,EAAE,EAAE,mBAAmB;QACvB,QAAQ,EAAE,uBAAuB;QACjC,OAAO,EAAE,sBAAsB;QAC/B,YAAY,EAAE,CAAC,GAAG,mBAAmB,CAAC;QACtC,QAAQ,EAAE,gCAAgC,EAAE;QAC5C,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YACxC,MAAM,OAAO,GAAuB,CAAC,OAAO,EAAE,EAAE,CAC9C,cAAc,CACZ;gBACE,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;gBACP,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,EACD;gBACE,QAAQ;aACT,CACF,CAAC;YAEJ,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC;KACkC,CAAC;AACzC,CAAC;AAED,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;AAE/C,eAAe,QAAQ,CAAC","sourcesContent":["import type { AnkhCommandHandler, AnkhRuntimeCommandProvider } from '@ankhorage/ankh';\n\nimport {\n createProviderCommandDescriptors,\n DOCTOR_COMMANDS,\n type DoctorCommandServices,\n runDoctorCommand,\n type RunDoctorCommandImpl,\n} from '../commands.js';\nimport { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';\nimport {\n DOCTOR_CAPABILITIES,\n DOCTOR_COMMAND_CATEGORY,\n DOCTOR_PACKAGE_NAME,\n DOCTOR_PACKAGE_VERSION,\n} from '../packageMetadata.js';\n\nexport interface CreateDoctorRuntimeProviderOptions {\n readonly runCommandImpl?: RunDoctorCommandImpl;\n readonly services?: Partial<DoctorCommandServices>;\n}\n\nexport function createDoctorRuntimeProvider(\n options: CreateDoctorRuntimeProviderOptions = {},\n): AnkhRuntimeCommandProvider {\n const runCommandImpl = options.runCommandImpl ?? runDoctorCommand;\n const services = {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n } satisfies DoctorCommandServices;\n\n return {\n id: DOCTOR_PACKAGE_NAME,\n category: DOCTOR_COMMAND_CATEGORY,\n version: DOCTOR_PACKAGE_VERSION,\n capabilities: [...DOCTOR_CAPABILITIES],\n commands: createProviderCommandDescriptors(),\n handlers: DOCTOR_COMMANDS.map((command) => {\n const handler: AnkhCommandHandler = (request) =>\n runCommandImpl(\n {\n argv: request.argv,\n command,\n context: request.context,\n },\n {\n services,\n },\n );\n\n return {\n path: command.path,\n handler,\n };\n }),\n } satisfies AnkhRuntimeCommandProvider;\n}\n\nconst provider = createDoctorRuntimeProvider();\n\nexport default provider;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EACL,KAAK,qBAAqB,EAK1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACpD;AAED,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CAkCjC"}
|
package/dist/cli/standalone.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
|
|
3
2
|
import { createDefaultCommandContext } from '../commandContext.js';
|
|
4
3
|
import { findDoctorCommandByStandaloneName, renderRootHelp, renderUnknownCommand, runDoctorCommand, } from '../commands.js';
|
|
4
|
+
import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
|
|
5
5
|
export async function runCli(argv, options = {}) {
|
|
6
6
|
const context = options.context ?? createDefaultCommandContext();
|
|
7
7
|
const runCommand = options.runCommandImpl ?? runDoctorCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.js","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"standalone.js","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAEL,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,gCAAgC,CAAC;AAQlF,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,IAAuB,EACvB,UAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,2BAA2B,EAAE,CAAC;IACjE,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAC9D,MAAM,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAEzC,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,iCAAiC,CAAC,UAAU,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC;QACtD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,UAAU,CACf;QACE,IAAI,EAAE,UAAU;QAChB,OAAO;QACP,OAAO;KACR,EACD;QACE,QAAQ,EAAE;YACR,aAAa,EAAE,gCAAgC;YAC/C,GAAG,OAAO,CAAC,QAAQ;SACpB;KACF,CACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC;AAClE,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AACxE,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC","sourcesContent":["#!/usr/bin/env bun\n\nimport type { DoctorCommandContext, DoctorCommandRunResult } from '../commandContext.js';\nimport { createDefaultCommandContext } from '../commandContext.js';\nimport {\n type DoctorCommandServices,\n findDoctorCommandByStandaloneName,\n renderRootHelp,\n renderUnknownCommand,\n runDoctorCommand,\n type RunDoctorCommandImpl,\n} from '../commands.js';\nimport { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';\n\nexport interface DoctorCliOptions {\n readonly context?: DoctorCommandContext;\n readonly runCommandImpl?: RunDoctorCommandImpl;\n readonly services?: Partial<DoctorCommandServices>;\n}\n\nexport async function runCli(\n argv: readonly string[],\n options: DoctorCliOptions = {},\n): Promise<DoctorCommandRunResult> {\n const context = options.context ?? createDefaultCommandContext();\n const runCommand = options.runCommandImpl ?? runDoctorCommand;\n const [firstToken, ...restTokens] = argv;\n\n if (firstToken === undefined || isHelpToken(firstToken)) {\n context.writeStdout(renderRootHelp(context.version));\n return { exitCode: 0 };\n }\n\n if (isVersionToken(firstToken)) {\n context.writeStdout(`${context.version}\\n`);\n return { exitCode: 0 };\n }\n\n const command = findDoctorCommandByStandaloneName(firstToken);\n if (command === null) {\n context.writeStderr(renderUnknownCommand(firstToken));\n return { exitCode: 1 };\n }\n\n return runCommand(\n {\n argv: restTokens,\n command,\n context,\n },\n {\n services: {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n },\n },\n );\n}\n\nfunction isHelpToken(value: string): boolean {\n return value === '--help' || value === '-h' || value === 'help';\n}\n\nfunction isVersionToken(value: string): boolean {\n return value === '--version' || value === '-v' || value === 'version';\n}\n\nif (import.meta.main) {\n const result = await runCli(process.argv.slice(2));\n process.exit(result.exitCode);\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type DoctorAnalysisRequest, type DoctorAnalysisResult } from './analysis.js';
|
|
2
|
+
export declare function analyzeDoctorTarget(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
|
|
3
|
+
export declare function analyzeDoctorTargetWithCliLayout(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
|
|
4
|
+
//# sourceMappingURL=dependencyPolicyAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyPolicyAnalysis.d.ts","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAQvB,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAI/B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { analyzeDoctorTarget as analyzeBaseDoctorTarget, } from './analysis.js';
|
|
4
|
+
import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
5
|
+
const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
|
|
6
|
+
const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
|
|
7
|
+
const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
|
|
8
|
+
export async function analyzeDoctorTarget(request) {
|
|
9
|
+
return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));
|
|
10
|
+
}
|
|
11
|
+
export async function analyzeDoctorTargetWithCliLayout(request) {
|
|
12
|
+
return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTargetWithCliLayout(request));
|
|
13
|
+
}
|
|
14
|
+
async function applyDevtoolsDependencyPlacementPolicy(result) {
|
|
15
|
+
if (!result.hasPackageJson) {
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
const packageJsonPath = path.join(result.targetPath, 'package.json');
|
|
19
|
+
const packageJson = await readPackageJson(packageJsonPath);
|
|
20
|
+
if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
|
|
24
|
+
const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
|
|
25
|
+
const diagnosticsWithoutPlacementRule = result.diagnostics.filter((diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID);
|
|
26
|
+
const diagnostics = hasRuntimeDevtools
|
|
27
|
+
? diagnosticsWithoutPlacementRule
|
|
28
|
+
: [
|
|
29
|
+
...diagnosticsWithoutPlacementRule,
|
|
30
|
+
createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
|
|
31
|
+
];
|
|
32
|
+
return {
|
|
33
|
+
...result,
|
|
34
|
+
diagnostics,
|
|
35
|
+
fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function readPackageJson(packageJsonPath) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
41
|
+
return isRecord(parsed) ? parsed : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result) {
|
|
48
|
+
return {
|
|
49
|
+
code: 'missing-dependency',
|
|
50
|
+
message: '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
|
|
51
|
+
path: packageJsonPath,
|
|
52
|
+
profile: result.profile,
|
|
53
|
+
ruleId: DEVTOOLS_REQUIRED_RULE_ID,
|
|
54
|
+
severity: 'error',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function expectsDevtools(packageJson) {
|
|
58
|
+
const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
|
|
59
|
+
if (scripts === null) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return Object.values(scripts).some((scriptValue) => isNonEmptyString(scriptValue) &&
|
|
63
|
+
(scriptValue.includes('ankhorage-eslint') ||
|
|
64
|
+
scriptValue.includes('ankhorage-prettier') ||
|
|
65
|
+
scriptValue.includes('ankhorage-knip')));
|
|
66
|
+
}
|
|
67
|
+
function hasDependency(dependencyMap, dependencyName) {
|
|
68
|
+
return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
|
|
69
|
+
}
|
|
70
|
+
function isNonEmptyString(value) {
|
|
71
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
72
|
+
}
|
|
73
|
+
function isRecord(value) {
|
|
74
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=dependencyPolicyAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyPolicyAnalysis.js","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport {\n analyzeDoctorTarget as analyzeBaseDoctorTarget,\n type DoctorAnalysisRequest,\n type DoctorAnalysisResult,\n} from './analysis.js';\nimport { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\n\nconst ANKH_PACKAGE_NAME = '@ankhorage/ankh';\nconst DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';\nconst DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';\n\nexport async function analyzeDoctorTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));\n}\n\nexport async function analyzeDoctorTargetWithCliLayout(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyDevtoolsDependencyPlacementPolicy(\n await analyzeBaseDoctorTargetWithCliLayout(request),\n );\n}\n\nasync function applyDevtoolsDependencyPlacementPolicy(\n result: DoctorAnalysisResult,\n): Promise<DoctorAnalysisResult> {\n if (!result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {\n return result;\n }\n\n const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;\n const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);\n const diagnosticsWithoutPlacementRule = result.diagnostics.filter(\n (diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID,\n );\n const diagnostics = hasRuntimeDevtools\n ? diagnosticsWithoutPlacementRule\n : [\n ...diagnosticsWithoutPlacementRule,\n createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),\n ];\n\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction createMissingRuntimeDevtoolsDiagnostic(\n packageJsonPath: string,\n result: DoctorAnalysisResult,\n): DoctorDiagnostic {\n return {\n code: 'missing-dependency',\n message:\n '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',\n path: packageJsonPath,\n profile: result.profile,\n ruleId: DEVTOOLS_REQUIRED_RULE_ID,\n severity: 'error',\n };\n}\n\nfunction expectsDevtools(packageJson: Record<string, unknown>): boolean {\n const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;\n if (scripts === null) {\n return false;\n }\n\n return Object.values(scripts).some(\n (scriptValue) =>\n isNonEmptyString(scriptValue) &&\n (scriptValue.includes('ankhorage-eslint') ||\n scriptValue.includes('ankhorage-prettier') ||\n scriptValue.includes('ankhorage-knip')),\n );\n}\n\nfunction hasDependency(\n dependencyMap: Record<string, unknown> | null,\n dependencyName: string,\n): boolean {\n return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export type { DoctorAnalysisRequest, DoctorAnalysisResult, DoctorFixPlan, DoctorPlannedChange, DoctorPlannedChangeKind, DoctorTargetCheck, DoctorTargetMode, } from './analysis.js';
|
|
2
|
-
export { analyzeDoctorTarget } from './analysis.js';
|
|
3
2
|
export { createDoctorRuntimeProvider, type CreateDoctorRuntimeProviderOptions, } from './cli/index.js';
|
|
4
3
|
export { runCli } from './cli/standalone.js';
|
|
5
|
-
export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
6
4
|
export type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
|
|
7
5
|
export { createDefaultCommandContext } from './commandContext.js';
|
|
6
|
+
export { analyzeDoctorTarget, analyzeDoctorTargetWithCliLayout, } from './dependencyPolicyAnalysis.js';
|
|
8
7
|
export type { DoctorDiagnostic, DoctorDiagnosticCode, DoctorDiagnosticSeverity, DoctorPolicyProfile, DoctorRuleId, } from './diagnostics.js';
|
|
9
8
|
export { analyzeAppManifest, analyzeAppManifestFile, analyzeAppManifestTarget, } from './manifestAnalysis.js';
|
|
10
9
|
export { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_METADATA, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from './packageMetadata.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,2BAA2B,EAC3B,KAAK,kCAAkC,GACxC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export { analyzeDoctorTarget } from './analysis.js';
|
|
2
1
|
export { createDoctorRuntimeProvider, } from './cli/index.js';
|
|
3
2
|
export { runCli } from './cli/standalone.js';
|
|
4
|
-
export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
5
3
|
export { createDefaultCommandContext } from './commandContext.js';
|
|
4
|
+
export { analyzeDoctorTarget, analyzeDoctorTargetWithCliLayout, } from './dependencyPolicyAnalysis.js';
|
|
6
5
|
export { analyzeAppManifest, analyzeAppManifestFile, analyzeAppManifestTarget, } from './manifestAnalysis.js';
|
|
7
6
|
export { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_METADATA, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from './packageMetadata.js';
|
|
8
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,+BAA+B,CAAC;AAQvC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC","sourcesContent":["export type {\n DoctorAnalysisRequest,\n DoctorAnalysisResult,\n DoctorFixPlan,\n DoctorPlannedChange,\n DoctorPlannedChangeKind,\n DoctorTargetCheck,\n DoctorTargetMode,\n} from './analysis.js';\nexport {\n createDoctorRuntimeProvider,\n type CreateDoctorRuntimeProviderOptions,\n} from './cli/index.js';\nexport { runCli } from './cli/standalone.js';\nexport type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';\nexport { createDefaultCommandContext } from './commandContext.js';\nexport {\n analyzeDoctorTarget,\n analyzeDoctorTargetWithCliLayout,\n} from './dependencyPolicyAnalysis.js';\nexport type {\n DoctorDiagnostic,\n DoctorDiagnosticCode,\n DoctorDiagnosticSeverity,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\nexport {\n analyzeAppManifest,\n analyzeAppManifestFile,\n analyzeAppManifestTarget,\n} from './manifestAnalysis.js';\nexport {\n DOCTOR_CAPABILITIES,\n DOCTOR_COMMAND_CATEGORY,\n DOCTOR_PACKAGE_METADATA,\n DOCTOR_PACKAGE_NAME,\n DOCTOR_PACKAGE_VERSION,\n} from './packageMetadata.js';\n"]}
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AnkhCommandHandler, AnkhRuntimeCommandProvider } from '@ankhorage/ankh';
|
|
2
2
|
|
|
3
|
-
import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
|
|
4
3
|
import {
|
|
5
4
|
createProviderCommandDescriptors,
|
|
6
5
|
DOCTOR_COMMANDS,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
runDoctorCommand,
|
|
9
8
|
type RunDoctorCommandImpl,
|
|
10
9
|
} from '../commands.js';
|
|
10
|
+
import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
|
|
11
11
|
import {
|
|
12
12
|
DOCTOR_CAPABILITIES,
|
|
13
13
|
DOCTOR_COMMAND_CATEGORY,
|
package/src/cli/standalone.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
|
|
4
3
|
import type { DoctorCommandContext, DoctorCommandRunResult } from '../commandContext.js';
|
|
5
4
|
import { createDefaultCommandContext } from '../commandContext.js';
|
|
6
5
|
import {
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
runDoctorCommand,
|
|
12
11
|
type RunDoctorCommandImpl,
|
|
13
12
|
} from '../commands.js';
|
|
13
|
+
import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
|
|
14
14
|
|
|
15
15
|
export interface DoctorCliOptions {
|
|
16
16
|
readonly context?: DoctorCommandContext;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
analyzeDoctorTarget as analyzeBaseDoctorTarget,
|
|
6
|
+
type DoctorAnalysisRequest,
|
|
7
|
+
type DoctorAnalysisResult,
|
|
8
|
+
} from './analysis.js';
|
|
9
|
+
import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
10
|
+
import type { DoctorDiagnostic } from './diagnostics.js';
|
|
11
|
+
|
|
12
|
+
const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
|
|
13
|
+
const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
|
|
14
|
+
const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
|
|
15
|
+
|
|
16
|
+
export async function analyzeDoctorTarget(
|
|
17
|
+
request: DoctorAnalysisRequest,
|
|
18
|
+
): Promise<DoctorAnalysisResult> {
|
|
19
|
+
return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function analyzeDoctorTargetWithCliLayout(
|
|
23
|
+
request: DoctorAnalysisRequest,
|
|
24
|
+
): Promise<DoctorAnalysisResult> {
|
|
25
|
+
return applyDevtoolsDependencyPlacementPolicy(
|
|
26
|
+
await analyzeBaseDoctorTargetWithCliLayout(request),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function applyDevtoolsDependencyPlacementPolicy(
|
|
31
|
+
result: DoctorAnalysisResult,
|
|
32
|
+
): Promise<DoctorAnalysisResult> {
|
|
33
|
+
if (!result.hasPackageJson) {
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const packageJsonPath = path.join(result.targetPath, 'package.json');
|
|
38
|
+
const packageJson = await readPackageJson(packageJsonPath);
|
|
39
|
+
if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
|
|
44
|
+
const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
|
|
45
|
+
const diagnosticsWithoutPlacementRule = result.diagnostics.filter(
|
|
46
|
+
(diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID,
|
|
47
|
+
);
|
|
48
|
+
const diagnostics = hasRuntimeDevtools
|
|
49
|
+
? diagnosticsWithoutPlacementRule
|
|
50
|
+
: [
|
|
51
|
+
...diagnosticsWithoutPlacementRule,
|
|
52
|
+
createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
...result,
|
|
57
|
+
diagnostics,
|
|
58
|
+
fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;
|
|
65
|
+
return isRecord(parsed) ? parsed : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createMissingRuntimeDevtoolsDiagnostic(
|
|
72
|
+
packageJsonPath: string,
|
|
73
|
+
result: DoctorAnalysisResult,
|
|
74
|
+
): DoctorDiagnostic {
|
|
75
|
+
return {
|
|
76
|
+
code: 'missing-dependency',
|
|
77
|
+
message:
|
|
78
|
+
'@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
|
|
79
|
+
path: packageJsonPath,
|
|
80
|
+
profile: result.profile,
|
|
81
|
+
ruleId: DEVTOOLS_REQUIRED_RULE_ID,
|
|
82
|
+
severity: 'error',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function expectsDevtools(packageJson: Record<string, unknown>): boolean {
|
|
87
|
+
const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
|
|
88
|
+
if (scripts === null) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return Object.values(scripts).some(
|
|
93
|
+
(scriptValue) =>
|
|
94
|
+
isNonEmptyString(scriptValue) &&
|
|
95
|
+
(scriptValue.includes('ankhorage-eslint') ||
|
|
96
|
+
scriptValue.includes('ankhorage-prettier') ||
|
|
97
|
+
scriptValue.includes('ankhorage-knip')),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function hasDependency(
|
|
102
|
+
dependencyMap: Record<string, unknown> | null,
|
|
103
|
+
dependencyName: string,
|
|
104
|
+
): boolean {
|
|
105
|
+
return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
109
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
113
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
114
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,15 +7,17 @@ export type {
|
|
|
7
7
|
DoctorTargetCheck,
|
|
8
8
|
DoctorTargetMode,
|
|
9
9
|
} from './analysis.js';
|
|
10
|
-
export { analyzeDoctorTarget } from './analysis.js';
|
|
11
10
|
export {
|
|
12
11
|
createDoctorRuntimeProvider,
|
|
13
12
|
type CreateDoctorRuntimeProviderOptions,
|
|
14
13
|
} from './cli/index.js';
|
|
15
14
|
export { runCli } from './cli/standalone.js';
|
|
16
|
-
export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
17
15
|
export type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
|
|
18
16
|
export { createDefaultCommandContext } from './commandContext.js';
|
|
17
|
+
export {
|
|
18
|
+
analyzeDoctorTarget,
|
|
19
|
+
analyzeDoctorTargetWithCliLayout,
|
|
20
|
+
} from './dependencyPolicyAnalysis.js';
|
|
19
21
|
export type {
|
|
20
22
|
DoctorDiagnostic,
|
|
21
23
|
DoctorDiagnosticCode,
|