From 0e73c593ffe585e56c2a480c7c7fc44202c9db1d Mon Sep 17 00:00:00 2001 From: bcoll Date: Mon, 21 Aug 2023 12:46:57 +0100 Subject: [PATCH] Allow `__STATIC_CONTENT_MANIFEST` to be imported from any directory Previously, `import manifest from "__STATIC_CONTENT_MANIFEST"` was only allowed in the "root" directory of the modules virtual file-system. This change adds stub modules for each additional module in each subdirectory, that re-export the module. This allows the manifest to be imported in any directory. --- packages/miniflare/src/plugins/core/index.ts | 33 +++++++++-- .../miniflare/src/runtime/config/workerd.ts | 2 +- .../miniflare/test/plugins/kv/sites.spec.ts | 56 +++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/packages/miniflare/src/plugins/core/index.ts b/packages/miniflare/src/plugins/core/index.ts index 08f7b572c..ac8209b35 100644 --- a/packages/miniflare/src/plugins/core/index.ts +++ b/packages/miniflare/src/plugins/core/index.ts @@ -1,6 +1,7 @@ import assert from "assert"; import { readFileSync } from "fs"; import fs from "fs/promises"; +import path from "path"; import tls from "tls"; import { TextEncoder } from "util"; import { bold } from "kleur/colors"; @@ -374,10 +375,7 @@ export const CORE_PLUGIN: Plugin< sourceMapRegistry, }) { // Define regular user worker - const additionalModuleNames = additionalModules.map(({ name }) => { - assert(name !== undefined); - return name; - }); + const additionalModuleNames = additionalModules.map(({ name }) => name); const workerScript = getWorkerScript( sourceMapRegistry, options, @@ -386,7 +384,32 @@ export const CORE_PLUGIN: Plugin< ); // Add additional modules (e.g. "__STATIC_CONTENT_MANIFEST") if any if ("modules" in workerScript) { - workerScript.modules.push(...additionalModules); + const subDirs = new Set( + workerScript.modules.map(({ name }) => path.posix.dirname(name)) + ); + // Ignore `.` as it's not a subdirectory, and we don't want to register + // additional modules in the root twice. + subDirs.delete("."); + + for (const module of additionalModules) { + workerScript.modules.push(module); + // In addition to adding the module, we add stub modules in each + // subdirectory re-exporting each additional module. These allow + // additional modules to be imported in every directory. + for (const subDir of subDirs) { + const relativePath = path.posix.relative(subDir, module.name); + const relativePathString = JSON.stringify(relativePath); + workerScript.modules.push({ + name: path.posix.join(subDir, module.name), + // TODO(someday): if we ever have additional modules without + // default exports, this may be a problem. For now, our only + // additional module is `__STATIC_CONTENT_MANIFEST` so it's fine. + // If needed, we could look for instances of `export default` or + // `as default` in the module's code as a heuristic. + esModule: `export * from ${relativePathString}; export { default } from ${relativePathString};`, + }); + } + } } const name = getUserServiceName(options.name); diff --git a/packages/miniflare/src/runtime/config/workerd.ts b/packages/miniflare/src/runtime/config/workerd.ts index 29b6245c4..c4b240ee5 100644 --- a/packages/miniflare/src/runtime/config/workerd.ts +++ b/packages/miniflare/src/runtime/config/workerd.ts @@ -67,7 +67,7 @@ export type Worker_DurableObjectStorage = | { localDisk?: string }; export type Worker_Module = { - name?: string; + name: string; } & ( | { esModule?: string } | { commonJsModule?: string } diff --git a/packages/miniflare/test/plugins/kv/sites.spec.ts b/packages/miniflare/test/plugins/kv/sites.spec.ts index 0d303b01c..995a9cc28 100644 --- a/packages/miniflare/test/plugins/kv/sites.spec.ts +++ b/packages/miniflare/test/plugins/kv/sites.spec.ts @@ -169,6 +169,62 @@ test("gets assets with module worker", async (t) => { t.is(await res.text(), "nested"); }); +test("can import manifest from any directory", async (t) => { + const tmp = await useTmp(t); + const testPath = path.join(tmp, "test.txt"); + await fs.writeFile(testPath, "test", "utf8"); + const mf = new Miniflare({ + modules: [ + { + type: "ESModule", + path: "index.mjs", + contents: ` + import root from "__STATIC_CONTENT_MANIFEST"; + import a1 from "./a/1.mjs"; + import ab2 from "./a/b/2.mjs"; + import ab3 from "./a/b/3.mjs"; + import abc4 from "./a/b/c/4.mjs"; + + export default { + async fetch() { + return Response.json({ + a1: a1 === root, + ab2: ab2 === root, + ab3: ab3 === root, + abc4: abc4 === root, + }); + } + } + `, + }, + { + type: "ESModule", + path: "a/1.mjs", + contents: 'export { default } from "__STATIC_CONTENT_MANIFEST";', + }, + { + type: "ESModule", + path: "a/b/2.mjs", + contents: 'export { default } from "__STATIC_CONTENT_MANIFEST";', + }, + { + type: "ESModule", + path: "a/b/3.mjs", + contents: 'export { default } from "__STATIC_CONTENT_MANIFEST";', + }, + { + type: "ESModule", + path: "a/b/c/4.mjs", + contents: 'export { default } from "__STATIC_CONTENT_MANIFEST";', + }, + ], + sitePath: tmp, + }); + t.teardown(() => mf.dispose()); + const res = await mf.dispatchFetch("http://localhost:8787/test.txt"); + t.deepEqual(await res.json(), { a1: true, ab2: true, ab3: true, abc4: true }); +}); + test("gets assets with percent-encoded paths", async (t) => { // https://github.com/cloudflare/miniflare/issues/326 const tmp = await useTmp(t);