@uxf/scripts 11.109.0 → 11.109.1
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/README.md +4 -6
- package/bin/uxf-dependencies-check.d.ts +2 -0
- package/bin/uxf-dependencies-check.js +15 -0
- package/package.json +3 -3
- package/src/uxf-dependencies-check/cli.d.ts +2 -0
- package/src/uxf-dependencies-check/cli.js +78 -0
- package/src/uxf-dependencies-check/conflict-check.d.ts +7 -0
- package/src/uxf-dependencies-check/conflict-check.js +47 -0
- package/src/uxf-dependencies-check/detect-package-manager.d.ts +6 -0
- package/src/uxf-dependencies-check/detect-package-manager.js +37 -0
- package/src/uxf-dependencies-check/index.d.ts +7 -0
- package/src/uxf-dependencies-check/index.js +79 -0
- package/src/uxf-dependencies-check/lock-parsers.d.ts +2 -0
- package/src/uxf-dependencies-check/lock-parsers.js +204 -0
- package/src/uxf-dependencies-check/peer-deps.d.ts +10 -0
- package/src/uxf-dependencies-check/peer-deps.js +168 -0
- package/src/uxf-dependencies-check/types.d.ts +27 -0
- package/src/uxf-dependencies-check/types.js +15 -0
- package/bin/uxf-dependencies-check.ts +0 -11
package/README.md
CHANGED
|
@@ -72,8 +72,6 @@ All-in-one dependency health check for projects using `@uxf/*` packages. Runs tw
|
|
|
72
72
|
|
|
73
73
|
Supports **yarn**, **npm**, **pnpm**, and **bun**. The package manager is auto-detected from the lock file.
|
|
74
74
|
|
|
75
|
-
Requires `tsx` or another tool to execute typescript (typically already available in your project's `devDependencies`).
|
|
76
|
-
|
|
77
75
|
```bash
|
|
78
76
|
Usage:
|
|
79
77
|
uxf-dependencies-check [options]
|
|
@@ -91,16 +89,16 @@ Build-time dependencies (`yargs`, `fast-glob`) are automatically ignored in the
|
|
|
91
89
|
|
|
92
90
|
```bash
|
|
93
91
|
# Run both checks (exits with code 1 if issues found)
|
|
94
|
-
npx
|
|
92
|
+
npx uxf-dependencies-check
|
|
95
93
|
|
|
96
94
|
# Auto-install missing peer deps
|
|
97
|
-
npx
|
|
95
|
+
npx uxf-dependencies-check --fix
|
|
98
96
|
|
|
99
97
|
# Preview what would be installed
|
|
100
|
-
npx
|
|
98
|
+
npx uxf-dependencies-check --dry-run
|
|
101
99
|
|
|
102
100
|
# Ignore specific packages in conflict check
|
|
103
|
-
npx
|
|
101
|
+
npx uxf-dependencies-check --exclude lodash dayjs
|
|
104
102
|
```
|
|
105
103
|
|
|
106
104
|
### What it checks
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const cli_1 = __importDefault(require("../src/uxf-dependencies-check/cli"));
|
|
8
|
+
(0, cli_1.default)()
|
|
9
|
+
.then((exitCode) => {
|
|
10
|
+
process.exitCode = exitCode;
|
|
11
|
+
})
|
|
12
|
+
.catch((e) => {
|
|
13
|
+
console.error(e); // eslint-disable-line no-console
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uxf/scripts",
|
|
3
|
-
"version": "11.109.
|
|
3
|
+
"version": "11.109.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"uxf-audit": "bin/uxf-audit.js",
|
|
8
8
|
"uxf-claude-sync": "bin/uxf-claude-sync.js",
|
|
9
|
-
"uxf-dependencies-check": "bin/uxf-dependencies-check.
|
|
9
|
+
"uxf-dependencies-check": "bin/uxf-dependencies-check.js",
|
|
10
10
|
"uxf-i18n-namespaces-gen": "bin/uxf-i18n-namespaces-gen.js",
|
|
11
11
|
"uxf-lunch": "bin/uxf-lunch.js",
|
|
12
12
|
"uxf-push-notifier": "bin/uxf-push-notifier.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"uxf-unused": "bin/uxf-unused.js"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "",
|
|
19
|
+
"build": "tsc -P tsconfig.build.json && chmod +x ./bin/*",
|
|
20
20
|
"run:mr-notifier": "GOOGLE_WEBHOOK_URL=<<TODO>> GITLAB_TOKEN=<<TODO>> CI_SERVER_URL=https://gitlab.uxf.cz node ./bin/uxf-merge-requests-notifier.js",
|
|
21
21
|
"test": "./node_modules/.bin/eslint ./bin",
|
|
22
22
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const index_1 = require("./index");
|
|
37
|
+
exports.default = async () => {
|
|
38
|
+
const yargs = (await Promise.resolve().then(() => __importStar(require("yargs")))).default;
|
|
39
|
+
const { hideBin } = await Promise.resolve().then(() => __importStar(require("yargs/helpers")));
|
|
40
|
+
const args = hideBin(process.argv);
|
|
41
|
+
const isHelp = args.includes("--help") || args.includes("-h");
|
|
42
|
+
const cli = yargs(args)
|
|
43
|
+
.usage("Usage: uxf-dependencies-check [options]")
|
|
44
|
+
.option("fix", {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
default: false,
|
|
47
|
+
describe: "Auto-install missing/mismatched peer dependencies",
|
|
48
|
+
})
|
|
49
|
+
.option("dry-run", {
|
|
50
|
+
type: "boolean",
|
|
51
|
+
default: false,
|
|
52
|
+
describe: "Show what would be installed without changes",
|
|
53
|
+
})
|
|
54
|
+
.option("exclude", {
|
|
55
|
+
alias: "e",
|
|
56
|
+
type: "array",
|
|
57
|
+
string: true,
|
|
58
|
+
default: [],
|
|
59
|
+
describe: "Packages to ignore in conflict check",
|
|
60
|
+
})
|
|
61
|
+
.strict()
|
|
62
|
+
.exitProcess(false);
|
|
63
|
+
try {
|
|
64
|
+
const argv = await cli.parse();
|
|
65
|
+
if (isHelp) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
return await (0, index_1.checkUxfDependencies)({
|
|
69
|
+
isFix: Boolean(argv.fix),
|
|
70
|
+
isDryRun: Boolean(argv.dryRun),
|
|
71
|
+
exclude: argv.exclude,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// yargs prints its own error messages (e.g. unknown options with --strict)
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Conflict, LockData } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Collect the set of package names that are direct dependencies of any installed @uxf/* package.
|
|
4
|
+
* Only these are relevant for version conflict detection.
|
|
5
|
+
*/
|
|
6
|
+
export declare function collectUxfDependencyNames(uxfPackages: string[], nodeModulesPath: string): Set<string>;
|
|
7
|
+
export declare function checkMultipleVersions(lockData: LockData, relevantPackages: Set<string>, excludedPackages?: Set<string>): Conflict[];
|
|
@@ -0,0 +1,47 @@
|
|
|
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.collectUxfDependencyNames = collectUxfDependencyNames;
|
|
7
|
+
exports.checkMultipleVersions = checkMultipleVersions;
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
/** Build-time / CLI-only dependencies that cannot cause runtime conflicts */
|
|
11
|
+
const IGNORED_PACKAGES = new Set(["yargs", "fast-glob"]);
|
|
12
|
+
/**
|
|
13
|
+
* Collect the set of package names that are direct dependencies of any installed @uxf/* package.
|
|
14
|
+
* Only these are relevant for version conflict detection.
|
|
15
|
+
*/
|
|
16
|
+
function collectUxfDependencyNames(uxfPackages, nodeModulesPath) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
const depNames = new Set();
|
|
19
|
+
for (const uxfPkg of uxfPackages) {
|
|
20
|
+
const pkgJsonPath = path_1.default.join(nodeModulesPath, uxfPkg, "package.json");
|
|
21
|
+
const pkgJson = (0, types_1.readPackageJson)(pkgJsonPath);
|
|
22
|
+
if (!pkgJson) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
for (const name of Object.keys((_a = pkgJson.dependencies) !== null && _a !== void 0 ? _a : {})) {
|
|
26
|
+
depNames.add(name);
|
|
27
|
+
}
|
|
28
|
+
for (const name of Object.keys((_b = pkgJson.peerDependencies) !== null && _b !== void 0 ? _b : {})) {
|
|
29
|
+
depNames.add(name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return depNames;
|
|
33
|
+
}
|
|
34
|
+
function checkMultipleVersions(lockData, relevantPackages, excludedPackages = new Set()) {
|
|
35
|
+
const allExcluded = new Set([...IGNORED_PACKAGES, ...excludedPackages]);
|
|
36
|
+
const conflicts = [];
|
|
37
|
+
for (const name of relevantPackages) {
|
|
38
|
+
if (allExcluded.has(name)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const versions = lockData.get(name);
|
|
42
|
+
if (versions && versions.size > 1) {
|
|
43
|
+
conflicts.push({ name, versions: Array.from(versions) });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return conflicts;
|
|
47
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.detectPackageManager = detectPackageManager;
|
|
7
|
+
exports.getInstallCommand = getInstallCommand;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const LOCK_FILES = [
|
|
11
|
+
{ file: "bun.lock", pm: "bun" },
|
|
12
|
+
{ file: "pnpm-lock.yaml", pm: "pnpm" },
|
|
13
|
+
{ file: "yarn.lock", pm: "yarn" },
|
|
14
|
+
{ file: "package-lock.json", pm: "npm" },
|
|
15
|
+
];
|
|
16
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
17
|
+
for (const { file, pm } of LOCK_FILES) {
|
|
18
|
+
if (fs_1.default.existsSync(path_1.default.resolve(cwd, file))) {
|
|
19
|
+
return pm;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error("Could not detect package manager. Please run this command from a project root.");
|
|
23
|
+
}
|
|
24
|
+
function getInstallCommand(pm) {
|
|
25
|
+
switch (pm) {
|
|
26
|
+
case "yarn":
|
|
27
|
+
return { cmd: "yarn", addArg: "add" };
|
|
28
|
+
case "pnpm":
|
|
29
|
+
return { cmd: "pnpm", addArg: "add" };
|
|
30
|
+
case "bun":
|
|
31
|
+
return { cmd: "bun", addArg: "add" };
|
|
32
|
+
case "npm":
|
|
33
|
+
return { cmd: "npm", addArg: "install" };
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Unsupported package manager: ${pm}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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.checkUxfDependencies = checkUxfDependencies;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const conflict_check_1 = require("./conflict-check");
|
|
10
|
+
const detect_package_manager_1 = require("./detect-package-manager");
|
|
11
|
+
const lock_parsers_1 = require("./lock-parsers");
|
|
12
|
+
const peer_deps_1 = require("./peer-deps");
|
|
13
|
+
function readProjectPackageJson(cwd) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(fs_1.default.readFileSync(path_1.default.resolve(cwd, "package.json"), "utf8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function checkUxfDependencies(options) {
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const pm = (0, detect_package_manager_1.detectPackageManager)(cwd);
|
|
24
|
+
const projectPkg = readProjectPackageJson(cwd);
|
|
25
|
+
if (!projectPkg) {
|
|
26
|
+
console.error("Could not read package.json in current directory."); // eslint-disable-line no-console
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
const nodeModulesPath = path_1.default.resolve(cwd, "node_modules");
|
|
30
|
+
if (!fs_1.default.existsSync(nodeModulesPath)) {
|
|
31
|
+
console.error("node_modules not found. Run install first."); // eslint-disable-line no-console
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
const uxfPackages = (0, peer_deps_1.findInstalledUxfPackages)(projectPkg);
|
|
35
|
+
if (uxfPackages.length === 0) {
|
|
36
|
+
console.log("No @uxf/* packages found in dependencies."); // eslint-disable-line no-console
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
console.log(`Detected package manager: ${pm}`); // eslint-disable-line no-console
|
|
40
|
+
console.log(`Found ${uxfPackages.length} @uxf/* packages: ${uxfPackages.join(", ")}\n`); // eslint-disable-line no-console
|
|
41
|
+
let hasIssues = false;
|
|
42
|
+
// --- Conflict check (lock file) ---
|
|
43
|
+
// Only check dependencies of @uxf/* packages, not the entire lock file
|
|
44
|
+
try {
|
|
45
|
+
const lockData = await (0, lock_parsers_1.parseLockFile)(pm, cwd);
|
|
46
|
+
const relevantDeps = (0, conflict_check_1.collectUxfDependencyNames)(uxfPackages, nodeModulesPath);
|
|
47
|
+
const conflicts = (0, conflict_check_1.checkMultipleVersions)(lockData, relevantDeps, new Set(options.exclude));
|
|
48
|
+
if (conflicts.length > 0) {
|
|
49
|
+
console.log("Dependency version conflicts in @uxf/* packages:"); // eslint-disable-line no-console
|
|
50
|
+
for (const { name, versions } of conflicts) {
|
|
51
|
+
console.log(` ${name}: ${versions.join(", ")}`); // eslint-disable-line no-console
|
|
52
|
+
}
|
|
53
|
+
console.log(); // eslint-disable-line no-console
|
|
54
|
+
hasIssues = true;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log("No dependency version conflicts found.\n"); // eslint-disable-line no-console
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
console.log("Could not parse lock file — skipping conflict check.\n"); // eslint-disable-line no-console
|
|
62
|
+
}
|
|
63
|
+
// --- Peer dependency check ---
|
|
64
|
+
const requiredPeerDeps = (0, peer_deps_1.collectRequiredPeerDeps)(uxfPackages, nodeModulesPath, projectPkg);
|
|
65
|
+
const { missing, mismatched } = (0, peer_deps_1.findMissingOrMismatchedDeps)(requiredPeerDeps, projectPkg);
|
|
66
|
+
if (missing.size === 0 && mismatched.size === 0) {
|
|
67
|
+
console.log("All peer dependencies are correctly installed."); // eslint-disable-line no-console
|
|
68
|
+
return hasIssues ? 1 : 0;
|
|
69
|
+
}
|
|
70
|
+
(0, peer_deps_1.printPeerDepsReport)(missing, mismatched);
|
|
71
|
+
hasIssues = true;
|
|
72
|
+
if (options.isDryRun || !options.isFix) {
|
|
73
|
+
(0, peer_deps_1.printSuggestedCommands)(missing, mismatched, pm);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
(0, peer_deps_1.installDeps)(missing, mismatched, pm);
|
|
77
|
+
console.log("\nAll peer dependencies are now installed."); // eslint-disable-line no-console
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.parseLockFile = parseLockFile;
|
|
40
|
+
const fs_1 = __importDefault(require("fs"));
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
/**
|
|
43
|
+
* Extract package name from a yarn.lock key like "@uxf/button@^1.0.0"
|
|
44
|
+
* Works for both Classic (v1) and Berry (v2+) key formats.
|
|
45
|
+
*/
|
|
46
|
+
function extractPackageNameFromYarnKey(key) {
|
|
47
|
+
var _a;
|
|
48
|
+
const match = key.match(/^(@[^/]+\/[^@]+|[^@]+)@/);
|
|
49
|
+
return match ? ((_a = match.at(1)) !== null && _a !== void 0 ? _a : null) : null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extract package name from a node_modules path like "node_modules/@uxf/button"
|
|
53
|
+
*/
|
|
54
|
+
function extractPackageNameFromPath(modulePath) {
|
|
55
|
+
var _a;
|
|
56
|
+
const match = modulePath.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)$/);
|
|
57
|
+
return match ? ((_a = match.at(1)) !== null && _a !== void 0 ? _a : null) : null;
|
|
58
|
+
}
|
|
59
|
+
function addToResult(result, name, version) {
|
|
60
|
+
const existing = result.get(name);
|
|
61
|
+
if (existing) {
|
|
62
|
+
existing.add(version);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
result.set(name, new Set([version]));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function parseYarnLock(cwd) {
|
|
69
|
+
const YAML = await Promise.resolve().then(() => __importStar(require("yaml")));
|
|
70
|
+
const content = fs_1.default.readFileSync(path_1.default.resolve(cwd, "yarn.lock"), "utf8");
|
|
71
|
+
const parsed = YAML.parse(content);
|
|
72
|
+
const result = new Map();
|
|
73
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
74
|
+
if (key === "__metadata") {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const entryObj = entry;
|
|
78
|
+
const name = extractPackageNameFromYarnKey(key);
|
|
79
|
+
const version = entryObj.version;
|
|
80
|
+
// Berry also has "resolution" field like "@uxf/core@npm:1.0.0" — extract version from it as fallback
|
|
81
|
+
const resolution = typeof entryObj.resolution === "string" ? entryObj.resolution : null;
|
|
82
|
+
if (!name) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (typeof version === "string") {
|
|
86
|
+
addToResult(result, name, version);
|
|
87
|
+
}
|
|
88
|
+
else if (resolution) {
|
|
89
|
+
// Berry resolution format: "package@npm:version" or "package@version"
|
|
90
|
+
const atIdx = resolution.lastIndexOf("@");
|
|
91
|
+
if (atIdx > 0) {
|
|
92
|
+
const resVersion = resolution.slice(atIdx + 1).replace(/^npm:/, "");
|
|
93
|
+
if (resVersion) {
|
|
94
|
+
addToResult(result, name, resVersion);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
async function parsePackageLockJson(cwd) {
|
|
102
|
+
var _a;
|
|
103
|
+
const content = fs_1.default.readFileSync(path_1.default.resolve(cwd, "package-lock.json"), "utf8");
|
|
104
|
+
const parsed = JSON.parse(content);
|
|
105
|
+
const result = new Map();
|
|
106
|
+
// package-lock.json v3 uses "packages" with node_modules paths as keys
|
|
107
|
+
const packages = (_a = parsed.packages) !== null && _a !== void 0 ? _a : {};
|
|
108
|
+
for (const [pkgPath, entry] of Object.entries(packages)) {
|
|
109
|
+
if (!pkgPath || pkgPath === "") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const name = extractPackageNameFromPath(pkgPath);
|
|
113
|
+
const version = entry.version;
|
|
114
|
+
if (!name || typeof version !== "string") {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
addToResult(result, name, version);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
async function parsePnpmLock(cwd) {
|
|
122
|
+
var _a, _b, _c;
|
|
123
|
+
const YAML = await Promise.resolve().then(() => __importStar(require("yaml")));
|
|
124
|
+
const content = fs_1.default.readFileSync(path_1.default.resolve(cwd, "pnpm-lock.yaml"), "utf8");
|
|
125
|
+
const parsed = YAML.parse(content);
|
|
126
|
+
const result = new Map();
|
|
127
|
+
// pnpm v9+ uses "snapshots" for resolved versions, older versions use "packages"
|
|
128
|
+
const lockfileVersion = String((_a = parsed.lockfileVersion) !== null && _a !== void 0 ? _a : "");
|
|
129
|
+
const isV9Plus = parseFloat(lockfileVersion) >= 9;
|
|
130
|
+
const packages = isV9Plus ? ((_b = parsed.snapshots) !== null && _b !== void 0 ? _b : {}) : ((_c = parsed.packages) !== null && _c !== void 0 ? _c : {});
|
|
131
|
+
for (const key of Object.keys(packages)) {
|
|
132
|
+
// Handle formats: "/@scope/name@version", "/name@version", "@scope/name@version", "name@version"
|
|
133
|
+
const cleaned = key.startsWith("/") ? key.slice(1) : key;
|
|
134
|
+
const atIdx = cleaned.startsWith("@") ? cleaned.indexOf("@", 1) : cleaned.indexOf("@");
|
|
135
|
+
if (atIdx === -1) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const name = cleaned.slice(0, atIdx);
|
|
139
|
+
const version = cleaned
|
|
140
|
+
.slice(atIdx + 1)
|
|
141
|
+
.split("(")
|
|
142
|
+
.at(0); // strip peerDep suffixes like "(react@18.0.0)"
|
|
143
|
+
if (!name || !version) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
addToResult(result, name, version);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Strip JSONC comments (// line comments) from bun.lock content.
|
|
152
|
+
* bun.lock is a JSONC-like text format, not valid JSON.
|
|
153
|
+
*/
|
|
154
|
+
function stripJsoncComments(content) {
|
|
155
|
+
return content.replace(/^\s*\/\/.*$/gm, "");
|
|
156
|
+
}
|
|
157
|
+
async function parseBunLock(cwd) {
|
|
158
|
+
var _a;
|
|
159
|
+
const content = fs_1.default.readFileSync(path_1.default.resolve(cwd, "bun.lock"), "utf8");
|
|
160
|
+
const parsed = JSON.parse(stripJsoncComments(content));
|
|
161
|
+
const result = new Map();
|
|
162
|
+
// bun.lock "packages" is a flat object: key is an ident like "@uxf/button",
|
|
163
|
+
// value is a tuple where the first element is "name@version" or just "version"
|
|
164
|
+
const packages = (_a = parsed.packages) !== null && _a !== void 0 ? _a : {};
|
|
165
|
+
for (const [key, value] of Object.entries(packages)) {
|
|
166
|
+
if (!key || key === "") {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const tuple = value;
|
|
170
|
+
if (!Array.isArray(tuple) || tuple.length === 0) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const raw = String(tuple.at(0));
|
|
174
|
+
// The first element is typically "name@version" or just "version"
|
|
175
|
+
// The key itself is the package identifier
|
|
176
|
+
let name;
|
|
177
|
+
let version;
|
|
178
|
+
// If the raw value contains @, extract version after last @
|
|
179
|
+
const lastAt = raw.lastIndexOf("@");
|
|
180
|
+
if (lastAt > 0) {
|
|
181
|
+
name = raw.slice(0, lastAt);
|
|
182
|
+
version = raw.slice(lastAt + 1);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// raw is just a version, key is the name
|
|
186
|
+
name = key;
|
|
187
|
+
version = raw;
|
|
188
|
+
}
|
|
189
|
+
if (!name || !version) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
addToResult(result, name, version);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
const PARSERS = {
|
|
197
|
+
yarn: parseYarnLock,
|
|
198
|
+
npm: parsePackageLockJson,
|
|
199
|
+
pnpm: parsePnpmLock,
|
|
200
|
+
bun: parseBunLock,
|
|
201
|
+
};
|
|
202
|
+
async function parseLockFile(pm, cwd = process.cwd()) {
|
|
203
|
+
return PARSERS[pm](cwd);
|
|
204
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DepInfo, MismatchInfo, PackageJson, PackageManager } from "./types";
|
|
2
|
+
export declare function findInstalledUxfPackages(projectPkg: PackageJson): string[];
|
|
3
|
+
export declare function collectRequiredPeerDeps(uxfPackages: string[], nodeModulesPath: string, projectPkg: PackageJson): Map<string, DepInfo>;
|
|
4
|
+
export declare function findMissingOrMismatchedDeps(requiredPeerDeps: Map<string, DepInfo>, projectPkg: PackageJson): {
|
|
5
|
+
missing: Map<string, DepInfo>;
|
|
6
|
+
mismatched: Map<string, MismatchInfo>;
|
|
7
|
+
};
|
|
8
|
+
export declare function printPeerDepsReport(missing: Map<string, DepInfo>, mismatched: Map<string, MismatchInfo>): void;
|
|
9
|
+
export declare function printSuggestedCommands(missing: Map<string, DepInfo>, mismatched: Map<string, MismatchInfo>, pm: PackageManager): void;
|
|
10
|
+
export declare function installDeps(missing: Map<string, DepInfo>, mismatched: Map<string, MismatchInfo>, pm: PackageManager): void;
|
|
@@ -0,0 +1,168 @@
|
|
|
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.findInstalledUxfPackages = findInstalledUxfPackages;
|
|
7
|
+
exports.collectRequiredPeerDeps = collectRequiredPeerDeps;
|
|
8
|
+
exports.findMissingOrMismatchedDeps = findMissingOrMismatchedDeps;
|
|
9
|
+
exports.printPeerDepsReport = printPeerDepsReport;
|
|
10
|
+
exports.printSuggestedCommands = printSuggestedCommands;
|
|
11
|
+
exports.installDeps = installDeps;
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const detect_package_manager_1 = require("./detect-package-manager");
|
|
15
|
+
const types_1 = require("./types");
|
|
16
|
+
/** Packages that consumers always install themselves — skip these in suggestions */
|
|
17
|
+
const CONSUMER_PROVIDED = new Set(["react", "react-dom", "next"]);
|
|
18
|
+
function findInstalledUxfPackages(projectPkg) {
|
|
19
|
+
const deps = { ...projectPkg.dependencies, ...projectPkg.devDependencies };
|
|
20
|
+
return Object.keys(deps).filter((name) => name.startsWith("@uxf/"));
|
|
21
|
+
}
|
|
22
|
+
function collectRequiredPeerDeps(uxfPackages, nodeModulesPath, projectPkg) {
|
|
23
|
+
var _a;
|
|
24
|
+
const prodDeps = new Set(Object.keys((_a = projectPkg.dependencies) !== null && _a !== void 0 ? _a : {}));
|
|
25
|
+
const peerDeps = new Map();
|
|
26
|
+
for (const uxfPkg of uxfPackages) {
|
|
27
|
+
const pkgJsonPath = path_1.default.join(nodeModulesPath, uxfPkg, "package.json");
|
|
28
|
+
const pkgJson = (0, types_1.readPackageJson)(pkgJsonPath);
|
|
29
|
+
if (!(pkgJson === null || pkgJson === void 0 ? void 0 : pkgJson.peerDependencies)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const isRequirerDev = !prodDeps.has(uxfPkg);
|
|
33
|
+
for (const [depName, depVersion] of Object.entries(pkgJson.peerDependencies)) {
|
|
34
|
+
if (!depVersion || depName.startsWith("@uxf/") || CONSUMER_PROVIDED.has(depName)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const existing = peerDeps.get(depName);
|
|
38
|
+
if (existing) {
|
|
39
|
+
existing.requiredBy.push(uxfPkg);
|
|
40
|
+
// If any requirer is a prod dependency, the peer dep should be prod too
|
|
41
|
+
if (!isRequirerDev) {
|
|
42
|
+
existing.isDev = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
peerDeps.set(depName, { version: depVersion, requiredBy: [uxfPkg], isDev: isRequirerDev });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return peerDeps;
|
|
51
|
+
}
|
|
52
|
+
function findMissingOrMismatchedDeps(requiredPeerDeps, projectPkg) {
|
|
53
|
+
const allProjectDeps = { ...projectPkg.dependencies, ...projectPkg.devDependencies };
|
|
54
|
+
const missing = new Map();
|
|
55
|
+
const mismatched = new Map();
|
|
56
|
+
for (const [depName, { version, requiredBy, isDev }] of requiredPeerDeps) {
|
|
57
|
+
const installedVersion = allProjectDeps[depName];
|
|
58
|
+
if (!installedVersion) {
|
|
59
|
+
missing.set(depName, { version, requiredBy, isDev });
|
|
60
|
+
}
|
|
61
|
+
else if (installedVersion !== version) {
|
|
62
|
+
mismatched.set(depName, { expected: version, actual: installedVersion, requiredBy, isDev });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { missing, mismatched };
|
|
66
|
+
}
|
|
67
|
+
function printPeerDepsReport(missing, mismatched) {
|
|
68
|
+
if (missing.size > 0) {
|
|
69
|
+
console.log("Missing peer dependencies:"); // eslint-disable-line no-console
|
|
70
|
+
for (const [name, { version, requiredBy, isDev }] of missing) {
|
|
71
|
+
const devTag = isDev ? " [dev]" : "";
|
|
72
|
+
console.log(` ${name}@${version}${devTag} (required by ${requiredBy.join(", ")})`); // eslint-disable-line no-console
|
|
73
|
+
}
|
|
74
|
+
console.log(); // eslint-disable-line no-console
|
|
75
|
+
}
|
|
76
|
+
if (mismatched.size > 0) {
|
|
77
|
+
console.log("Version mismatches:"); // eslint-disable-line no-console
|
|
78
|
+
for (const [name, { expected, actual, requiredBy, isDev }] of mismatched) {
|
|
79
|
+
const devTag = isDev ? " [dev]" : "";
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.log(` ${name}: installed ${actual}, expected ${expected}${devTag} (required by ${requiredBy.join(", ")})`);
|
|
82
|
+
}
|
|
83
|
+
console.log(); // eslint-disable-line no-console
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function formatInstallArgs(packages) {
|
|
87
|
+
return Array.from(packages.entries()).map(([name, version]) => `${name}@${version}`);
|
|
88
|
+
}
|
|
89
|
+
function splitDepsByDevFlag(deps) {
|
|
90
|
+
const prod = new Map();
|
|
91
|
+
const dev = new Map();
|
|
92
|
+
for (const [name, info] of deps) {
|
|
93
|
+
if (info.isDev) {
|
|
94
|
+
dev.set(name, info.version);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
prod.set(name, info.version);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { prod, dev };
|
|
101
|
+
}
|
|
102
|
+
function splitMismatchesByDevFlag(deps) {
|
|
103
|
+
const prod = new Map();
|
|
104
|
+
const dev = new Map();
|
|
105
|
+
for (const [name, info] of deps) {
|
|
106
|
+
if (info.isDev) {
|
|
107
|
+
dev.set(name, info.expected);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
prod.set(name, info.expected);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { prod, dev };
|
|
114
|
+
}
|
|
115
|
+
function printSuggestedCommands(missing, mismatched, pm) {
|
|
116
|
+
const { cmd, addArg } = (0, detect_package_manager_1.getInstallCommand)(pm);
|
|
117
|
+
if (missing.size > 0) {
|
|
118
|
+
const { prod, dev } = splitDepsByDevFlag(missing);
|
|
119
|
+
if (prod.size > 0) {
|
|
120
|
+
const pkgs = formatInstallArgs(prod).join(" ");
|
|
121
|
+
console.log(`To install missing dependencies, run:\n ${cmd} ${addArg} ${pkgs}\n`); // eslint-disable-line no-console
|
|
122
|
+
}
|
|
123
|
+
if (dev.size > 0) {
|
|
124
|
+
const pkgs = formatInstallArgs(dev).join(" ");
|
|
125
|
+
console.log(`To install missing dev dependencies, run:\n ${cmd} ${addArg} -D ${pkgs}\n`); // eslint-disable-line no-console
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (mismatched.size > 0) {
|
|
129
|
+
const { prod, dev } = splitMismatchesByDevFlag(mismatched);
|
|
130
|
+
if (prod.size > 0) {
|
|
131
|
+
const pkgs = formatInstallArgs(prod).join(" ");
|
|
132
|
+
console.log(`To fix version mismatches, run:\n ${cmd} ${addArg} ${pkgs}\n`); // eslint-disable-line no-console
|
|
133
|
+
}
|
|
134
|
+
if (dev.size > 0) {
|
|
135
|
+
const pkgs = formatInstallArgs(dev).join(" ");
|
|
136
|
+
console.log(`To fix dev version mismatches, run:\n ${cmd} ${addArg} -D ${pkgs}\n`); // eslint-disable-line no-console
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function installDeps(missing, mismatched, pm) {
|
|
141
|
+
const { cmd, addArg } = (0, detect_package_manager_1.getInstallCommand)(pm);
|
|
142
|
+
if (missing.size > 0) {
|
|
143
|
+
const { prod, dev } = splitDepsByDevFlag(missing);
|
|
144
|
+
if (prod.size > 0) {
|
|
145
|
+
const pkgs = formatInstallArgs(prod);
|
|
146
|
+
console.log("Installing missing dependencies..."); // eslint-disable-line no-console
|
|
147
|
+
(0, child_process_1.execFileSync)(cmd, [addArg, ...pkgs], { stdio: "inherit", cwd: process.cwd() });
|
|
148
|
+
}
|
|
149
|
+
if (dev.size > 0) {
|
|
150
|
+
const pkgs = formatInstallArgs(dev);
|
|
151
|
+
console.log("Installing missing dev dependencies..."); // eslint-disable-line no-console
|
|
152
|
+
(0, child_process_1.execFileSync)(cmd, [addArg, "-D", ...pkgs], { stdio: "inherit", cwd: process.cwd() });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (mismatched.size > 0) {
|
|
156
|
+
const { prod, dev } = splitMismatchesByDevFlag(mismatched);
|
|
157
|
+
if (prod.size > 0) {
|
|
158
|
+
const pkgs = formatInstallArgs(prod);
|
|
159
|
+
console.log("Fixing version mismatches..."); // eslint-disable-line no-console
|
|
160
|
+
(0, child_process_1.execFileSync)(cmd, [addArg, ...pkgs], { stdio: "inherit", cwd: process.cwd() });
|
|
161
|
+
}
|
|
162
|
+
if (dev.size > 0) {
|
|
163
|
+
const pkgs = formatInstallArgs(dev);
|
|
164
|
+
console.log("Fixing dev version mismatches..."); // eslint-disable-line no-console
|
|
165
|
+
(0, child_process_1.execFileSync)(cmd, [addArg, "-D", ...pkgs], { stdio: "inherit", cwd: process.cwd() });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type PackageManager = "yarn" | "npm" | "pnpm" | "bun";
|
|
2
|
+
export interface PackageJson {
|
|
3
|
+
dependencies?: Partial<Record<string, string>>;
|
|
4
|
+
devDependencies?: Partial<Record<string, string>>;
|
|
5
|
+
peerDependencies?: Partial<Record<string, string>>;
|
|
6
|
+
}
|
|
7
|
+
export interface DepInfo {
|
|
8
|
+
isDev: boolean;
|
|
9
|
+
requiredBy: string[];
|
|
10
|
+
version: string;
|
|
11
|
+
}
|
|
12
|
+
export interface MismatchInfo {
|
|
13
|
+
actual: string;
|
|
14
|
+
expected: string;
|
|
15
|
+
isDev: boolean;
|
|
16
|
+
requiredBy: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface Conflict {
|
|
19
|
+
name: string;
|
|
20
|
+
versions: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalized lock data: maps package name → set of resolved versions.
|
|
24
|
+
* Every lock file parser must return this shape.
|
|
25
|
+
*/
|
|
26
|
+
export type LockData = Map<string, Set<string>>;
|
|
27
|
+
export declare function readPackageJson(filePath: string): PackageJson | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
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.readPackageJson = readPackageJson;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
function readPackageJson(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, "utf8"));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|