From d4b126862ea8778a5a8a4383d3d350bf2326fb58 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Tue, 21 Jul 2026 18:11:24 +0800 Subject: [PATCH] fix(ci): preserve pnpm links in packed probes --- scripts/verify-built-package-invariants.mjs | 86 ++++++++++----------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/scripts/verify-built-package-invariants.mjs b/scripts/verify-built-package-invariants.mjs index 26da80599c..4b298946d1 100644 --- a/scripts/verify-built-package-invariants.mjs +++ b/scripts/verify-built-package-invariants.mjs @@ -3,15 +3,12 @@ import { spawnSync } from 'node:child_process' import { copyFileSync, - existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, rmSync, - symlinkSync, } from 'node:fs' -import { tmpdir } from 'node:os' import { dirname, resolve } from 'node:path' import { pathToFileURL } from 'node:url' @@ -19,7 +16,6 @@ const root = resolve(import.meta.dirname, '..') const loaderUrl = pathToFileURL(resolve(root, 'vendor/loader/lib/index.js')).href const failures = [] const manifests = globSync('packages/*/*/package.json', { cwd: root }).sort() -const stagingRoot = mkdtempSync(resolve(tmpdir(), 'dsh-built-package-invariants-')) const packArgs = ['pack', '--dry-run', '--json', '--ignore-scripts'] // Windows cannot spawn npm's .cmd shim directly; setup-node installs this JS // entrypoint beside node.exe, so the probe stays shell-free on every runner. @@ -27,38 +23,41 @@ const npmInvocation = process.platform === 'win32' ? [process.execPath, [resolve(dirname(process.execPath), 'node_modules/npm/bin/npm-cli.js'), ...packArgs]] : ['npm', packArgs] -try { - for (const [index, manifestPath] of manifests.entries()) { - const packageDir = dirname(resolve(root, manifestPath)) - const manifest = JSON.parse(readFileSync(resolve(root, manifestPath), 'utf8')) - const packageName = manifest.name - if (typeof packageName !== 'string' || packageName.length === 0) { - failures.push(`${manifestPath}: missing package name`) - continue - } +for (const manifestPath of manifests) { + const packageDir = dirname(resolve(root, manifestPath)) + const manifest = JSON.parse(readFileSync(resolve(root, manifestPath), 'utf8')) + const packageName = manifest.name + if (typeof packageName !== 'string' || packageName.length === 0) { + failures.push(`${manifestPath}: missing package name`) + continue + } - const pack = spawnSync(npmInvocation[0], npmInvocation[1], { - cwd: packageDir, - encoding: 'utf8', - }) - if (pack.status !== 0) { - const detail = pack.error?.message - ?? (pack.stderr.trim() || pack.stdout.trim() || `npm pack exited ${pack.status}`) - failures.push(`${packageName}: ${detail}`) - continue - } + const pack = spawnSync(npmInvocation[0], npmInvocation[1], { + cwd: packageDir, + encoding: 'utf8', + }) + if (pack.status !== 0) { + const detail = pack.error?.message + ?? (pack.stderr.trim() || pack.stdout.trim() || `npm pack exited ${pack.status}`) + failures.push(`${packageName}: ${detail}`) + continue + } - let files - try { - const result = JSON.parse(pack.stdout) - files = result[0]?.files - if (!Array.isArray(files)) throw new Error('npm pack returned no file inventory') - } catch (error) { - failures.push(`${packageName}: cannot parse npm pack inventory: ${String(error)}`) - continue - } + let files + try { + const result = JSON.parse(pack.stdout) + files = result[0]?.files + if (!Array.isArray(files)) throw new Error('npm pack returned no file inventory') + } catch (error) { + failures.push(`${packageName}: cannot parse npm pack inventory: ${String(error)}`) + continue + } - const stagedPackageDir = resolve(stagingRoot, String(index)) + // Keep the packed view below its owning package so Node reaches the real + // pnpm dependency links. Junctioning node_modules elsewhere breaks pnpm's + // relative workspace links on Windows. + const stagedPackageDir = mkdtempSync(resolve(packageDir, '.dsh-packed-invariant-')) + try { for (const file of files) { if (typeof file.path !== 'string' || (file.path !== 'package.json' && !file.path.startsWith('lib/'))) continue @@ -66,14 +65,6 @@ try { mkdirSync(dirname(target), { recursive: true }) copyFileSync(resolve(packageDir, file.path), target) } - const packageNodeModules = resolve(packageDir, 'node_modules') - if (existsSync(packageNodeModules)) { - symlinkSync( - packageNodeModules, - resolve(stagedPackageDir, 'node_modules'), - process.platform === 'win32' ? 'junction' : 'dir', - ) - } const probe = ` const companion = await import(${JSON.stringify(`${packageName}/invariant`)}); @@ -92,13 +83,14 @@ try { cwd: stagedPackageDir, encoding: 'utf8', }) - if (result.status === 0) continue - const detail = result.error?.message - ?? (result.stderr.trim() || result.stdout.trim() || `node exited ${result.status}`) - failures.push(`${packageName}: ${detail}`) + if (result.status !== 0) { + const detail = result.error?.message + ?? (result.stderr.trim() || result.stdout.trim() || `node exited ${result.status}`) + failures.push(`${packageName}: ${detail}`) + } + } finally { + rmSync(stagedPackageDir, { recursive: true, force: true }) } -} finally { - rmSync(stagingRoot, { recursive: true, force: true }) } if (failures.length > 0) {