singularity-forge/scripts/link-workspace-packages.cjs
TÂCHES 098ed35a50 fix: broken npm install — remove bundleDependencies, use postinstall symlinks (#369)
* fix: remove @gsd/* cross-deps that break npm install (#hotfix)

Workspace packages declared @gsd/* as dependencies in their own
package.json files. npm's bundleDependencies bundles packages into
node_modules/ but still tries to resolve sub-dependencies from the
registry — causing 404s for the unpublished @gsd/* scope.

- Remove @gsd/* from all dependencies (root and workspace packages)
- Add validate-pack.sh: tests tarball installability before publish
- Wire validate-pack into CI (every PR) and publish pipeline
- Bump to v2.10.10
- Update changelog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: drop bundleDependencies, use postinstall symlinks instead

bundleDependencies with workspace packages causes npm to resolve
@gsd/* from the registry during install — 404 since they're not
published. Replace with a postinstall script that creates
node_modules/@gsd/* symlinks pointing to packages/*.

- Remove @gsd/* from dependencies and bundleDependencies
- Add link-workspace-packages.cjs (CJS, runs before ESM postinstall)
- Update validate-pack to verify symlinks after install
- Include link script in files array

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: robust validate-pack + fallback workspace linking

- Keep @gsd/* in bundleDependencies (for npm pack bundling)
- Remove @gsd/* from root dependencies (prevents 404 registry lookups)
- Add link-workspace-packages.cjs fallback for when bundled symlinks
  aren't created
- Simplified validate-pack with better error diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove bundleDependencies — use postinstall symlinks only

npm 10.x fetches packument metadata for ALL deps including bundled ones.
@gsd/* packages don't exist on npm → 404 → hard install failure.

bundleDependencies is fundamentally broken for unpublished workspace
packages. Replace with:
- packages/ shipped via files array (already was)
- link-workspace-packages.cjs creates node_modules/@gsd/* symlinks in
  postinstall, pointing to packages/*
- No @gsd/* in dependencies or bundleDependencies at all

Tarball drops from 40M to 3M (no bundled node_modules).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add .npmignore to prevent .gitignore from excluding dist/

.gitignore contains /dist/ and packages/*/dist/ which are needed in
the published tarball. Without .npmignore, npm pack respects .gitignore
and excludes them — even though "files" in package.json should override.

An empty .npmignore causes npm to ignore .gitignore entirely, letting
the "files" field control what's packed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: avoid SIGPIPE in validate-pack on Linux

tar | grep -q causes SIGPIPE (exit 141) on Linux when grep closes the
pipe early. Write tar listing to a temp file and grep that instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:04:12 -06:00

70 lines
2.1 KiB
JavaScript

#!/usr/bin/env node
/**
* link-workspace-packages.cjs
*
* Creates node_modules/@gsd/* symlinks pointing to packages/* directories.
*
* During development, npm workspaces creates these automatically. But in the
* published tarball, workspace packages are shipped under packages/ (via the
* "files" field) and the @gsd/* imports in compiled code need node_modules/@gsd/*
* to resolve. This script bridges the gap.
*
* Runs as part of postinstall (before any ESM code that imports @gsd/*).
*/
const { existsSync, mkdirSync, symlinkSync, lstatSync, readlinkSync, unlinkSync, readdirSync } = require('fs')
const { resolve, join } = require('path')
const root = resolve(__dirname, '..')
const packagesDir = join(root, 'packages')
const nodeModulesGsd = join(root, 'node_modules', '@gsd')
// Map directory names to package names
const packageMap = {
'native': 'native',
'pi-agent-core': 'pi-agent-core',
'pi-ai': 'pi-ai',
'pi-coding-agent': 'pi-coding-agent',
'pi-tui': 'pi-tui',
}
// Ensure @gsd scope directory exists
if (!existsSync(nodeModulesGsd)) {
mkdirSync(nodeModulesGsd, { recursive: true })
}
let linked = 0
for (const [dir, name] of Object.entries(packageMap)) {
const source = join(packagesDir, dir)
const target = join(nodeModulesGsd, name)
if (!existsSync(source)) continue
// Skip if already correctly linked or is a real directory (bundled)
if (existsSync(target)) {
try {
const stat = lstatSync(target)
if (stat.isSymbolicLink()) {
const linkTarget = readlinkSync(target)
if (resolve(join(nodeModulesGsd, linkTarget)) === source || linkTarget === source) {
continue // Already correct
}
unlinkSync(target) // Wrong target, relink
} else {
continue // Real directory (e.g., from bundleDependencies), don't touch
}
} catch {
continue
}
}
try {
symlinkSync(source, target, 'junction') // junction works on Windows too
linked++
} catch {
// Non-fatal — may fail in read-only environments
}
}
if (linked > 0) {
process.stderr.write(` Linked ${linked} workspace packages\n`)
}