@rsdk/yarn.constraints 6.0.0-next.4 → 6.0.0-next.41
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/DEPENDENCY_MODEL.md +460 -0
- package/README.MD +85 -21
- package/__tests__/compatibility.test.ts +321 -0
- package/__tests__/config-validation.test.ts +42 -0
- package/__tests__/engine.test.ts +1107 -0
- package/__tests__/fixtures/imports/bin.js +4 -0
- package/__tests__/fixtures/imports/export-entry.mjs +1 -0
- package/__tests__/fixtures/imports/lib/lib-entry.js +3 -0
- package/__tests__/fixtures/imports/root-entry.js +4 -0
- package/__tests__/fixtures/imports/rules/transitive.js +3 -0
- package/__tests__/fixtures/imports/src/common.cjs +3 -0
- package/__tests__/fixtures/imports/src/common.cts +3 -0
- package/__tests__/fixtures/imports/src/component.tsx +4 -0
- package/__tests__/fixtures/imports/src/index.ts +13 -0
- package/__tests__/fixtures/imports/src/module.mjs +3 -0
- package/__tests__/fixtures/imports/src/module.mts +3 -0
- package/__tests__/fixtures/imports/src/plain.js +3 -0
- package/__tests__/fixtures/imports/src/test-only-usage.ts +1 -0
- package/__tests__/fixtures/imports/test/outside.ts +3 -0
- package/__tests__/imports.test.ts +218 -0
- package/__tests__/manifest-writer.test.ts +157 -0
- package/dist/ansi.d.ts +9 -0
- package/dist/ansi.js +24 -0
- package/dist/ansi.js.map +1 -0
- package/dist/bin/depdoc.d.ts +2 -0
- package/dist/bin/depdoc.js +157 -0
- package/dist/bin/depdoc.js.map +1 -0
- package/dist/collectors/config.d.ts +2 -0
- package/dist/collectors/config.js +28 -0
- package/dist/collectors/config.js.map +1 -0
- package/dist/collectors/external-metadata.d.ts +5 -0
- package/dist/collectors/external-metadata.js +110 -0
- package/dist/collectors/external-metadata.js.map +1 -0
- package/dist/collectors/package-extensions.d.ts +3 -0
- package/dist/collectors/package-extensions.js +43 -0
- package/dist/collectors/package-extensions.js.map +1 -0
- package/dist/collectors/type-providers.d.ts +3 -0
- package/dist/collectors/type-providers.js +46 -0
- package/dist/collectors/type-providers.js.map +1 -0
- package/dist/collectors/workspaces.d.ts +2 -0
- package/dist/collectors/workspaces.js +90 -0
- package/dist/collectors/workspaces.js.map +1 -0
- package/dist/dependency-model.d.ts +11 -0
- package/dist/dependency-model.js +18 -0
- package/dist/dependency-model.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +13 -33
- package/dist/index.js.map +1 -1
- package/dist/lib/imports.d.ts +11 -0
- package/dist/lib/imports.js +342 -0
- package/dist/lib/imports.js.map +1 -0
- package/dist/lib/package-json.d.ts +21 -0
- package/dist/lib/package-json.js +32 -0
- package/dist/lib/package-json.js.map +1 -0
- package/dist/model/config-validation.d.ts +6 -0
- package/dist/model/config-validation.js +31 -0
- package/dist/model/config-validation.js.map +1 -0
- package/dist/model/diagnostics.d.ts +4 -0
- package/dist/model/diagnostics.js +295 -0
- package/dist/model/diagnostics.js.map +1 -0
- package/dist/model/engine.d.ts +5 -0
- package/dist/model/engine.js +52 -0
- package/dist/model/engine.js.map +1 -0
- package/dist/model/expected.d.ts +20 -0
- package/dist/model/expected.js +89 -0
- package/dist/model/expected.js.map +1 -0
- package/dist/model/peer-propagation.d.ts +2 -0
- package/dist/model/peer-propagation.js +124 -0
- package/dist/model/peer-propagation.js.map +1 -0
- package/dist/model/placement.d.ts +9 -0
- package/dist/model/placement.js +210 -0
- package/dist/model/placement.js.map +1 -0
- package/dist/model/rules.d.ts +15 -0
- package/dist/model/rules.js +52 -0
- package/dist/model/rules.js.map +1 -0
- package/dist/model/types.d.ts +118 -0
- package/dist/model/types.js +9 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/versions.d.ts +3 -0
- package/dist/model/versions.js +77 -0
- package/dist/model/versions.js.map +1 -0
- package/dist/reporting.d.ts +3 -0
- package/dist/reporting.js +80 -0
- package/dist/reporting.js.map +1 -0
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +70 -0
- package/dist/runner.js.map +1 -0
- package/dist/writer/manifest-writer.d.ts +2 -0
- package/dist/writer/manifest-writer.js +72 -0
- package/dist/writer/manifest-writer.js.map +1 -0
- package/eslint.config.cjs +3 -0
- package/jest.config.js +1 -0
- package/package.json +7 -3
- package/src/ansi.ts +23 -0
- package/src/bin/depdoc.ts +213 -0
- package/src/collectors/config.ts +33 -0
- package/src/collectors/external-metadata.ts +148 -0
- package/src/collectors/package-extensions.ts +52 -0
- package/src/collectors/type-providers.ts +51 -0
- package/src/collectors/workspaces.ts +107 -0
- package/src/dependency-model.ts +26 -0
- package/src/index.ts +28 -45
- package/src/lib/imports.ts +435 -0
- package/src/lib/package-json.ts +46 -0
- package/src/model/config-validation.ts +49 -0
- package/src/model/diagnostics.ts +358 -0
- package/src/model/engine.ts +120 -0
- package/src/model/expected.ts +141 -0
- package/src/model/peer-propagation.ts +199 -0
- package/src/model/placement.ts +378 -0
- package/src/model/rules.ts +85 -0
- package/src/model/types.ts +165 -0
- package/src/model/versions.ts +114 -0
- package/src/reporting.ts +117 -0
- package/src/runner.ts +102 -0
- package/src/writer/manifest-writer.ts +111 -0
- package/tsconfig.build.json +1 -0
- package/tsconfig.json +6 -1
- package/dist/constraint-schema.d.ts +0 -1
- package/dist/constraint-schema.js +0 -17
- package/dist/constraint-schema.js.map +0 -1
- package/dist/dependency-checker.d.ts +0 -8
- package/dist/dependency-checker.js +0 -40
- package/dist/dependency-checker.js.map +0 -1
- package/src/constraint-schema.ts +0 -20
- package/src/dependency-checker.ts +0 -41
package/dist/runner.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runDependencyModel = runDependencyModel;
|
|
4
|
+
/**
|
|
5
|
+
* Dependency model runner.
|
|
6
|
+
*
|
|
7
|
+
* The runner coordinates impure work: locating the repo root, collecting facts,
|
|
8
|
+
* iterating external metadata discovery, invoking the pure engine, and applying
|
|
9
|
+
* manifest fixes when requested.
|
|
10
|
+
*/
|
|
11
|
+
const config_1 = require("./collectors/config");
|
|
12
|
+
const external_metadata_1 = require("./collectors/external-metadata");
|
|
13
|
+
const package_extensions_1 = require("./collectors/package-extensions");
|
|
14
|
+
const type_providers_1 = require("./collectors/type-providers");
|
|
15
|
+
const workspaces_1 = require("./collectors/workspaces");
|
|
16
|
+
const package_json_1 = require("./lib/package-json");
|
|
17
|
+
const engine_1 = require("./model/engine");
|
|
18
|
+
const manifest_writer_1 = require("./writer/manifest-writer");
|
|
19
|
+
function runDependencyModel(options = {}) {
|
|
20
|
+
const rootDir = options.rootDir ?? (0, package_json_1.findMonorepoRoot)(process.cwd());
|
|
21
|
+
if (!rootDir) {
|
|
22
|
+
throw new Error('Unable to find monorepo root');
|
|
23
|
+
}
|
|
24
|
+
const config = (0, config_1.loadConfig)(rootDir, options.constraintsPath);
|
|
25
|
+
const rules = config.rules ?? [];
|
|
26
|
+
const withDts = options.withDts === true;
|
|
27
|
+
const contexts = (0, workspaces_1.loadWorkspaces)(rootDir, withDts);
|
|
28
|
+
const packageExtensions = (0, package_extensions_1.loadPackageExtensions)(rootDir);
|
|
29
|
+
const workspaceNames = new Set(contexts.filter((ws) => !ws.isRoot).map((ws) => ws.name));
|
|
30
|
+
const typeProviderPackages = (0, type_providers_1.collectTypeProviderPackages)(rootDir, contexts[0].pkg, rules);
|
|
31
|
+
const externalPeerMetadata = new Map();
|
|
32
|
+
const externalTypeMetadata = new Map();
|
|
33
|
+
const providerIdents = (0, external_metadata_1.collectExternalProviderIdents)(contexts, workspaceNames);
|
|
34
|
+
let output = null;
|
|
35
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
36
|
+
(0, external_metadata_1.collectExternalMetadata)(rootDir, packageExtensions, providerIdents, externalPeerMetadata, externalTypeMetadata);
|
|
37
|
+
output = (0, engine_1.deriveDependencyModel)({
|
|
38
|
+
workspaces: contexts,
|
|
39
|
+
externalPeerMetadata,
|
|
40
|
+
externalTypeMetadata,
|
|
41
|
+
typeProviderPackages,
|
|
42
|
+
rules,
|
|
43
|
+
...(withDts ? { withDts: true } : {}),
|
|
44
|
+
});
|
|
45
|
+
let changed = false;
|
|
46
|
+
for (const ident of (0, external_metadata_1.collectExternalProviderIdents)(contexts, workspaceNames, output.expected)) {
|
|
47
|
+
if (providerIdents.has(ident))
|
|
48
|
+
continue;
|
|
49
|
+
providerIdents.add(ident);
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
if (!changed)
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (!output) {
|
|
56
|
+
throw new Error('Unable to derive dependency model');
|
|
57
|
+
}
|
|
58
|
+
if (options.fix && output.violations.length > 0) {
|
|
59
|
+
(0, manifest_writer_1.writeFixes)(rootDir, output.expected);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
rootDir,
|
|
63
|
+
violations: output.violations,
|
|
64
|
+
warnings: output.warnings,
|
|
65
|
+
contexts,
|
|
66
|
+
expected: output.expected,
|
|
67
|
+
config,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
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.writeFixes = writeFixes;
|
|
7
|
+
/**
|
|
8
|
+
* Manifest writer.
|
|
9
|
+
*
|
|
10
|
+
* The writer is the only module that mutates package.json files. It preserves
|
|
11
|
+
* unrelated fields, rewrites dependency sections from the expected graph, and
|
|
12
|
+
* skips writes when the normalized section content is unchanged.
|
|
13
|
+
*/
|
|
14
|
+
const node_fs_1 = require("node:fs");
|
|
15
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
16
|
+
const types_1 = require("../model/types");
|
|
17
|
+
function sortedRecord(map) {
|
|
18
|
+
return Object.fromEntries([...map.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
19
|
+
}
|
|
20
|
+
function recordsEqual(actual, expected) {
|
|
21
|
+
const actualEntries = Object.entries(actual ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
22
|
+
const expectedEntries = [...expected.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
23
|
+
return JSON.stringify(actualEntries) === JSON.stringify(expectedEntries);
|
|
24
|
+
}
|
|
25
|
+
function peerDependenciesMetaEqual(actual, expected) {
|
|
26
|
+
const actualEntries = Object.entries(actual ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
27
|
+
const expectedEntries = Object.entries(expected ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
28
|
+
return JSON.stringify(actualEntries) === JSON.stringify(expectedEntries);
|
|
29
|
+
}
|
|
30
|
+
function prunePeerDependenciesMeta(pkg, expectedPeerDependencies) {
|
|
31
|
+
if (pkg.peerDependenciesMeta === undefined)
|
|
32
|
+
return false;
|
|
33
|
+
const nextMeta = Object.fromEntries(Object.entries(pkg.peerDependenciesMeta)
|
|
34
|
+
.filter(([depIdent]) => expectedPeerDependencies.has(depIdent))
|
|
35
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
36
|
+
if (Object.keys(nextMeta).length === 0) {
|
|
37
|
+
delete pkg.peerDependenciesMeta;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (peerDependenciesMetaEqual(pkg.peerDependenciesMeta, nextMeta)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
pkg.peerDependenciesMeta = nextMeta;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
function writeFixes(rootDir, expectedByLocation) {
|
|
47
|
+
for (const expected of expectedByLocation.values()) {
|
|
48
|
+
const pkg = expected.workspace.pkg;
|
|
49
|
+
let changed = false;
|
|
50
|
+
for (const section of types_1.SECTIONS) {
|
|
51
|
+
if (expected.sections[section].size === 0) {
|
|
52
|
+
if (pkg[section] !== undefined) {
|
|
53
|
+
delete pkg[section];
|
|
54
|
+
changed = true;
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (!recordsEqual(pkg[section], expected.sections[section])) {
|
|
59
|
+
pkg[section] = sortedRecord(expected.sections[section]);
|
|
60
|
+
changed = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
changed =
|
|
64
|
+
prunePeerDependenciesMeta(pkg, expected.sections.peerDependencies) ||
|
|
65
|
+
changed;
|
|
66
|
+
if (changed) {
|
|
67
|
+
const pkgPath = node_path_1.default.join(rootDir, expected.workspace.location, 'package.json');
|
|
68
|
+
(0, node_fs_1.writeFileSync)(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=manifest-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-writer.js","sourceRoot":"","sources":["../../src/writer/manifest-writer.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('@rsdk/jest/jest.config');
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsdk/yarn.constraints",
|
|
3
|
-
"
|
|
3
|
+
"role": "cli",
|
|
4
|
+
"version": "6.0.0-next.41",
|
|
4
5
|
"description": "Useful common classes, functions and types",
|
|
5
6
|
"license": "Apache License 2.0",
|
|
6
7
|
"publishConfig": {
|
|
@@ -10,9 +11,12 @@
|
|
|
10
11
|
"url": "https://github.com/R-Vision/rsdk"
|
|
11
12
|
},
|
|
12
13
|
"main": "dist/index.js",
|
|
14
|
+
"bin": {
|
|
15
|
+
"depdoc": "dist/bin/depdoc.js"
|
|
16
|
+
},
|
|
13
17
|
"dependencies": {
|
|
14
|
-
"
|
|
18
|
+
"typescript": "5.7.3",
|
|
15
19
|
"yaml": "^2.6.1"
|
|
16
20
|
},
|
|
17
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "e6e580cf9f7b89e7b236530d767db5d6429c9c6f"
|
|
18
22
|
}
|
package/src/ansi.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared ANSI styling helpers for CLI output.
|
|
3
|
+
*
|
|
4
|
+
* Keep terminal escape codes centralized here so command/reporting modules can
|
|
5
|
+
* add restrained color without duplicating formatting details or hard-coding
|
|
6
|
+
* ANSI sequences throughout the codebase.
|
|
7
|
+
*/
|
|
8
|
+
const enabled =
|
|
9
|
+
process.env.NO_COLOR === undefined && process.env.FORCE_COLOR !== '0';
|
|
10
|
+
|
|
11
|
+
function wrap(open: string, close: string, value: string): string {
|
|
12
|
+
return enabled ? `${open}${value}${close}` : value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ansi = {
|
|
16
|
+
bold: (value: string): string => wrap('\u001B[1m', '\u001B[22m', value),
|
|
17
|
+
dim: (value: string): string => wrap('\u001B[2m', '\u001B[22m', value),
|
|
18
|
+
red: (value: string): string => wrap('\u001B[31m', '\u001B[39m', value),
|
|
19
|
+
green: (value: string): string => wrap('\u001B[32m', '\u001B[39m', value),
|
|
20
|
+
yellow: (value: string): string => wrap('\u001B[33m', '\u001B[39m', value),
|
|
21
|
+
cyan: (value: string): string => wrap('\u001B[36m', '\u001B[39m', value),
|
|
22
|
+
magenta: (value: string): string => wrap('\u001B[35m', '\u001B[39m', value),
|
|
23
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `depdoc` command-line entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* The CLI only parses commands and orchestrates high-level steps. Dependency
|
|
6
|
+
* placement decisions live in the model engine, while filesystem collection and
|
|
7
|
+
* manifest writes go through the runner.
|
|
8
|
+
*/
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
import { ansi } from '../ansi';
|
|
14
|
+
import {
|
|
15
|
+
explainDependency,
|
|
16
|
+
formatDependencyModelResult,
|
|
17
|
+
runDependencyModel,
|
|
18
|
+
} from '../dependency-model';
|
|
19
|
+
|
|
20
|
+
function printUsage(): void {
|
|
21
|
+
console.error(`${ansi.bold('Usage:')}
|
|
22
|
+
${ansi.cyan('depdoc')} check [--with-dts]
|
|
23
|
+
${ansi.cyan('depdoc')} fix [--with-dts]
|
|
24
|
+
${ansi.cyan('depdoc')} explain <workspace> <dependency>
|
|
25
|
+
${ansi.cyan('depdoc')} doctor`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function spawnStep(label: string, cmd: string, cwd: string): void {
|
|
29
|
+
console.error(`\n${ansi.cyan('──')} ${ansi.bold(label)}`);
|
|
30
|
+
console.error(`${ansi.dim('$')} ${ansi.cyan(cmd)}`);
|
|
31
|
+
execSync(cmd, { cwd, stdio: 'inherit' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function checkPeerRequirements(cwd: string): boolean {
|
|
35
|
+
const cmd = 'yarn explain peer-requirements';
|
|
36
|
+
|
|
37
|
+
console.error(`\n${ansi.cyan('──')} ${ansi.bold('yarn peer requirements')}`);
|
|
38
|
+
console.error(`${ansi.dim('$')} ${ansi.cyan(cmd)}`);
|
|
39
|
+
|
|
40
|
+
let output: string;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
output = execSync(cmd, {
|
|
44
|
+
cwd,
|
|
45
|
+
encoding: 'utf8',
|
|
46
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const childError = error as { stdout?: string; stderr?: string };
|
|
50
|
+
if (childError.stdout) process.stderr.write(childError.stdout);
|
|
51
|
+
if (childError.stderr) process.stderr.write(childError.stderr);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const failedRequirements = output
|
|
56
|
+
.split(/\r?\n/)
|
|
57
|
+
.filter((line) => line.includes('✘'));
|
|
58
|
+
|
|
59
|
+
if (failedRequirements.length === 0) {
|
|
60
|
+
console.error(ansi.green('✓ peer requirements are satisfied'));
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.error(failedRequirements.join('\n'));
|
|
65
|
+
console.error(
|
|
66
|
+
ansi.red(`${failedRequirements.length} peer requirement(s) failed`),
|
|
67
|
+
);
|
|
68
|
+
console.error(
|
|
69
|
+
ansi.dim('Run `yarn explain peer-requirements <hash>` for details.'),
|
|
70
|
+
);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function snapshotPackageJsons(
|
|
75
|
+
result: ReturnType<typeof runDependencyModel>,
|
|
76
|
+
): string {
|
|
77
|
+
return [...result.expected.keys()]
|
|
78
|
+
.sort()
|
|
79
|
+
.map((location) =>
|
|
80
|
+
readFileSync(path.join(result.rootDir, location, 'package.json'), 'utf8'),
|
|
81
|
+
)
|
|
82
|
+
.join('\0');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function main(): void {
|
|
86
|
+
const rawArgs = process.argv.slice(2);
|
|
87
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h')) {
|
|
88
|
+
printUsage();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const positional = rawArgs.filter((a) => !a.startsWith('--'));
|
|
93
|
+
const [command, firstArg, secondArg] = positional;
|
|
94
|
+
const withDts = rawArgs.includes('--with-dts');
|
|
95
|
+
|
|
96
|
+
if (!command) {
|
|
97
|
+
printUsage();
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (command === 'check') {
|
|
102
|
+
const result = runDependencyModel({ withDts });
|
|
103
|
+
|
|
104
|
+
console.error(formatDependencyModelResult(result));
|
|
105
|
+
process.exit(result.violations.length === 0 ? 0 : 1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (command === 'fix') {
|
|
109
|
+
const before = runDependencyModel({ fix: true, withDts });
|
|
110
|
+
const after = runDependencyModel({ withDts });
|
|
111
|
+
const fixed = Math.max(
|
|
112
|
+
0,
|
|
113
|
+
before.violations.length - after.violations.length,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
console.error(formatDependencyModelResult(after));
|
|
117
|
+
if (fixed > 0) {
|
|
118
|
+
console.error(
|
|
119
|
+
ansi.green(`fixed ${fixed} dependency model violation(s)`),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
process.exit(after.violations.length === 0 ? 0 : 1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (command === 'explain') {
|
|
126
|
+
if (!firstArg || !secondArg) {
|
|
127
|
+
printUsage();
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const result = runDependencyModel();
|
|
131
|
+
|
|
132
|
+
console.log(explainDependency(result, firstArg, secondArg));
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (command === 'doctor') {
|
|
137
|
+
runDoctor();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
printUsage();
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function runDoctor(): void {
|
|
146
|
+
console.error(`\n${ansi.cyan('──')} ${ansi.bold('depdoc fix')}`);
|
|
147
|
+
const fixResult = runDependencyModel({ fix: true });
|
|
148
|
+
const afterFix = runDependencyModel();
|
|
149
|
+
const fixed = Math.max(
|
|
150
|
+
0,
|
|
151
|
+
fixResult.violations.length - afterFix.violations.length,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
console.error(formatDependencyModelResult(afterFix));
|
|
155
|
+
if (fixed > 0) console.error(ansi.green(`fixed ${fixed} violation(s)`));
|
|
156
|
+
|
|
157
|
+
const { rootDir, config } = afterFix;
|
|
158
|
+
|
|
159
|
+
spawnStep('yarn install', 'yarn install', rootDir);
|
|
160
|
+
|
|
161
|
+
if (config.doctor?.buildCmd) {
|
|
162
|
+
spawnStep('build', config.doctor.buildCmd, rootDir);
|
|
163
|
+
} else {
|
|
164
|
+
console.error(
|
|
165
|
+
`\n${ansi.cyan('──')} ${ansi.yellow('build skipped')} ${ansi.dim('(doctor.buildCmd not configured in .constraints.yml)')}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const beforeDtsFixSnapshot = snapshotPackageJsons(afterFix);
|
|
170
|
+
|
|
171
|
+
console.error(`\n${ansi.cyan('──')} ${ansi.bold('depdoc fix --with-dts')}`);
|
|
172
|
+
const dtsFix = runDependencyModel({ fix: true, withDts: true });
|
|
173
|
+
const afterDtsFix = runDependencyModel({ withDts: true });
|
|
174
|
+
const afterDtsFixSnapshot = snapshotPackageJsons(afterDtsFix);
|
|
175
|
+
const dtsFixed = Math.max(
|
|
176
|
+
0,
|
|
177
|
+
dtsFix.violations.length - afterDtsFix.violations.length,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
console.error(formatDependencyModelResult(afterDtsFix));
|
|
181
|
+
if (dtsFixed > 0) {
|
|
182
|
+
console.error(
|
|
183
|
+
ansi.green(
|
|
184
|
+
`fixed ${dtsFixed} additional violation(s) from .d.ts surface`,
|
|
185
|
+
),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (beforeDtsFixSnapshot !== afterDtsFixSnapshot) {
|
|
190
|
+
spawnStep('yarn install (post-dts-fix)', 'yarn install', rootDir);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!checkPeerRequirements(rootDir)) process.exit(1);
|
|
194
|
+
|
|
195
|
+
if (config.doctor?.lintCmd) {
|
|
196
|
+
spawnStep('lint', config.doctor.lintCmd, rootDir);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (config.doctor?.typecheckCmd) {
|
|
200
|
+
spawnStep('typecheck', config.doctor.typecheckCmd, rootDir);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const finalViolations = afterDtsFix.violations.length;
|
|
204
|
+
|
|
205
|
+
console.error(
|
|
206
|
+
finalViolations === 0
|
|
207
|
+
? `\n${ansi.green('✓')} ${ansi.bold('doctor: all checks passed')}`
|
|
208
|
+
: `\n${ansi.red('✖')} ${ansi.bold(`doctor: ${finalViolations} violation(s) remain`)}`,
|
|
209
|
+
);
|
|
210
|
+
process.exit(finalViolations === 0 ? 0 : 1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
main();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constraints configuration collector.
|
|
3
|
+
*
|
|
4
|
+
* This module is the only place that resolves and parses `.constraints.yml`.
|
|
5
|
+
* It returns raw model configuration; rule semantics are handled later by the
|
|
6
|
+
* pure model layer.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import yaml from 'yaml';
|
|
11
|
+
|
|
12
|
+
import { assertValidDependencyModelConfig } from '../model/config-validation';
|
|
13
|
+
import type { DependencyModelConfig } from '../model/types';
|
|
14
|
+
|
|
15
|
+
export function loadConfig(
|
|
16
|
+
rootDir: string,
|
|
17
|
+
constraintsPath?: string,
|
|
18
|
+
): DependencyModelConfig {
|
|
19
|
+
const filepath =
|
|
20
|
+
constraintsPath ??
|
|
21
|
+
process.env.RSDK_YARN_CONSTRAINTS_CFG ??
|
|
22
|
+
path.join(rootDir, '.rsdk/.constraints.yml');
|
|
23
|
+
|
|
24
|
+
if (!existsSync(filepath)) return { rules: [] };
|
|
25
|
+
|
|
26
|
+
const config = yaml.parse(
|
|
27
|
+
readFileSync(filepath, 'utf8'),
|
|
28
|
+
) as DependencyModelConfig;
|
|
29
|
+
|
|
30
|
+
assertValidDependencyModelConfig(config);
|
|
31
|
+
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installed external package metadata collector.
|
|
3
|
+
*
|
|
4
|
+
* Peer and bundled-type metadata are read from installed packages and Yarn
|
|
5
|
+
* packageExtensions. The engine consumes the resulting maps as facts, keeping
|
|
6
|
+
* node_modules access outside the pure model.
|
|
7
|
+
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { readPackageJson } from '../lib/package-json';
|
|
11
|
+
import type {
|
|
12
|
+
ExpectedWorkspace,
|
|
13
|
+
PackageExtension,
|
|
14
|
+
WorkspaceContext,
|
|
15
|
+
} from '../model/types';
|
|
16
|
+
import { SECTIONS } from '../model/types';
|
|
17
|
+
|
|
18
|
+
import { getMatchingPackageExtensions } from './package-extensions';
|
|
19
|
+
|
|
20
|
+
const externalPeersCache = new Map<string, Map<string, string> | null>();
|
|
21
|
+
|
|
22
|
+
function getExternalRequiredPeers(
|
|
23
|
+
rootDir: string,
|
|
24
|
+
packageExtensions: Map<string, PackageExtension>,
|
|
25
|
+
extIdent: string,
|
|
26
|
+
): Map<string, string> | null {
|
|
27
|
+
const cacheKey = `${rootDir}:${extIdent}`;
|
|
28
|
+
if (externalPeersCache.has(cacheKey))
|
|
29
|
+
return externalPeersCache.get(cacheKey)!;
|
|
30
|
+
|
|
31
|
+
let peers: Record<string, string>;
|
|
32
|
+
let meta: Record<string, { optional?: boolean }>;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const pkg = readPackageJson(path.join(rootDir, 'node_modules', extIdent));
|
|
36
|
+
|
|
37
|
+
peers = { ...pkg.peerDependencies };
|
|
38
|
+
meta = { ...pkg.peerDependenciesMeta };
|
|
39
|
+
} catch {
|
|
40
|
+
peers = {};
|
|
41
|
+
meta = {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const extension of getMatchingPackageExtensions(
|
|
45
|
+
extIdent,
|
|
46
|
+
packageExtensions,
|
|
47
|
+
)) {
|
|
48
|
+
peers = { ...peers, ...extension.peerDependencies };
|
|
49
|
+
meta = { ...meta, ...extension.peerDependenciesMeta };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const required = new Map(
|
|
53
|
+
Object.entries(peers).filter(([ident]) => !meta[ident]?.optional),
|
|
54
|
+
);
|
|
55
|
+
const value = required.size > 0 ? required : null;
|
|
56
|
+
|
|
57
|
+
externalPeersCache.set(cacheKey, value);
|
|
58
|
+
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function exportsDeclareTypes(value: unknown): boolean {
|
|
63
|
+
if (!value || typeof value !== 'object') return false;
|
|
64
|
+
if (Array.isArray(value)) return value.some(exportsDeclareTypes);
|
|
65
|
+
|
|
66
|
+
for (const [key, nested] of Object.entries(
|
|
67
|
+
value as Record<string, unknown>,
|
|
68
|
+
)) {
|
|
69
|
+
if ((key === 'types' || key === 'typings') && typeof nested === 'string') {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (exportsDeclareTypes(nested)) return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getExternalTypeMetadata(
|
|
79
|
+
rootDir: string,
|
|
80
|
+
extIdent: string,
|
|
81
|
+
): { hasBundledTypes: boolean } {
|
|
82
|
+
try {
|
|
83
|
+
const pkg = readPackageJson(path.join(rootDir, 'node_modules', extIdent));
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
hasBundledTypes:
|
|
87
|
+
typeof pkg.types === 'string' ||
|
|
88
|
+
typeof pkg.typings === 'string' ||
|
|
89
|
+
exportsDeclareTypes(pkg.exports),
|
|
90
|
+
};
|
|
91
|
+
} catch {
|
|
92
|
+
return { hasBundledTypes: false };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function collectExternalProviderIdents(
|
|
97
|
+
contexts: WorkspaceContext[],
|
|
98
|
+
workspaceNames: Set<string>,
|
|
99
|
+
expectedByLocation?: Map<string, ExpectedWorkspace>,
|
|
100
|
+
): Set<string> {
|
|
101
|
+
const result = new Set<string>();
|
|
102
|
+
const add = (depIdent: string, range?: string): void => {
|
|
103
|
+
if (workspaceNames.has(depIdent) || range?.startsWith('workspace:')) return;
|
|
104
|
+
result.add(depIdent);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
for (const ctx of contexts) {
|
|
108
|
+
for (const section of SECTIONS) {
|
|
109
|
+
for (const [depIdent, range] of Object.entries(ctx.pkg[section] ?? {})) {
|
|
110
|
+
add(depIdent, range);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const depIdent of ctx.sourceUsage.keys()) add(depIdent);
|
|
114
|
+
for (const depIdent of ctx.dtsImports) add(depIdent);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (expectedByLocation) {
|
|
118
|
+
for (const expected of expectedByLocation.values()) {
|
|
119
|
+
for (const section of SECTIONS) {
|
|
120
|
+
for (const [depIdent, range] of expected.sections[section]) {
|
|
121
|
+
add(depIdent, range);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function collectExternalMetadata(
|
|
131
|
+
rootDir: string,
|
|
132
|
+
packageExtensions: Map<string, PackageExtension>,
|
|
133
|
+
idents: Set<string>,
|
|
134
|
+
externalPeerMetadata: Map<string, Map<string, string> | null>,
|
|
135
|
+
externalTypeMetadata: Map<string, { hasBundledTypes: boolean }>,
|
|
136
|
+
): void {
|
|
137
|
+
for (const ident of idents) {
|
|
138
|
+
if (!externalPeerMetadata.has(ident)) {
|
|
139
|
+
externalPeerMetadata.set(
|
|
140
|
+
ident,
|
|
141
|
+
getExternalRequiredPeers(rootDir, packageExtensions, ident),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (!externalTypeMetadata.has(ident)) {
|
|
145
|
+
externalTypeMetadata.set(ident, getExternalTypeMetadata(rootDir, ident));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yarn packageExtensions collector.
|
|
3
|
+
*
|
|
4
|
+
* Third-party peer metadata corrections are read from `.yarnrc.yml` here. The
|
|
5
|
+
* model receives these corrections as facts and does not parse Yarn config
|
|
6
|
+
* directly.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import yaml from 'yaml';
|
|
11
|
+
|
|
12
|
+
import type { PackageExtension } from '../model/types';
|
|
13
|
+
|
|
14
|
+
export function loadPackageExtensions(
|
|
15
|
+
rootDir: string,
|
|
16
|
+
): Map<string, PackageExtension> {
|
|
17
|
+
const filepath = path.join(rootDir, '.yarnrc.yml');
|
|
18
|
+
if (!existsSync(filepath)) return new Map();
|
|
19
|
+
|
|
20
|
+
const parsed = yaml.parse(readFileSync(filepath, 'utf8')) as {
|
|
21
|
+
packageExtensions?: Record<string, PackageExtension>;
|
|
22
|
+
};
|
|
23
|
+
const result = new Map<string, PackageExtension>();
|
|
24
|
+
|
|
25
|
+
for (const [selector, extension] of Object.entries(
|
|
26
|
+
parsed.packageExtensions ?? {},
|
|
27
|
+
)) {
|
|
28
|
+
result.set(selector, extension);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function extensionSelectorIdent(selector: string): string {
|
|
35
|
+
const atIndex = selector.lastIndexOf('@');
|
|
36
|
+
if (atIndex <= 0) return selector;
|
|
37
|
+
|
|
38
|
+
return selector.slice(0, atIndex);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getMatchingPackageExtensions(
|
|
42
|
+
depIdent: string,
|
|
43
|
+
packageExtensions: Map<string, PackageExtension>,
|
|
44
|
+
): PackageExtension[] {
|
|
45
|
+
const result: PackageExtension[] = [];
|
|
46
|
+
|
|
47
|
+
for (const [selector, extension] of packageExtensions) {
|
|
48
|
+
if (extensionSelectorIdent(selector) === depIdent) result.push(extension);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
}
|