@uxf/scripts 11.108.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 CHANGED
@@ -63,6 +63,54 @@ notify-push:
63
63
  - uxf-push-notifier --google-chat-webhook-url=$GOOGLE_CHAT_WEBHOOK
64
64
  ```
65
65
 
66
+ ## uxf-dependencies-check
67
+
68
+ All-in-one dependency health check for projects using `@uxf/*` packages. Runs two checks:
69
+
70
+ 1. **Version conflict detection** — parses the lock file and reports dependencies of `@uxf/*` packages that resolve to multiple versions
71
+ 2. **Peer dependency check** — verifies that all `peerDependencies` of installed `@uxf/*` packages are present and version-matched
72
+
73
+ Supports **yarn**, **npm**, **pnpm**, and **bun**. The package manager is auto-detected from the lock file.
74
+
75
+ ```bash
76
+ Usage:
77
+ uxf-dependencies-check [options]
78
+
79
+ Options:
80
+ --fix Auto-install missing/mismatched peer dependencies
81
+ --dry-run Show what would be installed without changes
82
+ -e, --exclude Packages to ignore in conflict check [array]
83
+ -h, --help Show help [boolean]
84
+ ```
85
+
86
+ Build-time dependencies (`yargs`, `fast-glob`) are automatically ignored in the conflict check.
87
+
88
+ ### Examples
89
+
90
+ ```bash
91
+ # Run both checks (exits with code 1 if issues found)
92
+ npx uxf-dependencies-check
93
+
94
+ # Auto-install missing peer deps
95
+ npx uxf-dependencies-check --fix
96
+
97
+ # Preview what would be installed
98
+ npx uxf-dependencies-check --dry-run
99
+
100
+ # Ignore specific packages in conflict check
101
+ npx uxf-dependencies-check --exclude lodash dayjs
102
+ ```
103
+
104
+ ### What it checks
105
+
106
+ - Parses the lock file (yarn.lock / package-lock.json / pnpm-lock.yaml / bun.lock) to detect version conflicts
107
+ - Scans all `@uxf/*` packages in your `dependencies` / `devDependencies`
108
+ - Reads each package's `peerDependencies` from `node_modules`
109
+ - Skips `@uxf/*`, `react`, `react-dom`, and `next` (you manage these yourself)
110
+ - Reports missing dependencies and version mismatches
111
+ - Distinguishes between prod and dev dependencies — peer deps required only by `devDependencies` are installed with `-D`
112
+ - With `--fix`, auto-installs missing peer deps using the detected package manager
113
+
66
114
  ## uxf-lunch
67
115
 
68
116
  ```bash
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -1,8 +1,15 @@
1
1
  #!/usr/bin/env node
2
- require("../src/uxf-dependencies-check/cli")()
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)()
3
9
  .then((exitCode) => {
4
- process.exitCode = exitCode;
5
- })
6
- .catch(() => {
7
- process.exitCode = 1;
8
- });
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,25 +1,25 @@
1
1
  {
2
2
  "name": "@uxf/scripts",
3
- "version": "11.108.0",
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-lunch": "bin/uxf-lunch.js",
10
9
  "uxf-dependencies-check": "bin/uxf-dependencies-check.js",
10
+ "uxf-i18n-namespaces-gen": "bin/uxf-i18n-namespaces-gen.js",
11
+ "uxf-lunch": "bin/uxf-lunch.js",
11
12
  "uxf-push-notifier": "bin/uxf-push-notifier.js",
12
13
  "uxf-release": "bin/uxf-release.js",
13
14
  "uxf-sitemap-check": "bin/uxf-sitemap-check.js",
14
15
  "uxf-sitemap-meta-export": "bin/uxf-sitemap-meta-export.js",
15
- "uxf-i18n-namespaces-gen": "bin/uxf-i18n-namespaces-gen.js",
16
16
  "uxf-unused": "bin/uxf-unused.js"
17
17
  },
18
18
  "scripts": {
19
- "build": "",
20
- "typecheck": "",
19
+ "build": "tsc -P tsconfig.build.json && chmod +x ./bin/*",
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
- "run:mr-notifier": "GOOGLE_WEBHOOK_URL=<<TODO>> GITLAB_TOKEN=<<TODO>> CI_SERVER_URL=https://gitlab.uxf.cz node ./bin/uxf-merge-requests-notifier.js"
22
+ "typecheck": "tsc --noEmit -p tsconfig.json"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<number>;
2
+ export default _default;
@@ -1,32 +1,78 @@
1
- const { checkUxfDependencies } = require("./index");
2
- const { argv } = require("process");
3
-
4
- module.exports = async () => {
5
- const cli = require("yargs")()
6
- .command("$0", "UXF Dependencies Check", (yargs) => {
7
- yargs.demandCommand(0, 0).usage(`
8
- Usage:
9
- uxf-dependencies-check [options]`);
10
- })
11
- .option("e", {
12
- alias: "exclude",
13
- array: true,
14
- default: [],
15
- describe: "Seznam balíčků, které budou ignorovány",
16
- })
17
- .strict(false)
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()
18
62
  .exitProcess(false);
19
-
20
63
  try {
21
- const { help, exclude } = cli.parse(argv.slice(2));
22
-
23
- if (Boolean(help)) {
64
+ const argv = await cli.parse();
65
+ if (isHelp) {
24
66
  return 0;
25
67
  }
26
-
27
- await checkUxfDependencies(new Set(exclude));
28
- } catch (e) {
29
- console.error(e);
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)
30
76
  return 1;
31
77
  }
32
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,6 @@
1
+ import type { PackageManager } from "./types";
2
+ export declare function detectPackageManager(cwd?: string): PackageManager;
3
+ export declare function getInstallCommand(pm: PackageManager): {
4
+ cmd: string;
5
+ addArg: string;
6
+ };
@@ -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,7 @@
1
+ interface CheckOptions {
2
+ isFix: boolean;
3
+ isDryRun: boolean;
4
+ exclude: string[];
5
+ }
6
+ export declare function checkUxfDependencies(options: CheckOptions): Promise<number>;
7
+ export {};
@@ -1,110 +1,79 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- const yarnLockPath = path.resolve(process.cwd(), "yarn.lock");
5
- const packageJsonPath = path.resolve(process.cwd(), "package.json");
6
-
7
- // Build-time / CLI-only dependencies that cannot cause runtime conflicts
8
- const IGNORED_PACKAGES = new Set(["yargs", "fast-glob"]);
9
-
10
- async function parseLockFile() {
11
- const YAML = await import("yaml");
12
-
13
- const content = fs.readFileSync(yarnLockPath, "utf8");
14
- return YAML.parse(content);
15
- }
16
-
17
- function parsePackageJson() {
18
- const content = fs.readFileSync(packageJsonPath, "utf8");
19
- return JSON.parse(content);
20
- }
21
-
22
- function extractPackageName(key) {
23
- const match = key.match(/^(@[^/]+\/[^@]+|[^@]+)@/);
24
- return match ? match[1] : null;
25
- }
26
-
27
- function findUxfPackagesInDependencies(lockData, packageJson) {
28
- const declaredDependencies = new Set(Object.keys(packageJson.dependencies || {}));
29
-
30
- const uxfPackages = [];
31
-
32
- for (const key of Object.keys(lockData)) {
33
- const name = extractPackageName(key);
34
- if (name?.startsWith("@uxf/") && declaredDependencies.has(name)) {
35
- uxfPackages.push(key);
36
- }
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;
37
19
  }
38
-
39
- return uxfPackages;
40
20
  }
41
-
42
- function collectDirectDependencies(packageKeys, lockData) {
43
- const found = {};
44
-
45
- for (const key of packageKeys) {
46
- const entry = lockData[key];
47
- if (!entry) continue;
48
-
49
- const dependencies = entry.dependencies || {};
50
-
51
- for (const depName in dependencies) {
52
- const matchingKeys = Object.keys(lockData).filter((lockKey) => extractPackageName(lockKey) === depName);
53
-
54
- for (const matchKey of matchingKeys) {
55
- const depEntry = lockData[matchKey];
56
- if (!depEntry || !depEntry.version) continue;
57
-
58
- if (!found[depName]) {
59
- found[depName] = new Set();
60
- }
61
-
62
- found[depName].add(depEntry.version);
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
63
52
  }
53
+ console.log(); // eslint-disable-line no-console
54
+ hasIssues = true;
64
55
  }
65
- }
66
-
67
- return found;
68
- }
69
-
70
- function checkMultipleVersions(dependencyMap, excludedPackages = new Set()) {
71
- const conflicts = [];
72
-
73
- for (const [name, versions] of Object.entries(dependencyMap)) {
74
- if (excludedPackages.has(name)) continue;
75
- if (versions.size > 1) {
76
- conflicts.push({ name, versions: Array.from(versions) });
56
+ else {
57
+ console.log("No dependency version conflicts found.\n"); // eslint-disable-line no-console
77
58
  }
78
59
  }
79
-
80
- return conflicts;
81
- }
82
-
83
- async function checkUxfDependencies(excludedPackages = new Set()) {
84
- const lockData = await parseLockFile();
85
- const packageJson = parsePackageJson();
86
-
87
- const uxfPackages = findUxfPackagesInDependencies(lockData, packageJson);
88
- const dependencies = collectDirectDependencies(uxfPackages, lockData);
89
-
90
- const allExcluded = new Set([...IGNORED_PACKAGES, ...excludedPackages]);
91
- const conflicts = checkMultipleVersions(dependencies, allExcluded);
92
-
93
- if (conflicts.length > 0) {
94
- console.log("Následující balíčky mají více verzí v závislostech UXF balíčků:");
95
- for (const { name, versions } of conflicts) {
96
- console.log(`- ${name}: ${versions.join(", ")}`);
97
- }
98
- process.exit(1);
99
- } else {
100
- console.log("Všechny závislosti UXF balíčků mají jednotnou verzi.");
101
- process.exit(0);
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;
102
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;
103
79
  }
104
-
105
- module.exports = {
106
- findUxfPackagesInDependencies,
107
- collectDirectDependencies,
108
- checkMultipleVersions,
109
- checkUxfDependencies,
110
- };
@@ -0,0 +1,2 @@
1
+ import type { LockData, PackageManager } from "./types";
2
+ export declare function parseLockFile(pm: PackageManager, cwd?: string): Promise<LockData>;
@@ -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
+ }
@@ -1,111 +0,0 @@
1
- const { findUxfPackagesInDependencies, collectDirectDependencies, checkMultipleVersions } = require("./index"); // Adjust the path if needed
2
-
3
- describe("findUxfPackagesInDependencies", () => {
4
- const lockData = {
5
- "@uxf/button@^1.0.0": {},
6
- "@uxf/form@^2.1.0": {},
7
- "react@^17.0.0": {},
8
- "lodash@^4.17.0": {},
9
- "classnames@^2.3.1": {},
10
- };
11
-
12
- const packageJson = {
13
- dependencies: {
14
- "@uxf/button": "^1.0.0",
15
- "@uxf/form": "^2.1.0",
16
- react: "^17.0.0",
17
- },
18
- };
19
-
20
- it("returns @uxf/* packages that are in package.json dependencies", () => {
21
- const result = findUxfPackagesInDependencies(lockData, packageJson);
22
- expect(result).toEqual(["@uxf/button@^1.0.0", "@uxf/form@^2.1.0"]);
23
- });
24
- });
25
-
26
- describe("collectDirectDependencies", () => {
27
- const lockData = {
28
- "@uxf/button@^1.0.0": {
29
- dependencies: {
30
- react: "^17.0.0",
31
- lodash: "^4.17.0",
32
- },
33
- },
34
- "@uxf/form@^2.1.0": {
35
- dependencies: {
36
- react: "^17.0.1", // different version range
37
- classnames: "^2.3.1",
38
- },
39
- },
40
- "react@^17.0.0": {
41
- version: "17.0.2",
42
- },
43
- "react@^17.0.1": {
44
- version: "17.0.1",
45
- },
46
- "lodash@^4.17.0": {
47
- version: "4.17.21",
48
- },
49
- "classnames@^2.3.1": {
50
- version: "2.3.1",
51
- },
52
- };
53
-
54
- const packageKeys = ["@uxf/button@^1.0.0", "@uxf/form@^2.1.0"];
55
-
56
- it("collects direct dependencies of multiple UXF packages with shared and unique deps", () => {
57
- const result = collectDirectDependencies(packageKeys, lockData);
58
-
59
- expect(result).toEqual({
60
- react: new Set(["17.0.2", "17.0.1"]),
61
- lodash: new Set(["4.17.21"]),
62
- classnames: new Set(["2.3.1"]),
63
- });
64
- });
65
-
66
- it("handles empty dependencies gracefully", () => {
67
- const lockData = {
68
- "@uxf/empty@1.0.0": {},
69
- };
70
-
71
- const result = collectDirectDependencies(["@uxf/empty@1.0.0"], lockData);
72
- expect(result).toEqual({});
73
- });
74
- });
75
-
76
- describe("checkMultipleVersions", () => {
77
- it("returns empty array when all packages have a single version", () => {
78
- const deps = {
79
- react: new Set(["17.0.2"]),
80
- lodash: new Set(["4.17.21"]),
81
- };
82
- expect(checkMultipleVersions(deps)).toEqual([]);
83
- });
84
-
85
- it("returns conflicts when a package has multiple versions", () => {
86
- const deps = {
87
- react: new Set(["17.0.2", "18.0.0"]),
88
- lodash: new Set(["4.17.21"]),
89
- };
90
- const conflicts = checkMultipleVersions(deps);
91
- expect(conflicts).toHaveLength(1);
92
- expect(conflicts[0].name).toBe("react");
93
- expect(conflicts[0].versions).toEqual(expect.arrayContaining(["17.0.2", "18.0.0"]));
94
- });
95
-
96
- it("excludes packages that are in the excludedPackages set", () => {
97
- const deps = {
98
- react: new Set(["17.0.2", "18.0.0"]),
99
- };
100
- const result = checkMultipleVersions(deps, new Set(["react"]));
101
- expect(result).toEqual([]);
102
- });
103
-
104
- it("returns multiple conflicts when several packages have version mismatches", () => {
105
- const deps = {
106
- react: new Set(["17.0.2", "18.0.0"]),
107
- lodash: new Set(["4.17.0", "4.17.21"]),
108
- };
109
- expect(checkMultipleVersions(deps)).toHaveLength(2);
110
- });
111
- });