Skip to content

Commit

Permalink
fix: module support in service-worker format workers
Browse files Browse the repository at this point in the history
This rewrites how we pass configuration to miniflare in `wrangler dev`'s local mode. Instead of passing the entire configuration as cli args, we now generate a `wrangler.toml` based on our generated/inferred configuration, and pass that to miniflare instead. This solves a couple of issues, notably -

- `text_blobs` now works in local mode+service-worker format
- `Text` modules now work in local mode+service-worker format
- We properly throw errors for `Data` module in service-worker format

Along with cloudflare/miniflare#205, this fixes #416.
  • Loading branch information
threepointone committed Mar 9, 2022
1 parent 835c3ae commit 1479888
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 50 deletions.
13 changes: 13 additions & 0 deletions .changeset/twelve-clocks-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"wrangler": patch
---

fix: module support in service-worker format workers

This rewrites how we pass configuration to miniflare in `wrangler dev`'s local mode. Instead of passing the entire configuration as cli args, we now generate a `wrangler.toml` based on our generated/inferred configuration, and pass that to miniflare instead. This solves a couple of issues, notably -

- `text_blobs` now works in local mode+service-worker format
- `Text` modules now work in local mode+service-worker format
- We properly throw errors for `Data` module in service-worker format

Along with https://github.com/cloudflare/miniflare/pull/205, this fixes https://github.com/cloudflare/wrangler2/issues/416.
5 changes: 5 additions & 0 deletions packages/example-rules-app/src/sw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import content from "./content.md";

addEventListener("fetch", (event) => {
event.respondWith(new Response(`${event.request.url}: ${content}`));
});
Empty file.
128 changes: 78 additions & 50 deletions packages/wrangler/src/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import * as TOML from "@iarna/toml";
import { watch } from "chokidar";
import clipboardy from "clipboardy";
import commandExists from "command-exists";
Expand All @@ -24,7 +25,7 @@ import { syncAssets } from "./sites";
import { getAPIToken } from "./user";
import type { CfPreviewToken } from "./api/preview";
import type { CfModule, CfWorkerInit, CfScriptFormat } from "./api/worker";
import type { Config } from "./config";
import type { Config, RawConfig } from "./config";
import type { Entry } from "./entry";
import type { AssetPaths } from "./sites";
import type { WatchMode } from "esbuild";
Expand Down Expand Up @@ -275,7 +276,6 @@ function useLocalWorker(props: {
inspectorPort: number;
enableLocalPersistence: boolean;
}) {
// TODO: pass vars via command line
const { bundle, format, bindings, port, assetPaths, inspectorPort } = props;
const local = useRef<ReturnType<typeof spawn>>();
const removeSignalExitListener = useRef<() => void>();
Expand Down Expand Up @@ -307,6 +307,80 @@ function useLocalWorker(props: {
);
}

// Let's also construct a wrangler.toml file
const pathToWranglerToml = path.join(
path.dirname(bundle.path),
"wrangler.toml"
);
{
const config: RawConfig = {
build: {
upload: {
rules: (props.rules || []).concat(DEFAULT_MODULE_RULES),
},
},
};

Object.entries(bindings.vars || {}).forEach(([key, value]) => {
config.vars ||= {};
config.vars[key] = value;
});

(bindings.kv_namespaces || []).forEach(({ binding, id }) => {
config.kv_namespaces ||= [];
config.kv_namespaces.push({ binding, id });
});

(bindings.durable_objects?.bindings || []).forEach(
({ name, class_name, script_name }) => {
if (script_name) {
throw new Error(
`⎔ Durable object bindings to external scripts are not yet supported in local mode.`
);
}
config.durable_objects ||= { bindings: [] };
config.durable_objects.bindings.push({ name, class_name });
}
);

if (format === "service-worker") {
if (bindings.wasm_modules) {
config.wasm_modules = bindings.wasm_modules;
}
if (bindings.text_blobs) {
config.text_blobs = bindings.text_blobs;
}

// In service-worker format, modules are referenced
// by global identifiers, so we convert it here.
// This identifier has to be a valid JS identifier,
// so we replace all non alphanumeric characters
// with an underscore.
const identifierSanitiseRegex = /[^a-zA-Z0-9_$]/g;

bundle.modules.forEach(({ name, type }) => {
if (type === "compiled-wasm") {
config.wasm_modules ||= {};
const identifier = name.replace(identifierSanitiseRegex, "_");
config.wasm_modules[identifier] = name;
} else if (type === "text") {
config.text_blobs ||= {};
const identifier = name.replace(identifierSanitiseRegex, "_");
config.text_blobs[identifier] = name;
} else {
throw new Error(
`⎔ Cannot use the module ${name} with type "${type}" in service-worker format yet`
);
}
});
}

await writeFile(
pathToWranglerToml,
TOML.stringify(config as TOML.JsonMap)
);
}

console.log("⎔ Starting a local server...");
// TODO: just use execa for this
local.current = spawn(
Expand All @@ -318,7 +392,7 @@ function useLocalWorker(props: {
bundle.path,
"--watch",
"--wrangler-config",
path.join(__dirname, "../miniflare-config-stubs/wrangler.empty.toml"),
pathToWranglerToml,
"--env",
path.join(__dirname, "../miniflare-config-stubs/.env.empty"),
"--package",
Expand All @@ -342,55 +416,8 @@ function useLocalWorker(props: {
...(props.enableLocalPersistence
? ["--kv-persist", "--cache-persist", "--do-persist"]
: []),
...Object.entries(bindings.vars || {}).flatMap(([key, value]) => {
return ["--binding", `${key}=${value}`];
}),
...(bindings.kv_namespaces || []).flatMap(({ binding }) => {
return ["--kv", binding];
}),
...(bindings.durable_objects?.bindings || []).flatMap(
({ name, class_name }) => {
return ["--do", `${name}=${class_name}`];
}
),
...Object.entries(bindings.wasm_modules || {}).flatMap(
([name, filePath]) => {
return [
"--wasm",
`${name}=${path.join(process.cwd(), filePath)}`,
];
}
),
...bundle.modules.reduce<string[]>((cmd, { name, type }) => {
if (format === "service-worker") {
if (type === "compiled-wasm") {
// In service-worker format, .wasm modules are referenced
// by global identifiers, so we convert it here.
// This identifier has to be a valid JS identifier,
// so we replace all non alphanumeric characters
// with an underscore.
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
return cmd.concat([`--wasm`, `${identifier}=${name}`]);
} else {
// TODO: we should actually support this
throw new Error(
`⎔ Unsupported module type ${type} for file ${name} in service-worker format`
);
}
}
return cmd;
}, []),
"--modules",
String(format === "modules"),
...(props.rules || [])
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.concat(DEFAULT_MODULE_RULES!)
.flatMap((rule) =>
rule.globs.flatMap((glob) => [
"--modules-rule",
`${rule.type}=${glob}`,
])
),
],
{
cwd: path.dirname(bundle.path),
Expand Down Expand Up @@ -463,6 +490,7 @@ function useLocalWorker(props: {
props.public,
props.rules,
bindings.wasm_modules,
bindings.text_blobs,
]);
return { inspectorUrl };
}
Expand Down

0 comments on commit 1479888

Please sign in to comment.