chore: import upstream snapshot with attribution
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@fixture/additional-modules",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "wrangler deploy --dry-run --outdir=dist",
|
||||
"check:type": "tsc",
|
||||
"deploy": "wrangler deploy",
|
||||
"start": "wrangler dev",
|
||||
"test:ci": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"type:tests": "tsc -p ./test/tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-tsconfig": "workspace:*",
|
||||
"@cloudflare/workers-types": "catalog:default",
|
||||
"@fixture/shared": "workspace:*",
|
||||
"typescript": "catalog:default",
|
||||
"undici": "catalog:default",
|
||||
"vitest": "catalog:default",
|
||||
"wrangler": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = "common";
|
||||
@@ -0,0 +1 @@
|
||||
export default "bundled";
|
||||
@@ -0,0 +1 @@
|
||||
export default "dynamic";
|
||||
@@ -0,0 +1 @@
|
||||
SELECT * FROM users;
|
||||
@@ -0,0 +1,32 @@
|
||||
import common from "./common.cjs";
|
||||
import dep from "./dep";
|
||||
import sql from "./example.sql";
|
||||
import text from "./text.txt";
|
||||
|
||||
export default <ExportedHandler>{
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/dep") {
|
||||
return new Response(dep);
|
||||
}
|
||||
if (url.pathname === "/text") {
|
||||
return new Response(text);
|
||||
}
|
||||
if (url.pathname === "/sql") {
|
||||
return new Response(sql);
|
||||
}
|
||||
if (url.pathname === "/common") {
|
||||
return new Response(common);
|
||||
}
|
||||
if (url.pathname === "/dynamic") {
|
||||
return new Response((await import("./dynamic.js")).default);
|
||||
}
|
||||
if (url.pathname.startsWith("/lang/")) {
|
||||
// Build the path dynamically to ensure esbuild doesn't inline the import.
|
||||
const language =
|
||||
"./lang/" + url.pathname.substring("/lang/".length) + ".js";
|
||||
return new Response((await import(language)).default.hello);
|
||||
}
|
||||
return new Response("Not Found", { status: 404 });
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export default { hello: "hello" };
|
||||
@@ -0,0 +1 @@
|
||||
export default { hello: "bonjour" };
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
declare module "*.txt" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module "*.sql" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -0,0 +1,216 @@
|
||||
import childProcess from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { removeDir } from "@fixture/shared/src/fs-helpers";
|
||||
import { afterAll, assert, beforeAll, describe, test, vi } from "vitest";
|
||||
import { unstable_startWorker } from "wrangler";
|
||||
import { wranglerEntryPath } from "../../shared/src/run-wrangler-long-lived";
|
||||
|
||||
async function getTmpDir() {
|
||||
return fs.mkdtemp(path.join(os.tmpdir(), "wrangler-modules-"));
|
||||
}
|
||||
|
||||
type WranglerDev = Awaited<ReturnType<typeof unstable_startWorker>>;
|
||||
function get(worker: WranglerDev, pathname: string) {
|
||||
const url = `http://example.com${pathname}`;
|
||||
// Disable Miniflare's pretty error page, so we can parse errors as JSON
|
||||
return worker.fetch(url, { headers: { "MF-Disable-Pretty-Error": "true" } });
|
||||
}
|
||||
|
||||
describe("find_additional_modules dev", () => {
|
||||
let tmpDir: string;
|
||||
let worker: WranglerDev;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Copy over files to a temporary directory as we'll be modifying them
|
||||
tmpDir = await getTmpDir();
|
||||
await fs.cp(
|
||||
path.resolve(__dirname, "..", "src"),
|
||||
path.join(tmpDir, "src"),
|
||||
{ recursive: true }
|
||||
);
|
||||
await fs.cp(
|
||||
path.resolve(__dirname, "..", "wrangler.jsonc"),
|
||||
path.join(tmpDir, "wrangler.jsonc")
|
||||
);
|
||||
|
||||
worker = await unstable_startWorker({
|
||||
config: path.join(tmpDir, "wrangler.jsonc"),
|
||||
});
|
||||
});
|
||||
afterAll(async () => {
|
||||
await worker.dispose();
|
||||
removeDir(tmpDir, { fireAndForget: true });
|
||||
});
|
||||
|
||||
test("supports bundled modules", async ({ expect }) => {
|
||||
const res = await get(worker, "/dep");
|
||||
expect(await res.text()).toBe("bundled");
|
||||
});
|
||||
test("supports text modules", async ({ expect }) => {
|
||||
const res = await get(worker, "/text");
|
||||
expect(await res.text()).toBe("test\n");
|
||||
});
|
||||
test("supports SQL modules", async ({ expect }) => {
|
||||
const res = await get(worker, "/sql");
|
||||
expect(await res.text()).toBe("SELECT * FROM users;\n");
|
||||
});
|
||||
test("supports dynamic imports", async ({ expect }) => {
|
||||
const res = await get(worker, "/dynamic");
|
||||
expect(await res.text()).toBe("dynamic");
|
||||
});
|
||||
test("supports commonjs lazy imports", async ({ expect }) => {
|
||||
const res = await get(worker, "/common");
|
||||
expect(await res.text()).toBe("common");
|
||||
});
|
||||
test("supports variable dynamic imports", async ({ expect }) => {
|
||||
const res = await get(worker, "/lang/en");
|
||||
expect(await res.text()).toBe("hello");
|
||||
});
|
||||
|
||||
test("watches additional modules", async ({ expect }) => {
|
||||
const srcDir = path.join(tmpDir, "src");
|
||||
|
||||
// Update dynamically imported file
|
||||
await fs.writeFile(
|
||||
path.join(srcDir, "dynamic.js"),
|
||||
'export default "new dynamic";'
|
||||
);
|
||||
await vi.waitFor(async () => {
|
||||
const res = await get(worker, "/dynamic");
|
||||
assert.strictEqual(await res.text(), "new dynamic");
|
||||
});
|
||||
|
||||
// Delete dynamically imported file
|
||||
await fs.rm(path.join(srcDir, "lang", "en.js"));
|
||||
|
||||
await vi.waitFor(async () => {
|
||||
await expect(get(worker, "/lang/en")).rejects.toThrow(
|
||||
'No such module "lang/en.js".'
|
||||
);
|
||||
});
|
||||
|
||||
// Create new dynamically imported file in new directory
|
||||
await fs.mkdir(path.join(srcDir, "lang", "en"));
|
||||
await fs.writeFile(
|
||||
path.join(srcDir, "lang", "en", "us.js"),
|
||||
'export default { hello: "hey" };'
|
||||
);
|
||||
await vi.waitFor(async () => {
|
||||
const res = await get(worker, "/lang/en/us");
|
||||
assert.strictEqual(await res.text(), "hey");
|
||||
});
|
||||
|
||||
// Update newly created file
|
||||
await fs.writeFile(
|
||||
path.join(srcDir, "lang", "en", "us.js"),
|
||||
'export default { hello: "bye" };'
|
||||
);
|
||||
await vi.waitFor(async () => {
|
||||
const res = await get(worker, "/lang/en/us");
|
||||
assert.strictEqual(await res.text(), "bye");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function build(cwd: string, outDir: string) {
|
||||
return childProcess.spawnSync(
|
||||
process.execPath,
|
||||
[wranglerEntryPath, "deploy", "--dry-run", `--outdir=${outDir}`],
|
||||
{ cwd }
|
||||
);
|
||||
}
|
||||
|
||||
describe("find_additional_modules deploy", () => {
|
||||
let tmpDir: string;
|
||||
beforeAll(async () => {
|
||||
tmpDir = await getTmpDir();
|
||||
});
|
||||
afterAll(async () => await removeDir(tmpDir, { fireAndForget: true }));
|
||||
|
||||
test("doesn't bundle additional modules", async ({ expect }) => {
|
||||
const outDir = path.join(tmpDir, "out");
|
||||
const result = await build(path.resolve(__dirname, ".."), outDir);
|
||||
expect(result.status).toBe(0);
|
||||
|
||||
// Check additional modules marked external, but other dependencies bundled
|
||||
const bundledEntryPath = path.join(outDir, "index.js");
|
||||
const bundledEntry = await fs.readFile(bundledEntryPath, "utf8");
|
||||
expect(bundledEntry).toMatchInlineSnapshot(`
|
||||
"// src/index.ts
|
||||
import common from "./common.cjs";
|
||||
|
||||
// src/dep.ts
|
||||
var dep_default = "bundled";
|
||||
|
||||
// src/index.ts
|
||||
import sql from "./example.sql";
|
||||
import text from "./text.txt";
|
||||
var index_default = {
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/dep") {
|
||||
return new Response(dep_default);
|
||||
}
|
||||
if (url.pathname === "/text") {
|
||||
return new Response(text);
|
||||
}
|
||||
if (url.pathname === "/sql") {
|
||||
return new Response(sql);
|
||||
}
|
||||
if (url.pathname === "/common") {
|
||||
return new Response(common);
|
||||
}
|
||||
if (url.pathname === "/dynamic") {
|
||||
return new Response((await import("./dynamic.js")).default);
|
||||
}
|
||||
if (url.pathname.startsWith("/lang/")) {
|
||||
const language = "./lang/" + url.pathname.substring("/lang/".length) + ".js";
|
||||
return new Response((await import(language)).default.hello);
|
||||
}
|
||||
return new Response("Not Found", { status: 404 });
|
||||
}
|
||||
};
|
||||
export {
|
||||
index_default as default
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
"
|
||||
`);
|
||||
|
||||
// Check additional modules included in output
|
||||
expect(existsSync(path.join(outDir, "text.txt"))).toBe(true);
|
||||
expect(existsSync(path.join(outDir, "dynamic.js"))).toBe(true);
|
||||
expect(existsSync(path.join(outDir, "lang", "en.js"))).toBe(true);
|
||||
expect(existsSync(path.join(outDir, "lang", "fr.js"))).toBe(true);
|
||||
});
|
||||
|
||||
test("fails with service worker entrypoint", async ({ expect }) => {
|
||||
// Write basic service worker with `find_additional_modules` enabled
|
||||
const serviceWorkerDir = path.join(tmpDir, "service-worker");
|
||||
await fs.mkdir(serviceWorkerDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(serviceWorkerDir, "index.js"),
|
||||
"addEventListener('fetch', (e) => e.respondWith(new Response()))"
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.join(serviceWorkerDir, "wrangler.toml"),
|
||||
[
|
||||
'name="service-worker-test"',
|
||||
'main = "index.js"',
|
||||
'compatibility_date = "2023-08-01"',
|
||||
"find_additional_modules = true",
|
||||
].join("\n")
|
||||
);
|
||||
|
||||
// Try build, and check fails
|
||||
const serviceWorkerOutDir = path.join(tmpDir, "service-worker-out");
|
||||
const result = await build(serviceWorkerDir, serviceWorkerOutDir);
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stderr.toString()).toContain(
|
||||
"`find_additional_modules` can only be used with an ES module entrypoint."
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "@cloudflare/workers-tsconfig/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"lib": ["esnext"],
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"types": ["@cloudflare/workers-types/experimental"],
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://turbo.build/schema.json",
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineProject, mergeConfig } from "vitest/config";
|
||||
import configShared from "../../vitest.shared";
|
||||
|
||||
export default mergeConfig(
|
||||
configShared,
|
||||
defineProject({
|
||||
test: {},
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "additional-modules",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2023-08-01",
|
||||
"find_additional_modules": true,
|
||||
"rules": [
|
||||
{
|
||||
"type": "CommonJS",
|
||||
"globs": ["**/*.cjs"],
|
||||
},
|
||||
{
|
||||
"type": "ESModule",
|
||||
"globs": ["**/*.js"],
|
||||
},
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user