@cellajs/cli 0.2.1 → 0.2.2
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 +12 -4
- package/dist/index.js +215 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -179,10 +179,18 @@ packageJsonSync: ['dependencies', 'devDependencies', 'scripts']
|
|
|
179
179
|
|
|
180
180
|
**Supported keys:** `dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies`, `scripts`, `engines`, `packageManager`, `overrides`, `exports`, `pnpm`
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
Each section merges three-way against the merge-base package.json (upstream as of your last
|
|
183
|
+
sync). An entry your fork never touched follows upstream: it is **added**, **updated** (a rewritten
|
|
184
|
+
script, a changed range) or **removed** when upstream dropped it. An entry your fork added or
|
|
185
|
+
changed stays as it is; only a strictly higher upstream version still bumps it, and never for
|
|
186
|
+
`scripts`. An entry your fork removed is not re-added. A dependency or script upstream dropped
|
|
187
|
+
stays while your own code still uses it: files that differ from upstream and scripts your fork
|
|
188
|
+
added or changed are searched for an import of the package, a run of its CLI, or a package manager
|
|
189
|
+
command that runs the script. The sync lists every change and warns about each entry it kept this
|
|
190
|
+
way, so you can remove it once your code no longer needs it. `exports` are add-only: an entry your fork
|
|
191
|
+
already defines is never rewritten, and `exports` only merges when both sides are subpath maps
|
|
192
|
+
(`{ ".": …, "./config": … }`). A workspace upstream added since your last sync arrives with its
|
|
193
|
+
package.json copied verbatim.
|
|
186
194
|
|
|
187
195
|
## Contributions (pull from forks)
|
|
188
196
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
|
|
3
3
|
// src/cella-cli.ts
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import { join as
|
|
4
|
+
import { existsSync as existsSync11, statSync as statSync3 } from "fs";
|
|
5
|
+
import { join as join18, resolve as resolve6 } from "path";
|
|
6
6
|
import process9 from "process";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
@@ -216,7 +216,7 @@ import process4 from "process";
|
|
|
216
216
|
// package.json
|
|
217
217
|
var package_default = {
|
|
218
218
|
name: "@cellajs/cli",
|
|
219
|
-
version: "0.2.
|
|
219
|
+
version: "0.2.2",
|
|
220
220
|
private: false,
|
|
221
221
|
license: "MIT",
|
|
222
222
|
description: "CLI for managing your monorepo and syncing with upstream Cella.",
|
|
@@ -707,7 +707,7 @@ function printProtectedConflicts(result) {
|
|
|
707
707
|
}
|
|
708
708
|
function findMaskingPins(files) {
|
|
709
709
|
return files.filter(
|
|
710
|
-
(file) => file.isPinned && file.status === "behind" && file.existsInFork && file.existsInUpstream
|
|
710
|
+
(file) => file.isPinned && !isManagedFile(file.path) && file.status === "behind" && file.existsInFork && file.existsInUpstream
|
|
711
711
|
);
|
|
712
712
|
}
|
|
713
713
|
function printMaskingPinWarning(files) {
|
|
@@ -2893,11 +2893,11 @@ function printOutdatedResults(enhancedPackages, dependentNameCache, forkPath) {
|
|
|
2893
2893
|
const sortedDependents = [...dependentMap.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
2894
2894
|
const maxDependentNameLen = Math.max(0, ...sortedDependents.map(([name]) => name.length));
|
|
2895
2895
|
for (const [dependentName, { location, count }] of sortedDependents) {
|
|
2896
|
-
const
|
|
2897
|
-
const relativePath = path2.relative(forkPath,
|
|
2896
|
+
const packageJsonPath2 = path2.join(location, "package.json");
|
|
2897
|
+
const relativePath = path2.relative(forkPath, packageJsonPath2);
|
|
2898
2898
|
const paddedName = dependentName.padEnd(maxDependentNameLen);
|
|
2899
2899
|
console.info(
|
|
2900
|
-
` ${colors_default.dim("\u2022")} ${paddedName} ${hyperlink(relativePath, `file://${
|
|
2900
|
+
` ${colors_default.dim("\u2022")} ${paddedName} ${hyperlink(relativePath, `file://${packageJsonPath2}`)} ${colors_default.dim(`${count}`)}`
|
|
2901
2901
|
);
|
|
2902
2902
|
}
|
|
2903
2903
|
}
|
|
@@ -3768,8 +3768,93 @@ function closePr(cwd, ref) {
|
|
|
3768
3768
|
}
|
|
3769
3769
|
|
|
3770
3770
|
// src/services/packages.ts
|
|
3771
|
-
import { existsSync as
|
|
3771
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
3772
|
+
import { join as join15 } from "path";
|
|
3773
|
+
|
|
3774
|
+
// src/services/package-usage.ts
|
|
3775
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, statSync } from "fs";
|
|
3772
3776
|
import { join as join14 } from "path";
|
|
3777
|
+
var MAX_FILE_BYTES = 1024 * 1024;
|
|
3778
|
+
function escapeRegExp(value) {
|
|
3779
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3780
|
+
}
|
|
3781
|
+
function readSearchable(filePath) {
|
|
3782
|
+
try {
|
|
3783
|
+
if (statSync(filePath).size > MAX_FILE_BYTES) return "";
|
|
3784
|
+
const text = readFileSync5(filePath, "utf-8");
|
|
3785
|
+
return text.includes("\0") ? "" : text;
|
|
3786
|
+
} catch {
|
|
3787
|
+
return "";
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
function fileSource(forkPath, path4) {
|
|
3791
|
+
let text;
|
|
3792
|
+
return { path: path4, label: path4, text: () => text ??= readSearchable(join14(forkPath, path4)) };
|
|
3793
|
+
}
|
|
3794
|
+
async function readScriptsAtRef(forkPath, ref, path4) {
|
|
3795
|
+
try {
|
|
3796
|
+
return JSON.parse(await git(["show", `${ref}:${path4}`], forkPath)).scripts ?? {};
|
|
3797
|
+
} catch {
|
|
3798
|
+
return {};
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
async function scriptSources(forkPath, upstreamRef, baseRef, path4) {
|
|
3802
|
+
let forkScripts;
|
|
3803
|
+
try {
|
|
3804
|
+
forkScripts = JSON.parse(readFileSync5(join14(forkPath, path4), "utf-8")).scripts ?? {};
|
|
3805
|
+
} catch {
|
|
3806
|
+
return [];
|
|
3807
|
+
}
|
|
3808
|
+
const baseScripts = await readScriptsAtRef(forkPath, baseRef, path4);
|
|
3809
|
+
const upstreamScripts = await readScriptsAtRef(forkPath, upstreamRef, path4);
|
|
3810
|
+
return Object.entries(forkScripts).filter(([name, value]) => value !== baseScripts[name] && value !== upstreamScripts[name]).map(([name, value]) => ({ path: path4, label: `${path4} (scripts.${name})`, text: () => value }));
|
|
3811
|
+
}
|
|
3812
|
+
function binNames(forkPath, location, name) {
|
|
3813
|
+
const bareName = name.split("/").pop();
|
|
3814
|
+
for (const dir of [join14(forkPath, location), forkPath]) {
|
|
3815
|
+
const manifest = join14(dir, "node_modules", name, "package.json");
|
|
3816
|
+
if (!existsSync8(manifest)) continue;
|
|
3817
|
+
try {
|
|
3818
|
+
const { bin } = JSON.parse(readFileSync5(manifest, "utf-8"));
|
|
3819
|
+
if (typeof bin === "string") return [bareName];
|
|
3820
|
+
return bin && typeof bin === "object" ? Object.keys(bin) : [];
|
|
3821
|
+
} catch {
|
|
3822
|
+
return [];
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
return [bareName];
|
|
3826
|
+
}
|
|
3827
|
+
function packagePatterns(forkPath, location, name) {
|
|
3828
|
+
const specifiers = [name];
|
|
3829
|
+
const typed = name.match(/^@types\/(.+)$/);
|
|
3830
|
+
if (typed) specifiers.push(typed[1].includes("__") ? `@${typed[1].replace("__", "/")}` : typed[1]);
|
|
3831
|
+
const patterns = specifiers.map((specifier) => new RegExp(`(['"\`])${escapeRegExp(specifier)}(?:/[^'"\`\\s]*)?\\1`));
|
|
3832
|
+
for (const bin of binNames(forkPath, location, name)) {
|
|
3833
|
+
patterns.push(new RegExp(`(?:^|[\\s'"\`(;&|=])${escapeRegExp(bin)}(?=$|[\\s'"\`);&|])`, "m"));
|
|
3834
|
+
}
|
|
3835
|
+
return patterns;
|
|
3836
|
+
}
|
|
3837
|
+
function scriptPattern(name) {
|
|
3838
|
+
return new RegExp(`\\b(?:pnpm|npm|yarn|bun)\\s+(?:[^\\n;&|]*?\\s)?${escapeRegExp(name)}(?=$|[\\s'"\`);&|])`, "m");
|
|
3839
|
+
}
|
|
3840
|
+
async function loadForkUsage(forkPath, upstreamRef, baseRef) {
|
|
3841
|
+
const diff = await git(["diff", "--name-only", "-z", "--diff-filter=d", upstreamRef], forkPath);
|
|
3842
|
+
const sources = [];
|
|
3843
|
+
for (const path4 of diff.split("\0").filter(Boolean)) {
|
|
3844
|
+
if (isPackageJson(path4)) sources.push(...await scriptSources(forkPath, upstreamRef, baseRef, path4));
|
|
3845
|
+
else if (!isManagedFile(path4)) sources.push(fileSource(forkPath, path4));
|
|
3846
|
+
}
|
|
3847
|
+
const findIn = (scoped, patterns) => scoped.find((source) => patterns.some((pattern) => pattern.test(source.text())))?.label;
|
|
3848
|
+
return {
|
|
3849
|
+
findPackage: (location, name) => findIn(
|
|
3850
|
+
location ? sources.filter((source) => isUnderAnyFolder(source.path, [location])) : sources,
|
|
3851
|
+
packagePatterns(forkPath, location, name)
|
|
3852
|
+
),
|
|
3853
|
+
findScript: (name) => findIn(sources, [scriptPattern(name)])
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
// src/services/packages.ts
|
|
3773
3858
|
function parseComparableVersion(version) {
|
|
3774
3859
|
const trimmed = version.trim();
|
|
3775
3860
|
if (trimmed === "" || trimmed === "*" || trimmed.includes("workspace:") || trimmed.includes("catalog:") || trimmed.includes("file:") || trimmed.includes("link:") || trimmed.includes("git+") || trimmed.includes("github:") || trimmed.includes("http://") || trimmed.includes("https://") || trimmed.includes("||")) {
|
|
@@ -3797,24 +3882,49 @@ function isHigherVersion(upstreamVersion, forkVersion) {
|
|
|
3797
3882
|
if (!upCoerced || !forkCoerced) return false;
|
|
3798
3883
|
return compareVersions(upCoerced, forkCoerced) > 0;
|
|
3799
3884
|
}
|
|
3800
|
-
|
|
3885
|
+
var DEPENDENCY_KEYS = [
|
|
3886
|
+
"dependencies",
|
|
3887
|
+
"devDependencies",
|
|
3888
|
+
"peerDependencies",
|
|
3889
|
+
"optionalDependencies"
|
|
3890
|
+
];
|
|
3891
|
+
function mergeRecord(forkRecord, upstreamRecord, baseRecord, { bumpHigher = true, findUse } = {}) {
|
|
3801
3892
|
if (!upstreamRecord) return void 0;
|
|
3802
3893
|
const merged = { ...forkRecord || {} };
|
|
3894
|
+
const base = baseRecord || {};
|
|
3803
3895
|
const added = [];
|
|
3804
|
-
|
|
3896
|
+
const updated = [];
|
|
3897
|
+
const removed = [];
|
|
3898
|
+
const kept = [];
|
|
3805
3899
|
for (const [name, upstreamValue] of Object.entries(upstreamRecord)) {
|
|
3806
3900
|
const forkValue = merged[name];
|
|
3807
3901
|
if (forkValue === void 0) {
|
|
3902
|
+
if (name in base) continue;
|
|
3808
3903
|
merged[name] = upstreamValue;
|
|
3809
3904
|
added.push(name);
|
|
3810
|
-
|
|
3811
|
-
} else if (
|
|
3905
|
+
} else if (forkValue === upstreamValue) {
|
|
3906
|
+
} else if (base[name] === forkValue) {
|
|
3812
3907
|
merged[name] = upstreamValue;
|
|
3813
|
-
|
|
3908
|
+
updated.push(name);
|
|
3909
|
+
} else if (bumpHigher && isHigherVersion(upstreamValue, forkValue)) {
|
|
3910
|
+
merged[name] = upstreamValue;
|
|
3911
|
+
updated.push(name);
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
for (const [name, forkValue] of Object.entries(merged)) {
|
|
3915
|
+
if (name in upstreamRecord) continue;
|
|
3916
|
+
if (base[name] === void 0 || base[name] !== forkValue) continue;
|
|
3917
|
+
const usedAt = findUse?.(name);
|
|
3918
|
+
if (usedAt) {
|
|
3919
|
+
kept.push({ name, usedAt });
|
|
3920
|
+
continue;
|
|
3814
3921
|
}
|
|
3922
|
+
delete merged[name];
|
|
3923
|
+
removed.push(name);
|
|
3815
3924
|
}
|
|
3816
3925
|
const sorted = Object.fromEntries(Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)));
|
|
3817
|
-
|
|
3926
|
+
const changed = added.length + updated.length + removed.length > 0;
|
|
3927
|
+
return { merged: sorted, changed, added, updated, removed, kept };
|
|
3818
3928
|
}
|
|
3819
3929
|
function isSubpathMap(value) {
|
|
3820
3930
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -3828,14 +3938,15 @@ function safeMergeExports(forkExports, upstreamExports) {
|
|
|
3828
3938
|
for (const name of added) merged[name] = upstreamExports[name];
|
|
3829
3939
|
return { merged, changed: added.length > 0, added };
|
|
3830
3940
|
}
|
|
3831
|
-
function safeMergePnpm(forkPnpm, upstreamPnpm) {
|
|
3941
|
+
function safeMergePnpm(forkPnpm, upstreamPnpm, basePnpm) {
|
|
3832
3942
|
if (!upstreamPnpm) return void 0;
|
|
3833
3943
|
const merged = { ...forkPnpm || {} };
|
|
3834
3944
|
let changed = false;
|
|
3835
3945
|
if (upstreamPnpm.overrides) {
|
|
3836
|
-
const result =
|
|
3946
|
+
const result = mergeRecord(
|
|
3837
3947
|
merged.overrides,
|
|
3838
|
-
upstreamPnpm.overrides
|
|
3948
|
+
upstreamPnpm.overrides,
|
|
3949
|
+
basePnpm?.overrides
|
|
3839
3950
|
);
|
|
3840
3951
|
if (result?.changed) {
|
|
3841
3952
|
merged.overrides = result.merged;
|
|
@@ -3843,18 +3954,15 @@ function safeMergePnpm(forkPnpm, upstreamPnpm) {
|
|
|
3843
3954
|
}
|
|
3844
3955
|
}
|
|
3845
3956
|
if (upstreamPnpm.patchedDependencies) {
|
|
3846
|
-
const
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
merged.patchedDependencies = Object.fromEntries(
|
|
3856
|
-
Object.entries(forkPatched).sort(([a], [b]) => a.localeCompare(b))
|
|
3857
|
-
);
|
|
3957
|
+
const result = mergeRecord(
|
|
3958
|
+
merged.patchedDependencies,
|
|
3959
|
+
upstreamPnpm.patchedDependencies,
|
|
3960
|
+
basePnpm?.patchedDependencies,
|
|
3961
|
+
{ bumpHigher: false }
|
|
3962
|
+
);
|
|
3963
|
+
if (result?.changed) {
|
|
3964
|
+
merged.patchedDependencies = result.merged;
|
|
3965
|
+
changed = true;
|
|
3858
3966
|
}
|
|
3859
3967
|
}
|
|
3860
3968
|
if (upstreamPnpm.packageExtensions) {
|
|
@@ -3885,9 +3993,9 @@ function safeMergePnpm(forkPnpm, upstreamPnpm) {
|
|
|
3885
3993
|
return { merged, changed };
|
|
3886
3994
|
}
|
|
3887
3995
|
function readPackageJson(filePath) {
|
|
3888
|
-
if (!
|
|
3996
|
+
if (!existsSync9(filePath)) return null;
|
|
3889
3997
|
try {
|
|
3890
|
-
return JSON.parse(
|
|
3998
|
+
return JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
3891
3999
|
} catch {
|
|
3892
4000
|
return null;
|
|
3893
4001
|
}
|
|
@@ -3896,31 +4004,50 @@ function writePackageJson(filePath, data) {
|
|
|
3896
4004
|
writeFileSync4(filePath, `${JSON.stringify(data, null, 2)}
|
|
3897
4005
|
`, "utf-8");
|
|
3898
4006
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
4007
|
+
function packageJsonPath(relativePath) {
|
|
4008
|
+
return relativePath ? `${relativePath}/package.json` : "package.json";
|
|
4009
|
+
}
|
|
4010
|
+
async function readPackageJsonAtRef(forkPath, ref, relativePath) {
|
|
3901
4011
|
try {
|
|
3902
|
-
|
|
4012
|
+
const raw = await git(["show", `${ref}:${packageJsonPath(relativePath)}`], forkPath);
|
|
4013
|
+
return { raw, data: JSON.parse(raw) };
|
|
3903
4014
|
} catch {
|
|
3904
4015
|
return null;
|
|
3905
4016
|
}
|
|
3906
4017
|
}
|
|
3907
4018
|
async function discoverPackageLocations(forkPath, upstreamRef) {
|
|
3908
4019
|
const stdout = await git(["ls-tree", "-r", "--name-only", upstreamRef], forkPath, { ignoreErrors: true });
|
|
3909
|
-
|
|
3910
|
-
|
|
4020
|
+
return stdout.split("\n").filter((p) => p.endsWith("/package.json") || p === "package.json").map((p) => p === "package.json" ? "" : p.replace("/package.json", ""));
|
|
4021
|
+
}
|
|
4022
|
+
async function resolveMergeBase(forkPath, upstreamRef) {
|
|
4023
|
+
try {
|
|
4024
|
+
return (await getEffectiveMergeBase(forkPath, "HEAD", upstreamRef)).base || null;
|
|
4025
|
+
} catch {
|
|
4026
|
+
return null;
|
|
4027
|
+
}
|
|
3911
4028
|
}
|
|
3912
|
-
async function syncPackageJson(
|
|
4029
|
+
async function syncPackageJson(config, baseRef, usage, relativePath, keysToSync) {
|
|
4030
|
+
const { forkPath, upstreamRef } = config;
|
|
3913
4031
|
const changes = [];
|
|
3914
|
-
const
|
|
4032
|
+
const kept = [];
|
|
4033
|
+
const pkgRelPath = packageJsonPath(relativePath);
|
|
4034
|
+
const pkgPath = join15(forkPath, pkgRelPath);
|
|
3915
4035
|
const forkPkg = readPackageJson(pkgPath);
|
|
3916
|
-
const
|
|
3917
|
-
|
|
3918
|
-
|
|
4036
|
+
const upstream = await readPackageJsonAtRef(forkPath, upstreamRef, relativePath);
|
|
4037
|
+
const basePkg = baseRef ? (await readPackageJsonAtRef(forkPath, baseRef, relativePath))?.data : void 0;
|
|
4038
|
+
if (!upstream) return { updated: false, changes, kept };
|
|
4039
|
+
if (!forkPkg) {
|
|
4040
|
+
const workspaceArrived = relativePath !== "" && existsSync9(join15(forkPath, relativePath));
|
|
4041
|
+
if (basePkg || !workspaceArrived || isIgnored(pkgRelPath, config)) return { updated: false, changes, kept };
|
|
4042
|
+
writeFileSync4(pkgPath, upstream.raw.endsWith("\n") ? upstream.raw : `${upstream.raw}
|
|
4043
|
+
`, "utf-8");
|
|
4044
|
+
return { updated: true, changes: ["copied from upstream (new workspace)"], kept };
|
|
3919
4045
|
}
|
|
4046
|
+
const upstreamPkg = upstream.data;
|
|
3920
4047
|
let updated = false;
|
|
3921
4048
|
for (const key of keysToSync) {
|
|
3922
4049
|
if (key === "pnpm") {
|
|
3923
|
-
const result2 = safeMergePnpm(forkPkg.pnpm, upstreamPkg.pnpm);
|
|
4050
|
+
const result2 = safeMergePnpm(forkPkg.pnpm, upstreamPkg.pnpm, basePkg?.pnpm);
|
|
3924
4051
|
if (result2?.changed) {
|
|
3925
4052
|
forkPkg.pnpm = result2.merged;
|
|
3926
4053
|
updated = true;
|
|
@@ -3931,14 +4058,16 @@ async function syncPackageJson(forkPath, upstreamRef, relativePath, keysToSync)
|
|
|
3931
4058
|
if (key === "packageManager") {
|
|
3932
4059
|
const upstreamValue2 = upstreamPkg[key];
|
|
3933
4060
|
const forkValue2 = forkPkg[key];
|
|
4061
|
+
const baseValue2 = basePkg?.[key];
|
|
3934
4062
|
if (upstreamValue2 && !forkValue2) {
|
|
4063
|
+
if (baseValue2) continue;
|
|
3935
4064
|
forkPkg[key] = upstreamValue2;
|
|
3936
4065
|
updated = true;
|
|
3937
4066
|
changes.push(`${key}: added`);
|
|
3938
|
-
} else if (upstreamValue2 && forkValue2 && upstreamValue2 !== forkValue2 && isHigherVersion(upstreamValue2, forkValue2)) {
|
|
4067
|
+
} else if (upstreamValue2 && forkValue2 && upstreamValue2 !== forkValue2 && (forkValue2 === baseValue2 || isHigherVersion(upstreamValue2, forkValue2))) {
|
|
3939
4068
|
forkPkg[key] = upstreamValue2;
|
|
3940
4069
|
updated = true;
|
|
3941
|
-
changes.push(`${key}:
|
|
4070
|
+
changes.push(`${key}: updated`);
|
|
3942
4071
|
}
|
|
3943
4072
|
continue;
|
|
3944
4073
|
}
|
|
@@ -3953,46 +4082,64 @@ async function syncPackageJson(forkPath, upstreamRef, relativePath, keysToSync)
|
|
|
3953
4082
|
}
|
|
3954
4083
|
const forkValue = forkPkg[key];
|
|
3955
4084
|
const upstreamValue = upstreamPkg[key];
|
|
3956
|
-
const
|
|
3957
|
-
|
|
4085
|
+
const baseValue = basePkg?.[key];
|
|
4086
|
+
let findUse;
|
|
4087
|
+
if (usage && key === "scripts") findUse = (name) => usage.findScript(name);
|
|
4088
|
+
else if (usage && DEPENDENCY_KEYS.includes(key)) findUse = (name) => usage.findPackage(relativePath, name);
|
|
4089
|
+
const result = mergeRecord(forkValue, upstreamValue, baseValue, { bumpHigher: key !== "scripts", findUse });
|
|
4090
|
+
kept.push(...(result?.kept ?? []).map(({ name, usedAt }) => `${key}.${name}: used in ${usedAt}`));
|
|
3958
4091
|
if (result?.changed) {
|
|
3959
4092
|
forkPkg[key] = result.merged;
|
|
3960
4093
|
updated = true;
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
4094
|
+
changes.push(
|
|
4095
|
+
...result.added.map((name) => `${key}.${name}: added`),
|
|
4096
|
+
...result.updated.map((name) => `${key}.${name}: updated`),
|
|
4097
|
+
...result.removed.map((name) => `${key}.${name}: removed`)
|
|
4098
|
+
);
|
|
3966
4099
|
}
|
|
3967
4100
|
}
|
|
3968
4101
|
if (updated) {
|
|
3969
4102
|
writePackageJson(pkgPath, forkPkg);
|
|
3970
4103
|
}
|
|
3971
|
-
return { updated, changes };
|
|
4104
|
+
return { updated, changes, kept };
|
|
3972
4105
|
}
|
|
3973
4106
|
async function runPackages(config, options = {}) {
|
|
3974
4107
|
createSpinner("syncing package.json files...");
|
|
3975
4108
|
const keysToSync = config.settings.packageJsonSync || ["dependencies", "devDependencies"];
|
|
3976
4109
|
const conflictedSet = new Set(options.conflictedFiles ?? []);
|
|
4110
|
+
const baseRef = await resolveMergeBase(config.forkPath, config.upstreamRef);
|
|
4111
|
+
const usage = baseRef ? await loadForkUsage(config.forkPath, config.upstreamRef, baseRef) : null;
|
|
3977
4112
|
const locations = await discoverPackageLocations(config.forkPath, config.upstreamRef);
|
|
3978
|
-
|
|
4113
|
+
const reports = [];
|
|
3979
4114
|
const skipped = [];
|
|
3980
4115
|
for (const location of locations) {
|
|
3981
|
-
const pkgRelPath = location
|
|
4116
|
+
const pkgRelPath = packageJsonPath(location);
|
|
3982
4117
|
if (conflictedSet.has(pkgRelPath)) {
|
|
3983
4118
|
skipped.push(pkgRelPath);
|
|
3984
4119
|
continue;
|
|
3985
4120
|
}
|
|
3986
4121
|
spinnerText(`syncing ${location || "root"}/package.json...`);
|
|
3987
|
-
const { updated } = await syncPackageJson(config
|
|
3988
|
-
if (updated) {
|
|
3989
|
-
changedCount += 1;
|
|
3990
|
-
}
|
|
4122
|
+
const { updated, changes, kept: kept2 } = await syncPackageJson(config, baseRef, usage, location, keysToSync);
|
|
4123
|
+
if (updated || kept2.length > 0) reports.push({ path: pkgRelPath, changes, kept: kept2 });
|
|
3991
4124
|
}
|
|
4125
|
+
const changedCount = reports.filter(({ changes }) => changes.length > 0).length;
|
|
3992
4126
|
spinnerSuccess(
|
|
3993
4127
|
"package sync complete",
|
|
3994
4128
|
`${changedCount} package.json${changedCount === 1 ? " was" : "s were"} changed`
|
|
3995
4129
|
);
|
|
4130
|
+
for (const { path: path4, changes } of reports) {
|
|
4131
|
+
if (changes.length === 0) continue;
|
|
4132
|
+
console.info(` ${path4}`);
|
|
4133
|
+
for (const change of changes) console.info(` ${colors_default.dim(change)}`);
|
|
4134
|
+
}
|
|
4135
|
+
const kept = reports.flatMap(({ path: path4, kept: kept2 }) => kept2.map((entry) => `${path4} ${entry}`));
|
|
4136
|
+
if (kept.length > 0) {
|
|
4137
|
+
console.info();
|
|
4138
|
+
console.warn(
|
|
4139
|
+
`${warningMark} kept ${kept.length} ${kept.length === 1 ? "entry" : "entries"} upstream dropped, the fork still uses ${kept.length === 1 ? "it" : "them"}:`
|
|
4140
|
+
);
|
|
4141
|
+
for (const entry of kept) console.warn(` ${colors_default.dim("\u2192")} ${entry}`);
|
|
4142
|
+
}
|
|
3996
4143
|
if (skipped.length > 0) {
|
|
3997
4144
|
console.info();
|
|
3998
4145
|
console.warn(
|
|
@@ -4477,7 +4624,7 @@ async function runForks(config) {
|
|
|
4477
4624
|
|
|
4478
4625
|
// src/services/stats.ts
|
|
4479
4626
|
import { readFile as readFile2 } from "fs/promises";
|
|
4480
|
-
import { join as
|
|
4627
|
+
import { join as join17 } from "path";
|
|
4481
4628
|
|
|
4482
4629
|
// src/utils/coverage-utils.ts
|
|
4483
4630
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
@@ -4488,6 +4635,7 @@ function summaryPath(forkPath) {
|
|
|
4488
4635
|
return path3.join(forkPath, ".coverage", "coverage-summary.json");
|
|
4489
4636
|
}
|
|
4490
4637
|
function colorPct(pct) {
|
|
4638
|
+
if (typeof pct !== "number" || !Number.isFinite(pct)) return colors_default.dim("n/a");
|
|
4491
4639
|
const label = `${pct.toFixed(2)}%`;
|
|
4492
4640
|
if (pct >= 80) return colors_default.green(label);
|
|
4493
4641
|
if (pct >= 50) return colors_default.yellow(label);
|
|
@@ -4550,6 +4698,11 @@ function printCoverageSummary(forkPath, options = {}) {
|
|
|
4550
4698
|
if (stale) {
|
|
4551
4699
|
console.info(colors_default.yellow(" \u26A0 data is older than 7 days \u2014 run `pnpm test` to refresh"));
|
|
4552
4700
|
}
|
|
4701
|
+
if (!Object.keys(summary).some((key) => key !== "total")) {
|
|
4702
|
+
console.info(colors_default.yellow(" no coverage data found \u2014 run `pnpm test` to regenerate"));
|
|
4703
|
+
console.info();
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4553
4706
|
const total = summary.total;
|
|
4554
4707
|
console.info(
|
|
4555
4708
|
` ${colors_default.bold("overall")} lines ${colorPct(total.lines.pct)} ${colors_default.dim(`(${total.lines.covered}/${total.lines.total})`)} funcs ${colorPct(total.functions.pct)} branches ${colorPct(total.branches.pct)}`
|
|
@@ -4565,7 +4718,7 @@ function printCoverageSummary(forkPath, options = {}) {
|
|
|
4565
4718
|
// src/services/stats.ts
|
|
4566
4719
|
var sourceExtensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs", "json", "jsonc", "css", "html"]);
|
|
4567
4720
|
async function getWorkspacePackages(forkPath) {
|
|
4568
|
-
const content = await readFile2(
|
|
4721
|
+
const content = await readFile2(join17(forkPath, "pnpm-workspace.yaml"), "utf-8");
|
|
4569
4722
|
return parseYamlBlockList(content, "packages").map((pattern) => {
|
|
4570
4723
|
if (pattern.endsWith("/*")) {
|
|
4571
4724
|
const base = pattern.slice(0, -2);
|
|
@@ -4728,7 +4881,7 @@ function getForkPath() {
|
|
|
4728
4881
|
const envPath = getEnv("CELLA_FORK_PATH");
|
|
4729
4882
|
if (envPath) {
|
|
4730
4883
|
const resolved = resolve6(envPath);
|
|
4731
|
-
if (!
|
|
4884
|
+
if (!existsSync11(resolved) || !statSync3(resolved).isDirectory()) {
|
|
4732
4885
|
throw new Error(`CELLA_FORK_PATH is not a valid directory: ${resolved}`);
|
|
4733
4886
|
}
|
|
4734
4887
|
return resolved;
|
|
@@ -4740,7 +4893,7 @@ function getForkPath() {
|
|
|
4740
4893
|
return cwd;
|
|
4741
4894
|
}
|
|
4742
4895
|
async function preflight(forkPath) {
|
|
4743
|
-
if (!
|
|
4896
|
+
if (!existsSync11(join18(forkPath, ".git"))) {
|
|
4744
4897
|
throw new Error(`not a git repository: ${forkPath}`);
|
|
4745
4898
|
}
|
|
4746
4899
|
}
|