Skip to content

Commit 0170977

Browse files
committed
Add canary runtime-resolution smoke to catch resolver-escape regressions
1 parent 00f3f8f commit 0170977

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"test:install": "playwright install",
5050
"test:banner": "node scripts/test-browser-banner.mjs",
5151
"test:canary-one-run": "node scripts/assert-canary-one-run-builds.mjs",
52+
"test:canary-runtime-installed": "node scripts/assert-canary-runtime-installed.mjs",
5253
"test:optional-deps-contract": "node scripts/assert-optional-deps-canary-matrix.mjs",
5354
"test:stable-install-modes": "node scripts/assert-stable-install-modes.mjs",
5455
"test:create-dev-workflow": "node scripts/assert-create-dev-workflow.mjs",
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env node
2+
// Pins the contract that the installed `extension` canary resolves its
3+
// `extension-develop` runtime from this workspace's own node_modules,
4+
// never from a surrounding monorepo's programs/develop. Captures the
5+
// resolver-escape regression where the workspace walker climbed past
6+
// node_modules into an outer extension.js checkout and either threw
7+
// "Local extension-develop runtime is not built" or silently loaded the
8+
// wrong runtime.
9+
10+
import fs from 'node:fs'
11+
import os from 'node:os'
12+
import path from 'node:path'
13+
import {spawn} from 'node:child_process'
14+
import {fileURLToPath} from 'node:url'
15+
16+
const __filename = fileURLToPath(import.meta.url)
17+
const __dirname = path.dirname(__filename)
18+
const repoRoot = path.resolve(__dirname, '..')
19+
const probePath = path.join(__dirname, 'probe-extension-develop-resolve.cjs')
20+
21+
const installedExtensionBin = path.join(
22+
repoRoot,
23+
'node_modules',
24+
'extension',
25+
'bin',
26+
'extension.cjs'
27+
)
28+
const installedExtensionDevelopRoot = path.join(
29+
repoRoot,
30+
'node_modules',
31+
'extension-develop'
32+
)
33+
34+
if (!fs.existsSync(installedExtensionBin)) {
35+
console.error(`✖ Canary not installed: missing ${installedExtensionBin}`)
36+
console.error(' Run `pnpm install` in _FUTURE/examples first.')
37+
process.exit(1)
38+
}
39+
40+
if (!fs.existsSync(installedExtensionDevelopRoot)) {
41+
console.error(`✖ Missing installed runtime: ${installedExtensionDevelopRoot}`)
42+
process.exit(1)
43+
}
44+
45+
const fakeProject = fs.mkdtempSync(
46+
path.join(os.tmpdir(), 'extjs-resolve-probe-')
47+
)
48+
fs.writeFileSync(
49+
path.join(fakeProject, 'manifest.json'),
50+
JSON.stringify(
51+
{manifest_version: 3, name: 'probe', version: '0.0.1'},
52+
null,
53+
2
54+
)
55+
)
56+
57+
const result = await new Promise((resolve) => {
58+
const child = spawn(
59+
process.execPath,
60+
[installedExtensionBin, 'dev', fakeProject, '--no-telemetry'],
61+
{
62+
cwd: repoRoot,
63+
env: {
64+
...process.env,
65+
NODE_OPTIONS:
66+
`${process.env.NODE_OPTIONS ?? ''} --require ${probePath}`.trim(),
67+
// Strip any stale env override that could mask resolver behavior.
68+
EXTENSION_DEVELOP_ROOT: ''
69+
},
70+
stdio: ['ignore', 'pipe', 'pipe']
71+
}
72+
)
73+
74+
let stdout = ''
75+
let stderr = ''
76+
child.stdout.on('data', (d) => (stdout += d.toString()))
77+
child.stderr.on('data', (d) => (stderr += d.toString()))
78+
79+
const killTimer = setTimeout(() => {
80+
child.kill('SIGKILL')
81+
}, 60_000)
82+
83+
child.on('close', (code) => {
84+
clearTimeout(killTimer)
85+
resolve({code: code ?? 1, stdout, stderr})
86+
})
87+
child.on('error', (err) => {
88+
clearTimeout(killTimer)
89+
resolve({code: 1, stdout, stderr: String(err?.message || err)})
90+
})
91+
})
92+
93+
fs.rmSync(fakeProject, {recursive: true, force: true})
94+
95+
const match = result.stderr.match(/__EXT_DEV_RESOLVED__::(.+)$/m)
96+
97+
if (!match) {
98+
if (
99+
/Local extension-develop runtime is not built/.test(
100+
`${result.stdout}\n${result.stderr}`
101+
)
102+
) {
103+
console.error(result.stdout)
104+
console.error(result.stderr)
105+
console.error(
106+
'✖ Resolver-escape regression: the canary CLI walked into an outer ' +
107+
'workspace and demanded its programs/develop be compiled. The ' +
108+
'walker must bail when startDir is inside node_modules.'
109+
)
110+
process.exit(1)
111+
}
112+
113+
console.error(result.stdout)
114+
console.error(result.stderr)
115+
console.error(
116+
'✖ Probe never observed extension-develop being loaded ' +
117+
`(exit ${result.code})`
118+
)
119+
process.exit(1)
120+
}
121+
122+
const resolved = match[1].trim()
123+
const expectedPrefix = installedExtensionDevelopRoot + path.sep
124+
125+
if (!resolved.startsWith(expectedPrefix)) {
126+
console.error(
127+
'✖ extension-develop resolved outside the local install:\n' +
128+
` expected prefix: ${expectedPrefix}\n` +
129+
` resolved: ${resolved}`
130+
)
131+
process.exit(1)
132+
}
133+
134+
console.log(`✔ extension-develop resolved to ${resolved}`)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict'
2+
3+
// Loaded via `node --require` to observe which `extension-develop/dist/module.*`
4+
// the canary CLI actually loads. Prints a single marker line to stderr and
5+
// exits cleanly so we don't drag the dev server into a real run.
6+
7+
const Module = require('module')
8+
9+
// Matches both shapes the resolver can target: the installed runtime under
10+
// `node_modules/extension-develop/dist/module.*` and the workspace runtime
11+
// under `programs/develop/dist/module.*`. The assertion downstream verifies
12+
// which of the two actually got loaded.
13+
const PATTERN =
14+
/(?:[\\/]extension-develop[\\/]|[\\/]programs[\\/]develop[\\/])dist[\\/]module\.[cm]?js$/
15+
const origLoad = Module._load
16+
17+
let observed = false
18+
Module._load = function patchedLoad(request, parent, isMain) {
19+
const result = origLoad.call(this, request, parent, isMain)
20+
21+
if (!observed && typeof request === 'string' && PATTERN.test(request)) {
22+
observed = true
23+
const line = `__EXT_DEV_RESOLVED__::${request}\n`
24+
if (process.stderr.write(line)) process.exit(0)
25+
else process.stderr.once('drain', () => process.exit(0))
26+
}
27+
28+
return result
29+
}

0 commit comments

Comments
 (0)