|
1 | 1 | // .pnpmfile.cjs — Two jobs: |
2 | 2 | // |
3 | | -// 1. Auto-replay: clone + patch CCC on first `pnpm install` (if pins exist). |
| 3 | +// 1. Auto-replay: clone + patch managed forks on first `pnpm install` (if pins exist). |
4 | 4 | // replay.sh handles git clone, merge replay, lockfile removal, and source |
5 | 5 | // patching (jq exports rewrite). It does NOT run pnpm install |
6 | | -// internally — the root workspace install handles CCC deps alongside |
| 6 | +// internally — the root workspace install handles fork deps alongside |
7 | 7 | // everything else. |
8 | 8 | // |
9 | | -// 2. readPackage hook: rewrite CCC deps from catalog ranges to workspace:*. |
10 | | -// CCC packages live in pnpm-workspace.yaml, so you'd expect pnpm to link |
| 9 | +// 2. readPackage hook: rewrite fork deps from catalog ranges to workspace:*. |
| 10 | +// Fork packages live in pnpm-workspace.yaml, so you'd expect pnpm to link |
11 | 11 | // them automatically. It doesn't — catalog: specifiers resolve to a semver |
12 | 12 | // range (e.g. ^1.12.2) BEFORE workspace linking is considered, so pnpm |
13 | 13 | // fetches from the registry even with link-workspace-packages = true. |
14 | 14 | // This hook intercepts every package.json at resolution time and forces |
15 | | -// workspace:* for any dep whose name matches a local CCC package. |
16 | | -// When CCC is not cloned, hasCcc is false and the hook is a no-op, so |
17 | | -// the catalog range falls through to the registry normally. |
| 15 | +// workspace:* for any dep whose name matches a local fork package. |
| 16 | +// When no forks are cloned, the hook is a no-op, so catalog ranges fall |
| 17 | +// through to the registry normally. |
18 | 18 |
|
19 | | -const { execSync } = require("child_process"); |
| 19 | +const { execFileSync } = require("child_process"); |
20 | 20 | const { existsSync, readdirSync, readFileSync } = require("fs"); |
21 | 21 | const { join } = require("path"); |
22 | 22 |
|
23 | | -const cccCache = join(__dirname, "ccc-dev", "ccc"); |
24 | | -const cccRefs = join(__dirname, "ccc-dev", "pins", "REFS"); |
| 23 | +// Discover all *-fork/ directories with config.json |
| 24 | +const forkDirs = []; |
| 25 | +for (const entry of readdirSync(__dirname, { withFileTypes: true })) { |
| 26 | + if (!entry.isDirectory() || !entry.name.endsWith("-fork")) continue; |
| 27 | + const configPath = join(__dirname, entry.name, "config.json"); |
| 28 | + if (existsSync(configPath)) { |
| 29 | + const config = JSON.parse(readFileSync(configPath, "utf8")); |
| 30 | + if (!config.cloneDir) continue; |
| 31 | + forkDirs.push({ |
| 32 | + name: entry.name, |
| 33 | + dir: join(__dirname, entry.name), |
| 34 | + config, |
| 35 | + }); |
| 36 | + } |
| 37 | +} |
25 | 38 |
|
26 | | -// 1. Auto-replay CCC pins on first pnpm install |
27 | | -// Skip when ccc:record is running — it rebuilds pins from scratch. |
| 39 | +// 1. Auto-replay fork pins on first pnpm install |
| 40 | +// Skip when fork:record is running — it rebuilds pins from scratch. |
28 | 41 | // Detect via argv since pnpmfile loads before npm_lifecycle_event is set. |
29 | | -const isCccRecord = process.argv.some((a) => a === "ccc:record"); |
30 | | -if (!isCccRecord && !existsSync(cccCache) && existsSync(cccRefs)) { |
31 | | - try { |
32 | | - execSync("bash ccc-dev/replay.sh", { |
33 | | - cwd: __dirname, |
34 | | - stdio: ["ignore", "pipe", "pipe"], |
35 | | - }); |
36 | | - } catch (err) { |
37 | | - process.stderr.write("Replaying CCC pins…\n"); |
38 | | - process.stderr.write(err.stdout?.toString() ?? ""); |
39 | | - process.stderr.write(err.stderr?.toString() ?? ""); |
40 | | - throw err; |
| 42 | +const isRecord = process.argv.some((a) => a === "fork:record"); |
| 43 | +if (!isRecord) { |
| 44 | + for (const fork of forkDirs) { |
| 45 | + const cloneDir = join(fork.dir, fork.config.cloneDir); |
| 46 | + const hasPins = existsSync(join(fork.dir, "pins", "manifest")); |
| 47 | + if (!existsSync(cloneDir) && hasPins) { |
| 48 | + try { |
| 49 | + execFileSync("bash", ["fork-scripts/replay.sh", fork.name], { |
| 50 | + cwd: __dirname, |
| 51 | + stdio: ["ignore", "pipe", "pipe"], |
| 52 | + }); |
| 53 | + } catch (err) { |
| 54 | + process.stderr.write(`Replaying ${fork.name} pins…\n`); |
| 55 | + process.stderr.write(err.stdout?.toString() ?? ""); |
| 56 | + process.stderr.write(err.stderr?.toString() ?? ""); |
| 57 | + throw err; |
| 58 | + } |
| 59 | + } |
41 | 60 | } |
42 | 61 | } |
43 | 62 |
|
44 | | -// 2. Discover local CCC packages and build the override map |
45 | | -const cccPkgs = join(cccCache, "packages"); |
| 63 | +// 2. Discover local fork packages and build the override map |
46 | 64 | const localOverrides = {}; |
47 | | -if (existsSync(cccPkgs)) { |
48 | | - for (const dir of readdirSync(cccPkgs, { withFileTypes: true })) { |
49 | | - if (!dir.isDirectory()) continue; |
50 | | - const pkgJsonPath = join(cccPkgs, dir.name, "package.json"); |
51 | | - if (!existsSync(pkgJsonPath)) continue; |
52 | | - const { name } = JSON.parse(readFileSync(pkgJsonPath, "utf8")); |
53 | | - if (name) { |
54 | | - localOverrides[name] = "workspace:*"; |
| 65 | +for (const fork of forkDirs) { |
| 66 | + const cloneDir = join(fork.dir, fork.config.cloneDir); |
| 67 | + if (!existsSync(cloneDir)) continue; |
| 68 | + const includes = fork.config.workspace?.include ?? []; |
| 69 | + const excludes = new Set(fork.config.workspace?.exclude ?? []); |
| 70 | + for (const pattern of includes) { |
| 71 | + // Simple glob: only supports trailing /* (e.g. "packages/*") |
| 72 | + const base = pattern.replace(/\/\*$/, ""); |
| 73 | + const pkgsRoot = join(cloneDir, base); |
| 74 | + if (!existsSync(pkgsRoot)) continue; |
| 75 | + for (const dir of readdirSync(pkgsRoot, { withFileTypes: true })) { |
| 76 | + if (!dir.isDirectory()) continue; |
| 77 | + const relPath = `${base}/${dir.name}`; |
| 78 | + if (excludes.has(relPath)) continue; |
| 79 | + const pkgJsonPath = join(pkgsRoot, dir.name, "package.json"); |
| 80 | + if (!existsSync(pkgJsonPath)) continue; |
| 81 | + const { name } = JSON.parse(readFileSync(pkgJsonPath, "utf8")); |
| 82 | + if (name) { |
| 83 | + localOverrides[name] = "workspace:*"; |
| 84 | + } |
55 | 85 | } |
56 | 86 | } |
57 | 87 | } |
58 | 88 |
|
59 | | -const hasCcc = Object.keys(localOverrides).length > 0; |
| 89 | +const hasOverrides = Object.keys(localOverrides).length > 0; |
60 | 90 |
|
61 | 91 | function readPackage(pkg) { |
62 | | - if (!hasCcc) return pkg; |
| 92 | + if (!hasOverrides) return pkg; |
63 | 93 |
|
64 | 94 | for (const field of [ |
65 | 95 | "dependencies", |
|
0 commit comments