isolate-package 1.32.0 → 1.33.0-0
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/dist/index.mjs +1 -1
- package/dist/{isolate-BRD2AgVJ.mjs → isolate-DTwgcMAN.mjs} +357 -36
- package/dist/isolate-DTwgcMAN.mjs.map +1 -0
- package/dist/isolate-bin.mjs +1 -1
- package/package.json +1 -1
- package/src/isolate.ts +8 -4
- package/src/lib/lockfile/helpers/bun-lockfile.ts +143 -0
- package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +7 -139
- package/src/lib/patches/collect-installed-names-bun.test.ts +154 -0
- package/src/lib/patches/collect-installed-names-bun.ts +87 -0
- package/src/lib/patches/collect-installed-names-pnpm.test.ts +316 -0
- package/src/lib/patches/collect-installed-names-pnpm.ts +364 -0
- package/src/lib/patches/copy-patches.test.ts +125 -0
- package/src/lib/patches/copy-patches.ts +38 -1
- package/src/lib/patches/write-isolate-pnpm-workspace.test.ts +189 -0
- package/src/lib/patches/write-isolate-pnpm-workspace.ts +80 -0
- package/dist/isolate-BRD2AgVJ.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as detectPackageManager, i as defineConfig, l as readTypedJson, n as listInternalPackages, o as resolveConfig, r as createPackagesRegistry, s as resolveWorkspacePaths, t as isolate } from "./isolate-
|
|
1
|
+
import { c as detectPackageManager, i as defineConfig, l as readTypedJson, n as listInternalPackages, o as resolveConfig, r as createPackagesRegistry, s as resolveWorkspacePaths, t as isolate } from "./isolate-DTwgcMAN.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
//#region src/get-internal-package-names.ts
|
|
4
4
|
/**
|
|
@@ -486,30 +486,8 @@ function resolveConfig(initialConfig) {
|
|
|
486
486
|
return config;
|
|
487
487
|
}
|
|
488
488
|
//#endregion
|
|
489
|
-
//#region src/lib/lockfile/helpers/
|
|
490
|
-
/**
|
|
491
|
-
* Serialize a value to JSON with trailing commas after every array element and
|
|
492
|
-
* object property, matching Bun's native bun.lock output format.
|
|
493
|
-
*/
|
|
494
|
-
function serializeWithTrailingCommas(value, indent = 2) {
|
|
495
|
-
/**
|
|
496
|
-
* Add trailing commas after values that precede a closing bracket/brace.
|
|
497
|
-
* Apply repeatedly because consecutive closing brackets (e.g. ]\n}) need
|
|
498
|
-
* multiple passes — the first pass adds a comma after the inner value, and
|
|
499
|
-
* subsequent passes handle the outer brackets.
|
|
500
|
-
*/
|
|
501
|
-
let result = JSON.stringify(value, null, indent);
|
|
502
|
-
let previous;
|
|
503
|
-
do {
|
|
504
|
-
previous = result;
|
|
505
|
-
result = result.replace(/(["\d\w\]}-])\n(\s*[\]}])/g, "$1,\n$2");
|
|
506
|
-
} while (result !== previous);
|
|
507
|
-
return result;
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Extract dependency names from a workspace entry, optionally including
|
|
511
|
-
* devDependencies.
|
|
512
|
-
*/
|
|
489
|
+
//#region src/lib/lockfile/helpers/bun-lockfile.ts
|
|
490
|
+
/** Extract dependency names from a workspace entry. */
|
|
513
491
|
function collectDependencyNames(entry, includeDevDependencies) {
|
|
514
492
|
const names = /* @__PURE__ */ new Set();
|
|
515
493
|
for (const name of Object.keys(entry.dependencies ?? {})) names.add(name);
|
|
@@ -519,8 +497,8 @@ function collectDependencyNames(entry, includeDevDependencies) {
|
|
|
519
497
|
return [...names];
|
|
520
498
|
}
|
|
521
499
|
/**
|
|
522
|
-
* Check whether a package entry represents a workspace package by examining
|
|
523
|
-
* identifier string (first element in the entry array).
|
|
500
|
+
* Check whether a package entry represents a workspace package by examining
|
|
501
|
+
* its identifier string (first element in the entry array).
|
|
524
502
|
*/
|
|
525
503
|
function isWorkspacePackageEntry(entry) {
|
|
526
504
|
const ident = entry[0];
|
|
@@ -532,16 +510,16 @@ function isWorkspacePackageEntry(entry) {
|
|
|
532
510
|
* - workspace packages: [ident, info] -> index 1
|
|
533
511
|
* - git/github packages: [ident, info, checksum] -> index 1
|
|
534
512
|
*
|
|
535
|
-
* Detection: if the second element is a string (registry URL or checksum),
|
|
536
|
-
* info object is deeper. Workspace entries have only 2 elements.
|
|
513
|
+
* Detection: if the second element is a string (registry URL or checksum),
|
|
514
|
+
* the info object is deeper. Workspace entries have only 2 elements.
|
|
537
515
|
*/
|
|
538
516
|
function getPackageInfoObject(entry) {
|
|
539
517
|
if (entry.length <= 1) return void 0;
|
|
540
518
|
/** Workspace entries: [ident, info] */
|
|
541
519
|
if (isWorkspacePackageEntry(entry)) return typeof entry[1] === "object" ? entry[1] : void 0;
|
|
542
520
|
/**
|
|
543
|
-
* npm entries with registry URL: [ident, registryUrl, info, checksum].
|
|
544
|
-
*
|
|
521
|
+
* npm entries with registry URL: [ident, registryUrl, info, checksum]. The
|
|
522
|
+
* second element is a string (the registry URL).
|
|
545
523
|
*/
|
|
546
524
|
if (typeof entry[1] === "string") return typeof entry[2] === "object" ? entry[2] : void 0;
|
|
547
525
|
/** git/tarball entries: [ident, info, checksum] */
|
|
@@ -549,8 +527,8 @@ function getPackageInfoObject(entry) {
|
|
|
549
527
|
}
|
|
550
528
|
/**
|
|
551
529
|
* Recursively collect all package keys that are required, starting from a set
|
|
552
|
-
* of direct dependency names and walking through their transitive
|
|
553
|
-
* in the packages section.
|
|
530
|
+
* of direct dependency names and walking through their transitive
|
|
531
|
+
* dependencies in the packages section.
|
|
554
532
|
*/
|
|
555
533
|
function collectRequiredPackages(directDependencyNames, packages) {
|
|
556
534
|
const required = /* @__PURE__ */ new Set();
|
|
@@ -577,6 +555,27 @@ function collectRequiredPackages(directDependencyNames, packages) {
|
|
|
577
555
|
}
|
|
578
556
|
return required;
|
|
579
557
|
}
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/lib/lockfile/helpers/generate-bun-lockfile.ts
|
|
560
|
+
/**
|
|
561
|
+
* Serialize a value to JSON with trailing commas after every array element and
|
|
562
|
+
* object property, matching Bun's native bun.lock output format.
|
|
563
|
+
*/
|
|
564
|
+
function serializeWithTrailingCommas(value, indent = 2) {
|
|
565
|
+
/**
|
|
566
|
+
* Add trailing commas after values that precede a closing bracket/brace.
|
|
567
|
+
* Apply repeatedly because consecutive closing brackets (e.g. ]\n}) need
|
|
568
|
+
* multiple passes — the first pass adds a comma after the inner value, and
|
|
569
|
+
* subsequent passes handle the outer brackets.
|
|
570
|
+
*/
|
|
571
|
+
let result = JSON.stringify(value, null, indent);
|
|
572
|
+
let previous;
|
|
573
|
+
do {
|
|
574
|
+
previous = result;
|
|
575
|
+
result = result.replace(/(["\d\w\]}-])\n(\s*[\]}])/g, "$1,\n$2");
|
|
576
|
+
} while (result !== previous);
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
580
579
|
async function generateBunLockfile({ workspaceRootDir, targetPackageDir, isolateDir, internalDepPackageNames, packagesRegistry, includeDevDependencies }) {
|
|
581
580
|
const log = useLogger();
|
|
582
581
|
log.debug("Generating Bun lockfile...");
|
|
@@ -1553,10 +1552,252 @@ function listInternalPackages(manifest, packagesRegistry, { includeDevDependenci
|
|
|
1553
1552
|
return [...new Set(result)];
|
|
1554
1553
|
}
|
|
1555
1554
|
//#endregion
|
|
1555
|
+
//#region src/lib/patches/collect-installed-names-bun.ts
|
|
1556
|
+
/**
|
|
1557
|
+
* Walk the workspace bun.lock starting from the target package and its
|
|
1558
|
+
* internal workspace dependencies, returning the set of every package name
|
|
1559
|
+
* that will end up installed in the isolate (including deep
|
|
1560
|
+
* external-to-external transitives).
|
|
1561
|
+
*
|
|
1562
|
+
* Used by `copyPatches` to preserve patches for transitive deps that aren't
|
|
1563
|
+
* directly listed on any internal manifest. Returns an empty set on any
|
|
1564
|
+
* failure so the caller falls back to manifest-based reachability.
|
|
1565
|
+
*/
|
|
1566
|
+
function collectInstalledNamesFromBunLockfile({ workspaceRootDir, targetPackageDir, internalDepPackageNames, packagesRegistry, includeDevDependencies }) {
|
|
1567
|
+
const log = useLogger();
|
|
1568
|
+
try {
|
|
1569
|
+
const lockfilePath = path.join(workspaceRootDir, "bun.lock");
|
|
1570
|
+
if (!fs.existsSync(lockfilePath)) {
|
|
1571
|
+
log.debug("No bun.lock available for installed-names walk");
|
|
1572
|
+
return /* @__PURE__ */ new Set();
|
|
1573
|
+
}
|
|
1574
|
+
const lockfile = readTypedJsonSync(lockfilePath);
|
|
1575
|
+
const targetWorkspaceKey = path.relative(workspaceRootDir, targetPackageDir).split(path.sep).join(path.posix.sep);
|
|
1576
|
+
const internalWorkspaceKeys = internalDepPackageNames.map((name) => {
|
|
1577
|
+
const pkg = packagesRegistry[name];
|
|
1578
|
+
if (!pkg) return null;
|
|
1579
|
+
return pkg.rootRelativeDir.split(path.sep).join(path.posix.sep);
|
|
1580
|
+
}).filter((key) => Boolean(key));
|
|
1581
|
+
const directDependencyNames = /* @__PURE__ */ new Set();
|
|
1582
|
+
const targetEntry = lockfile.workspaces[targetWorkspaceKey];
|
|
1583
|
+
if (targetEntry) for (const name of collectDependencyNames(targetEntry, includeDevDependencies)) directDependencyNames.add(name);
|
|
1584
|
+
for (const workspaceKey of internalWorkspaceKeys) {
|
|
1585
|
+
const entry = lockfile.workspaces[workspaceKey];
|
|
1586
|
+
if (!entry) continue;
|
|
1587
|
+
/** Internal workspace deps never bring in their devDependencies */
|
|
1588
|
+
for (const name of collectDependencyNames(entry, false)) directDependencyNames.add(name);
|
|
1589
|
+
}
|
|
1590
|
+
return collectRequiredPackages(directDependencyNames, lockfile.packages);
|
|
1591
|
+
} catch (err) {
|
|
1592
|
+
log.debug(`Failed to walk bun.lock for installed names: ${err instanceof Error ? err.message : String(err)}`);
|
|
1593
|
+
return /* @__PURE__ */ new Set();
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
//#endregion
|
|
1597
|
+
//#region src/lib/patches/collect-installed-names-pnpm.ts
|
|
1598
|
+
/**
|
|
1599
|
+
* Walk the workspace pnpm lockfile starting from the target package and its
|
|
1600
|
+
* internal workspace dependencies, returning the set of every package name
|
|
1601
|
+
* that will end up installed in the isolate (including deep
|
|
1602
|
+
* external-to-external transitives).
|
|
1603
|
+
*
|
|
1604
|
+
* Used by `copyPatches` to preserve patches for transitive deps that aren't
|
|
1605
|
+
* directly listed on any internal manifest. Returns an empty set on any
|
|
1606
|
+
* failure so the caller falls back to manifest-based reachability. When the
|
|
1607
|
+
* lockfile is present but lacks a `packages` section, returns just the
|
|
1608
|
+
* direct importer dep names.
|
|
1609
|
+
*/
|
|
1610
|
+
async function collectInstalledNamesFromPnpmLockfile({ workspaceRootDir, targetPackageDir, internalDepPackageNames, packagesRegistry, majorVersion, includeDevDependencies }) {
|
|
1611
|
+
const log = useLogger();
|
|
1612
|
+
try {
|
|
1613
|
+
const useVersion9 = majorVersion >= 9;
|
|
1614
|
+
const isRush = isRushWorkspace(workspaceRootDir);
|
|
1615
|
+
const lockfileDir = isRush ? path.join(workspaceRootDir, "common/config/rush") : workspaceRootDir;
|
|
1616
|
+
const lockfile = useVersion9 ? await readWantedLockfile$1(lockfileDir, { ignoreIncompatible: false }) : await readWantedLockfile(lockfileDir, { ignoreIncompatible: false });
|
|
1617
|
+
if (!lockfile) {
|
|
1618
|
+
log.debug("No pnpm lockfile available for installed-names walk");
|
|
1619
|
+
return /* @__PURE__ */ new Set();
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Normalize separators to POSIX so Windows callers match the lockfile's
|
|
1623
|
+
* importer keys (mirrors generate-pnpm-lockfile.ts). Applied once here so
|
|
1624
|
+
* the `isTarget` equality check below compares apples-to-apples — without
|
|
1625
|
+
* this, on Windows the raw id with backslashes wouldn't match the
|
|
1626
|
+
* normalized id used as the importers map key.
|
|
1627
|
+
*/
|
|
1628
|
+
const targetImporterId = toLockfileImporterKey(useVersion9 ? getLockfileImporterId$1(workspaceRootDir, targetPackageDir) : getLockfileImporterId(workspaceRootDir, targetPackageDir), isRush);
|
|
1629
|
+
const importerIds = [targetImporterId, ...internalDepPackageNames.map((name) => packagesRegistry[name]?.rootRelativeDir).filter((dir) => Boolean(dir)).map((dir) => toLockfileImporterKey(dir, isRush))];
|
|
1630
|
+
const packages = lockfile.packages;
|
|
1631
|
+
if (!packages) {
|
|
1632
|
+
log.debug("Lockfile has no packages section to walk");
|
|
1633
|
+
return collectImporterDirectNames(lockfile.importers, importerIds, targetImporterId, includeDevDependencies);
|
|
1634
|
+
}
|
|
1635
|
+
const names = /* @__PURE__ */ new Set();
|
|
1636
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1637
|
+
const queue = [];
|
|
1638
|
+
for (const importerId of importerIds) {
|
|
1639
|
+
const importer = lockfile.importers[importerId];
|
|
1640
|
+
if (!importer) continue;
|
|
1641
|
+
enqueueImporterDeps({
|
|
1642
|
+
importer,
|
|
1643
|
+
names,
|
|
1644
|
+
queue,
|
|
1645
|
+
useVersion9,
|
|
1646
|
+
includeDevDependencies: importerId === targetImporterId && includeDevDependencies
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
while (queue.length > 0) {
|
|
1650
|
+
const depPath = queue.pop();
|
|
1651
|
+
if (seen.has(depPath)) continue;
|
|
1652
|
+
seen.add(depPath);
|
|
1653
|
+
names.add(extractPackageName(depPath));
|
|
1654
|
+
const pkg = packages[depPath];
|
|
1655
|
+
if (!pkg) continue;
|
|
1656
|
+
enqueueResolvedDeps(pkg.dependencies, names, queue, useVersion9, seen);
|
|
1657
|
+
enqueueResolvedDeps(pkg.optionalDependencies, names, queue, useVersion9, seen);
|
|
1658
|
+
/**
|
|
1659
|
+
* Peer requirement values are name → semver-range, not resolved depPaths.
|
|
1660
|
+
* Just record the names so a patch on a peer-only external transitive
|
|
1661
|
+
* survives filtering (mirrors the bun walker and the sister manifest
|
|
1662
|
+
* walker, which both include peerDependencies).
|
|
1663
|
+
*/
|
|
1664
|
+
collectNames(pkg.peerDependencies, names);
|
|
1665
|
+
}
|
|
1666
|
+
return names;
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
log.debug(`Failed to walk pnpm lockfile for installed names: ${err instanceof Error ? err.message : String(err)}`);
|
|
1669
|
+
return /* @__PURE__ */ new Set();
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Convert a raw importer id (as returned by `getLockfileImporterId` or a
|
|
1674
|
+
* package's rootRelativeDir) to the form actually used as a key in
|
|
1675
|
+
* `lockfile.importers`: POSIX separators, with the Rush `../../` prefix when
|
|
1676
|
+
* the workspace lives under `common/config/rush`. Lockfile keys are always
|
|
1677
|
+
* POSIX regardless of the host OS, so backslashes are normalized
|
|
1678
|
+
* unconditionally rather than relying on `path.sep`.
|
|
1679
|
+
*/
|
|
1680
|
+
function toLockfileImporterKey(importerId, isRush) {
|
|
1681
|
+
const posix = importerId.split(path.sep).join(path.posix.sep).replace(/\\/g, "/");
|
|
1682
|
+
return isRush ? `../../${posix}` : posix;
|
|
1683
|
+
}
|
|
1684
|
+
function enqueueImporterDeps({ importer, names, queue, useVersion9, includeDevDependencies }) {
|
|
1685
|
+
enqueueResolvedDeps(importer.dependencies, names, queue, useVersion9);
|
|
1686
|
+
enqueueResolvedDeps(importer.optionalDependencies, names, queue, useVersion9);
|
|
1687
|
+
if (includeDevDependencies) enqueueResolvedDeps(importer.devDependencies, names, queue, useVersion9);
|
|
1688
|
+
/**
|
|
1689
|
+
* Importer peerDependencies usually aren't a separate map in the lockfile
|
|
1690
|
+
* (autoInstallPeers folds them into `dependencies`), but record names if
|
|
1691
|
+
* they happen to be present.
|
|
1692
|
+
*/
|
|
1693
|
+
collectNames(importer.peerDependencies, names);
|
|
1694
|
+
}
|
|
1695
|
+
function enqueueResolvedDeps(deps, names, queue, useVersion9, seen) {
|
|
1696
|
+
if (!deps) return;
|
|
1697
|
+
for (const [alias, ref] of Object.entries(deps)) {
|
|
1698
|
+
/**
|
|
1699
|
+
* The alias is the name as listed in the parent's dependencies map. For
|
|
1700
|
+
* non-aliased installs this is also the resolved package name. We add it
|
|
1701
|
+
* to the set as a candidate name; visiting the actual depPath below
|
|
1702
|
+
* refines this with the true installed name.
|
|
1703
|
+
*/
|
|
1704
|
+
names.add(alias);
|
|
1705
|
+
const depPath = refToRelative(ref, alias, useVersion9);
|
|
1706
|
+
if (depPath && !seen?.has(depPath)) queue.push(depPath);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
function collectNames(deps, names) {
|
|
1710
|
+
if (!deps) return;
|
|
1711
|
+
for (const name of Object.keys(deps)) names.add(name);
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Mirrors `@pnpm/dependency-path`'s `refToRelative`. The depPath shape differs
|
|
1715
|
+
* between pnpm 8 (lockfile v6, normalized to v5 keys like `/foo/1.0.0`) and
|
|
1716
|
+
* pnpm 9 (lockfile v9 keys like `foo@1.0.0`). Returns the depPath used as a
|
|
1717
|
+
* key in `lockfile.packages`, or null if the ref points to a workspace link.
|
|
1718
|
+
*/
|
|
1719
|
+
function refToRelative(reference, pkgName, useVersion9) {
|
|
1720
|
+
if (!reference) return null;
|
|
1721
|
+
if (reference.startsWith("link:")) return null;
|
|
1722
|
+
return useVersion9 ? refToRelativeV9(reference, pkgName) : refToRelativeV8(reference, pkgName);
|
|
1723
|
+
}
|
|
1724
|
+
function refToRelativeV9(reference, pkgName) {
|
|
1725
|
+
if (reference.startsWith("@")) return reference;
|
|
1726
|
+
const atIndex = reference.indexOf("@");
|
|
1727
|
+
if (atIndex === -1) return `${pkgName}@${reference}`;
|
|
1728
|
+
const colonIndex = reference.indexOf(":");
|
|
1729
|
+
const bracketIndex = reference.indexOf("(");
|
|
1730
|
+
if ((colonIndex === -1 || atIndex < colonIndex) && (bracketIndex === -1 || atIndex < bracketIndex)) return reference;
|
|
1731
|
+
return `${pkgName}@${reference}`;
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* v8 form: pnpm 8 (lockfile v6) is normalized on read to v5-style depPaths
|
|
1735
|
+
* with leading slash and `/` separator between name and version. Plain
|
|
1736
|
+
* version refs build that key; refs already containing a `/` (peer-suffixed
|
|
1737
|
+
* or pre-formed) are returned verbatim. Mirrors `@pnpm/dependency-path@2.x`.
|
|
1738
|
+
*/
|
|
1739
|
+
function refToRelativeV8(reference, pkgName) {
|
|
1740
|
+
if (reference.startsWith("file:")) return reference;
|
|
1741
|
+
const slashIndex = reference.indexOf("/");
|
|
1742
|
+
const bracketIndex = reference.indexOf("(");
|
|
1743
|
+
const noSlashBeforeBracket = bracketIndex !== -1 && reference.lastIndexOf("/", bracketIndex) === -1;
|
|
1744
|
+
if (slashIndex === -1 || noSlashBeforeBracket) return `/${pkgName}/${reference}`;
|
|
1745
|
+
return reference;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Extract the bare package name from a pnpm depPath. Strips the optional
|
|
1749
|
+
* peer-resolution suffix (e.g. `(react@18.0.0)`) before parsing. Handles
|
|
1750
|
+
* both v9 (`@scope/foo@1.0.0`) and v8 (`/@scope/foo/1.0.0`) shapes.
|
|
1751
|
+
*/
|
|
1752
|
+
function extractPackageName(depPath) {
|
|
1753
|
+
const peerStart = indexOfPeersSuffix(depPath);
|
|
1754
|
+
const trimmed = peerStart === -1 ? depPath : depPath.substring(0, peerStart);
|
|
1755
|
+
if (trimmed.startsWith("/")) {
|
|
1756
|
+
/** v8 v5-style: `/<name>/<version>` */
|
|
1757
|
+
const stripped = trimmed.slice(1);
|
|
1758
|
+
if (stripped.startsWith("@")) {
|
|
1759
|
+
const secondSlash = stripped.indexOf("/", stripped.indexOf("/") + 1);
|
|
1760
|
+
return secondSlash === -1 ? stripped : stripped.slice(0, secondSlash);
|
|
1761
|
+
}
|
|
1762
|
+
const firstSlash = stripped.indexOf("/");
|
|
1763
|
+
return firstSlash === -1 ? stripped : stripped.slice(0, firstSlash);
|
|
1764
|
+
}
|
|
1765
|
+
return getPackageName(trimmed);
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Mirrors `@pnpm/dependency-path`'s `indexOfPeersSuffix`. Returns the index
|
|
1769
|
+
* where the peer-resolution suffix starts, or -1 if there is none.
|
|
1770
|
+
*/
|
|
1771
|
+
function indexOfPeersSuffix(depPath) {
|
|
1772
|
+
if (!depPath.endsWith(")")) return -1;
|
|
1773
|
+
let open = 1;
|
|
1774
|
+
for (let i = depPath.length - 2; i >= 0; i--) if (depPath[i] === "(") open--;
|
|
1775
|
+
else if (depPath[i] === ")") open++;
|
|
1776
|
+
else if (!open) return i + 1;
|
|
1777
|
+
return -1;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Fallback when the lockfile is missing `packages`: just return importer
|
|
1781
|
+
* direct dep names so we at least cover some of the graph.
|
|
1782
|
+
*/
|
|
1783
|
+
function collectImporterDirectNames(importers, importerIds, targetImporterId, includeDevDependencies) {
|
|
1784
|
+
const names = /* @__PURE__ */ new Set();
|
|
1785
|
+
for (const importerId of importerIds) {
|
|
1786
|
+
const importer = importers[importerId];
|
|
1787
|
+
if (!importer) continue;
|
|
1788
|
+
const isTarget = importerId === targetImporterId;
|
|
1789
|
+
for (const name of Object.keys(importer.dependencies ?? {})) names.add(name);
|
|
1790
|
+
for (const name of Object.keys(importer.optionalDependencies ?? {})) names.add(name);
|
|
1791
|
+
for (const name of Object.keys(importer.peerDependencies ?? {})) names.add(name);
|
|
1792
|
+
if (isTarget && includeDevDependencies) for (const name of Object.keys(importer.devDependencies ?? {})) names.add(name);
|
|
1793
|
+
}
|
|
1794
|
+
return names;
|
|
1795
|
+
}
|
|
1796
|
+
//#endregion
|
|
1556
1797
|
//#region src/lib/patches/copy-patches.ts
|
|
1557
|
-
async function copyPatches({ workspaceRootDir, targetPackageManifest, packagesRegistry, isolateDir, includeDevDependencies }) {
|
|
1798
|
+
async function copyPatches({ workspaceRootDir, targetPackageDir, targetPackageManifest, packagesRegistry, internalDepPackageNames, isolateDir, includeDevDependencies }) {
|
|
1558
1799
|
const log = useLogger();
|
|
1559
|
-
const { name: packageManagerName } = usePackageManager();
|
|
1800
|
+
const { name: packageManagerName, majorVersion } = usePackageManager();
|
|
1560
1801
|
let patchedDependencies;
|
|
1561
1802
|
/**
|
|
1562
1803
|
* Only try reading pnpm-workspace.yaml for pnpm workspaces. Bun workspaces
|
|
@@ -1594,6 +1835,28 @@ async function copyPatches({ workspaceRootDir, targetPackageManifest, packagesRe
|
|
|
1594
1835
|
packagesRegistry,
|
|
1595
1836
|
includeDevDependencies
|
|
1596
1837
|
});
|
|
1838
|
+
/**
|
|
1839
|
+
* Manifest-based reachability misses external→external transitives because
|
|
1840
|
+
* external manifests aren't loaded here. Walk the package-manager's
|
|
1841
|
+
* lockfile to also pick up those names, so a patch for a deeply-nested
|
|
1842
|
+
* external dep (e.g. `@react-pdf/render` reached via `@react-pdf/renderer`)
|
|
1843
|
+
* survives isolation.
|
|
1844
|
+
*/
|
|
1845
|
+
const lockfileInstalledNames = packageManagerName === "pnpm" ? await collectInstalledNamesFromPnpmLockfile({
|
|
1846
|
+
workspaceRootDir,
|
|
1847
|
+
targetPackageDir,
|
|
1848
|
+
internalDepPackageNames,
|
|
1849
|
+
packagesRegistry,
|
|
1850
|
+
majorVersion,
|
|
1851
|
+
includeDevDependencies
|
|
1852
|
+
}) : packageManagerName === "bun" ? collectInstalledNamesFromBunLockfile({
|
|
1853
|
+
workspaceRootDir,
|
|
1854
|
+
targetPackageDir,
|
|
1855
|
+
internalDepPackageNames,
|
|
1856
|
+
packagesRegistry,
|
|
1857
|
+
includeDevDependencies
|
|
1858
|
+
}) : /* @__PURE__ */ new Set();
|
|
1859
|
+
for (const name of lockfileInstalledNames) reachableDependencyNames.add(name);
|
|
1597
1860
|
const filteredPatches = filterPatchedDependencies({
|
|
1598
1861
|
patchedDependencies,
|
|
1599
1862
|
targetPackageManifest,
|
|
@@ -1644,6 +1907,58 @@ async function readLockfilePatchedDependencies(workspaceRootDir) {
|
|
|
1644
1907
|
}
|
|
1645
1908
|
}
|
|
1646
1909
|
//#endregion
|
|
1910
|
+
//#region src/lib/patches/write-isolate-pnpm-workspace.ts
|
|
1911
|
+
/**
|
|
1912
|
+
* Copy `pnpm-workspace.yaml` from the workspace root to the isolate directory,
|
|
1913
|
+
* filtering its `patchedDependencies` field so it only references patches that
|
|
1914
|
+
* were actually copied to the isolate. Without this, `pnpm install` in the
|
|
1915
|
+
* isolate fails when patches that don't apply to the target package are
|
|
1916
|
+
* declared in the workspace root config (see issue #178).
|
|
1917
|
+
*
|
|
1918
|
+
* The yaml is only rewritten when filtering is required. The file is copied
|
|
1919
|
+
* verbatim — preserving comments, key order, and trailing whitespace — when
|
|
1920
|
+
* any of the following hold:
|
|
1921
|
+
*
|
|
1922
|
+
* - The source yaml cannot be read or parsed.
|
|
1923
|
+
* - The parsed settings have no `patchedDependencies` field.
|
|
1924
|
+
* - Every entry in `patchedDependencies` is also present in `copiedPatches`
|
|
1925
|
+
* (no exclusions, so rewriting would only churn formatting).
|
|
1926
|
+
*
|
|
1927
|
+
* Otherwise, `patchedDependencies` is rewritten to the entries in
|
|
1928
|
+
* `copiedPatches` (or removed entirely when none remain).
|
|
1929
|
+
*/
|
|
1930
|
+
function writeIsolatePnpmWorkspace({ workspaceRootDir, isolateDir, copiedPatches }) {
|
|
1931
|
+
const log = useLogger();
|
|
1932
|
+
const sourcePath = path.join(workspaceRootDir, "pnpm-workspace.yaml");
|
|
1933
|
+
const targetPath = path.join(isolateDir, "pnpm-workspace.yaml");
|
|
1934
|
+
let settings;
|
|
1935
|
+
try {
|
|
1936
|
+
settings = readTypedYamlSync(sourcePath);
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
log.warn(`Could not read pnpm-workspace.yaml, falling back to verbatim copy: ${error instanceof Error ? error.message : String(error)}`);
|
|
1939
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
if (!settings || !settings.patchedDependencies) {
|
|
1943
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* If every patch declared in the source yaml was kept, copy verbatim so
|
|
1948
|
+
* comments, ordering, and trailing whitespace are preserved.
|
|
1949
|
+
*/
|
|
1950
|
+
const sourceSpecs = Object.keys(settings.patchedDependencies);
|
|
1951
|
+
const copiedSpecs = new Set(Object.keys(copiedPatches));
|
|
1952
|
+
if (!sourceSpecs.some((spec) => !copiedSpecs.has(spec))) {
|
|
1953
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
const filteredEntries = Object.entries(copiedPatches).map(([spec, patchFile]) => [spec, patchFile.path]);
|
|
1957
|
+
if (filteredEntries.length > 0) settings.patchedDependencies = Object.fromEntries(filteredEntries);
|
|
1958
|
+
else delete settings.patchedDependencies;
|
|
1959
|
+
writeTypedYamlSync(targetPath, settings);
|
|
1960
|
+
}
|
|
1961
|
+
//#endregion
|
|
1647
1962
|
//#region src/isolate.ts
|
|
1648
1963
|
const __dirname = getDirname(import.meta.url);
|
|
1649
1964
|
function createIsolator(config) {
|
|
@@ -1742,8 +2057,10 @@ function createIsolator(config) {
|
|
|
1742
2057
|
await writeManifest(isolateDir, outputManifest);
|
|
1743
2058
|
const copiedPatches = (packageManager.name === "pnpm" || packageManager.name === "bun") && !config.forceNpm ? await copyPatches({
|
|
1744
2059
|
workspaceRootDir,
|
|
2060
|
+
targetPackageDir,
|
|
1745
2061
|
targetPackageManifest: outputManifest,
|
|
1746
2062
|
packagesRegistry,
|
|
2063
|
+
internalDepPackageNames: internalPackageNames,
|
|
1747
2064
|
isolateDir,
|
|
1748
2065
|
includeDevDependencies: config.includeDevDependencies
|
|
1749
2066
|
}) : {};
|
|
@@ -1794,7 +2111,11 @@ function createIsolator(config) {
|
|
|
1794
2111
|
log.debug("Packages folder names:", packagesFolderNames);
|
|
1795
2112
|
const packages = packagesFolderNames.map((x) => path.join(x, "/*"));
|
|
1796
2113
|
writeTypedYamlSync(path.join(isolateDir, "pnpm-workspace.yaml"), { packages });
|
|
1797
|
-
} else
|
|
2114
|
+
} else writeIsolatePnpmWorkspace({
|
|
2115
|
+
workspaceRootDir,
|
|
2116
|
+
isolateDir,
|
|
2117
|
+
copiedPatches
|
|
2118
|
+
});
|
|
1798
2119
|
if (packageManager.name === "bun" && !config.forceNpm) {
|
|
1799
2120
|
/** Add workspaces field to the manifest so Bun treats the isolate as a workspace */
|
|
1800
2121
|
const manifest = await readManifest(isolateDir);
|
|
@@ -1837,4 +2158,4 @@ async function isolate(config) {
|
|
|
1837
2158
|
//#endregion
|
|
1838
2159
|
export { loadConfigFromFile as a, detectPackageManager as c, defineConfig as i, readTypedJson as l, listInternalPackages as n, resolveConfig as o, createPackagesRegistry as r, resolveWorkspacePaths as s, isolate as t, filterObjectUndefined as u };
|
|
1839
2160
|
|
|
1840
|
-
//# sourceMappingURL=isolate-
|
|
2161
|
+
//# sourceMappingURL=isolate-DTwgcMAN.mjs.map
|