mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(webworker): retain createRequire dependencies
This commit is contained in:
@@ -37,7 +37,9 @@ const repoRoot = fileURLToPath(new URL('../../../../', import.meta.url))
|
||||
const SUBJECT = '@deepseek-ai/dsh-timeout'
|
||||
const LANDLOCK = '@deepseek-ai/node-addon-landlock-run'
|
||||
const PLUGIN_INVENTORY = '@deepseek-ai/dsh-plugin-package-inventory-deepseek'
|
||||
const TERMINAL_BASH = '@deepseek-ai/dsh-terminal-bash'
|
||||
const WEB_SERVER = '@deepseek-ai/dsh-host-webserver'
|
||||
const XTERM_HEADLESS = '@xterm/headless'
|
||||
|
||||
const workspaces = indexWorkspacePackages(repoRoot)
|
||||
|
||||
@@ -114,6 +116,15 @@ const packedWebServer = (): ReturnType<typeof packVfsImage> => webServerMemo ??=
|
||||
entries: [],
|
||||
})
|
||||
|
||||
let terminalBashMemo: ReturnType<typeof packVfsImage> | undefined
|
||||
const packedTerminalBash = (): ReturnType<typeof packVfsImage> => terminalBashMemo ??= packVfsImage({
|
||||
config: `- id: subject\n name: '${TERMINAL_BASH}'\n`,
|
||||
profile: 'terminal-bash-dependency-check',
|
||||
workspaces,
|
||||
resolveFrom: repoRoot,
|
||||
entries: [],
|
||||
})
|
||||
|
||||
/** The image's archive, inflated once: mounting reads the tar, not the gzip member. */
|
||||
let archiveMemo: Uint8Array | undefined
|
||||
const archive = async (): Promise<Uint8Array> =>
|
||||
@@ -218,6 +229,27 @@ const archive = async (): Promise<Uint8Array> =>
|
||||
expect(typeof webserver.WebServer).toBe('function')
|
||||
})
|
||||
|
||||
it('loads a third-party dependency requested through createRequire', async () => {
|
||||
const result = packedTerminalBash()
|
||||
expect(result.missing).toEqual([])
|
||||
expect(result.packages.get(XTERM_HEADLESS)).toBeGreaterThan(0)
|
||||
expect(Object.hasOwn(result.files, `node_modules/${XTERM_HEADLESS}/lib-headless/xterm-headless.js`)).toBe(true)
|
||||
|
||||
const vfs = loadVfsImage(await inflateImage(result.image, 'the packed terminal backend'), DEFAULT_ROOT)
|
||||
const loader = new WorkerModuleLoader({
|
||||
vfs,
|
||||
root: DEFAULT_ROOT,
|
||||
staticModules: createNodeBuiltins(),
|
||||
staticModulePrefixes: REPLACED_PREFIXES,
|
||||
})
|
||||
setActiveVfs(vfs)
|
||||
setActiveModuleLoader(loader)
|
||||
const terminal = loader.requireFrom(`${DEFAULT_ROOT}/workspace`)(TERMINAL_BASH) as {
|
||||
BashTerminalBackend?: unknown
|
||||
}
|
||||
expect(typeof terminal.BashTerminalBackend).toBe('function')
|
||||
})
|
||||
|
||||
it('runs the unchanged Landlock entry package over the Worker platform executable', async () => {
|
||||
const result = packedLandlock()
|
||||
expect(workspaces.has(LANDLOCK)).toBe(true)
|
||||
|
||||
@@ -70,6 +70,8 @@ class Transformer {
|
||||
private moduleSyntax = false
|
||||
private readonly moduleRequests = new Set<string>()
|
||||
private readonly metaResolveRequests = new Set<string>()
|
||||
private readonly createRequireBindings = new Set<string>()
|
||||
private readonly requireBindings = new Set<string>(['require'])
|
||||
|
||||
constructor(source: string, private readonly path: string) {
|
||||
// A `#!` line is only legal at offset zero, and the prologue takes that spot;
|
||||
@@ -154,7 +156,14 @@ class Transformer {
|
||||
if (Array.isArray(node.attributes) && node.attributes.length > 0) {
|
||||
this.fail('import attributes are not supported', node.start)
|
||||
}
|
||||
const request = `require(${this.literal(node.source as Node)})`
|
||||
const source = node.source as Node
|
||||
if (source.value === 'node:module' || source.value === 'module') {
|
||||
for (const specifier of node.specifiers as Node[]) {
|
||||
if (specifier.type !== 'ImportSpecifier' || nameOf(specifier.imported as Node) !== 'createRequire') continue
|
||||
this.createRequireBindings.add(nameOf(specifier.local as Node))
|
||||
}
|
||||
}
|
||||
const request = `require(${this.literal(source)})`
|
||||
const specifiers = node.specifiers as Node[]
|
||||
if (specifiers.length === 0) {
|
||||
this.replace(node.start, node.end, `${request};`)
|
||||
@@ -337,12 +346,21 @@ class Transformer {
|
||||
if (argument !== undefined && typeof argument.value === 'string') this.moduleRequests.add(argument.value)
|
||||
break
|
||||
}
|
||||
case 'VariableDeclarator': {
|
||||
const id = record.id as Node
|
||||
const init = record.init as Node | null
|
||||
if (id.type === 'Identifier' && init !== null && this.isCreateRequireCall(init)) {
|
||||
this.requireBindings.add(nameOf(id))
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'CallExpression': {
|
||||
// CommonJS bodies pass through untransformed, but their literal
|
||||
// `require()` calls are module requests all the same.
|
||||
// CommonJS bodies pass through untransformed, but literal calls through
|
||||
// the wrapper's `require` or Node's `createRequire` are module requests
|
||||
// all the same.
|
||||
const callee = record.callee as Node
|
||||
const callArguments = record.arguments as Node[]
|
||||
if (callee.type === 'Identifier' && callee.name === 'require' && callArguments.length === 1
|
||||
if (this.isRequireCall(callee) && callArguments.length === 1
|
||||
&& typeof callArguments[0]?.value === 'string') {
|
||||
this.moduleRequests.add(callArguments[0].value)
|
||||
}
|
||||
@@ -410,6 +428,17 @@ class Transformer {
|
||||
}
|
||||
}
|
||||
|
||||
private isCreateRequireCall(node: Node): boolean {
|
||||
if (node.type !== 'CallExpression') return false
|
||||
const callee = node.callee as Node
|
||||
return callee.type === 'Identifier' && this.createRequireBindings.has(nameOf(callee))
|
||||
}
|
||||
|
||||
private isRequireCall(callee: Node): boolean {
|
||||
return (callee.type === 'Identifier' && this.requireBindings.has(nameOf(callee)))
|
||||
|| this.isCreateRequireCall(callee)
|
||||
}
|
||||
|
||||
run(): string {
|
||||
// Transforming a lowered body again would nest the protocol inside itself:
|
||||
// it still runs, only slower and unreadable, so a mis-wired manifest must
|
||||
@@ -539,8 +568,9 @@ export interface LoweredModule {
|
||||
readonly lowered: boolean
|
||||
/**
|
||||
* Static module requests the body makes: import and re-export sources,
|
||||
* literal dynamic imports, and literal `require()` calls. Computed requests
|
||||
* are absent — they resolve (and fail loud) at runtime only.
|
||||
* literal dynamic imports, and literal calls through `require` or an imported
|
||||
* `createRequire`. Computed requests are absent — they resolve (and fail loud)
|
||||
* at runtime only.
|
||||
*/
|
||||
readonly moduleRequests: readonly string[]
|
||||
/**
|
||||
|
||||
@@ -144,6 +144,20 @@ check(
|
||||
check('lowered mirrors code !== source', cjsAwait.lowered, cjsAwait.code !== 'module.exports = async () => { await 1 }\n')
|
||||
}
|
||||
|
||||
{
|
||||
const direct = lowerModuleSource({
|
||||
filename: 'node_modules/p/direct.js',
|
||||
source: "import { createRequire } from 'node:module'\ncreateRequire(import.meta.url)('@xterm/headless')\n",
|
||||
})
|
||||
check('literal createRequire call is a module request', direct.moduleRequests, ['node:module', '@xterm/headless'])
|
||||
|
||||
const assigned = lowerModuleSource({
|
||||
filename: 'node_modules/p/assigned.js',
|
||||
source: "import { createRequire as makeRequire } from 'node:module'\nconst localRequire = makeRequire(import.meta.url)\nlocalRequire('p')\n",
|
||||
})
|
||||
check('literal call through a createRequire binding is a module request', assigned.moduleRequests, ['node:module', 'p'])
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. Import forms.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user