diff --git a/.changeset/curly-emus-press.md b/.changeset/curly-emus-press.md new file mode 100644 index 000000000000..fbb2d627874c --- /dev/null +++ b/.changeset/curly-emus-press.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +fix: `_worker.js/` directory support for dynamically imported chunks in `wrangler pages dev` diff --git a/fixtures/pages-workerjs-directory/public/_worker.js/other-script.js b/fixtures/pages-workerjs-directory/public/_worker.js/other-script.js index 58c57157d36c..720d88d4f00e 100644 --- a/fixtures/pages-workerjs-directory/public/_worker.js/other-script.js +++ b/fixtures/pages-workerjs-directory/public/_worker.js/other-script.js @@ -1 +1 @@ -export default "test"; +export default "other-script-test"; diff --git a/fixtures/pages-workerjs-directory/tests/index.test.ts b/fixtures/pages-workerjs-directory/tests/index.test.ts index d4446d65f562..7978d564359f 100644 --- a/fixtures/pages-workerjs-directory/tests/index.test.ts +++ b/fixtures/pages-workerjs-directory/tests/index.test.ts @@ -23,8 +23,8 @@ describe.concurrent("Pages _worker.js/ directory", () => { fetch(`http://${ip}:${port}/static`).then((resp) => resp.text()) ).resolves.toContain("static"); await expect( - fetch(`http://${ip}:${port}/other-script`).then((resp) => resp.text()) - ).resolves.toContain("test"); + fetch(`http://${ip}:${port}/other-script.js`).then((resp) => resp.text()) + ).resolves.toContain("other-script-test"); await stop(); }); diff --git a/packages/wrangler/src/api/dev.ts b/packages/wrangler/src/api/dev.ts index 35d08d399af2..62fac6a609c6 100644 --- a/packages/wrangler/src/api/dev.ts +++ b/packages/wrangler/src/api/dev.ts @@ -5,6 +5,7 @@ import { logger } from "../logger"; import type { Environment } from "../config"; import type { Rule } from "../config/environment"; import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli/types"; +import type { CfModule } from "../worker"; import type { RequestInit, Response, RequestInfo } from "undici"; export interface UnstableDevOptions { @@ -43,7 +44,6 @@ export interface UnstableDevOptions { bucket_name: string; preview_bucket_name?: string; }[]; - processEntrypoint?: boolean; moduleRoot?: string; rules?: Rule[]; logLevel?: "none" | "info" | "error" | "log" | "warn" | "debug"; // Specify logging level [choices: "debug", "info", "log", "warn", "error", "none"] [default: "log"] @@ -51,6 +51,8 @@ export interface UnstableDevOptions { local?: boolean; accountId?: string; experimental?: { + processEntrypoint?: boolean; + additionalModules?: CfModule[]; d1Databases?: Environment["d1_databases"]; disableExperimentalWarning?: boolean; // Disables wrangler's warning when unstable APIs are used. disableDevRegistry?: boolean; // Disables wrangler's support multi-worker setups. May reduce flakiness when used in tests in CI. @@ -96,6 +98,8 @@ export async function unstable_dev( const { // there are two types of "experimental" options: // 1. options to unstable_dev that we're still testing or are unsure of + processEntrypoint = false, + additionalModules, disableDevRegistry, disableExperimentalWarning, forceLocal, @@ -154,7 +158,8 @@ export async function unstable_dev( }, config: options?.config, env: options?.env, - processEntrypoint: !!options?.processEntrypoint, + processEntrypoint, + additionalModules, bundle: options?.bundle, compatibilityDate: options?.compatibilityDate, compatibilityFlags: options?.compatibilityFlags, @@ -245,6 +250,8 @@ export async function unstable_dev( }, config: options?.config, env: options?.env, + processEntrypoint, + additionalModules, bundle: options?.bundle, compatibilityDate: options?.compatibilityDate, compatibilityFlags: options?.compatibilityFlags, diff --git a/packages/wrangler/src/bundle.ts b/packages/wrangler/src/bundle.ts index 105f29da7ac2..90034f1912bb 100644 --- a/packages/wrangler/src/bundle.ts +++ b/packages/wrangler/src/bundle.ts @@ -157,6 +157,7 @@ export async function bundleWorker( loader?: Record; sourcemap?: esbuild.CommonOptions["sourcemap"]; plugins?: esbuild.Plugin[]; + additionalModules?: CfModule[]; // TODO: Rip these out https://github.com/cloudflare/workers-sdk/issues/2153 disableModuleCollection?: boolean; isOutfile?: boolean; @@ -193,6 +194,7 @@ export async function bundleWorker( disableModuleCollection, isOutfile, forPages, + additionalModules = [], } = options; // We create a temporary directory for any oneoff files we @@ -457,16 +459,20 @@ export async function bundleWorker( entryPointOutputs[0][0] ); + const modules = [...additionalModules, ...moduleCollector.modules]; + // copy all referenced modules into the output bundle directory - for (const module of moduleCollector.modules) { - fs.writeFileSync( - path.join(path.dirname(resolvedEntryPointPath), module.name), - module.content + for (const module of modules) { + const modulePath = path.join( + path.dirname(resolvedEntryPointPath), + module.name ); + fs.mkdirSync(path.dirname(modulePath), { recursive: true }); + fs.writeFileSync(modulePath, module.content); } return { - modules: moduleCollector.modules, + modules, dependencies, resolvedEntryPointPath, bundleType, diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 046f067e1df0..65dabfa75914 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -31,7 +31,7 @@ import type { Config, Environment } from "./config"; import type { Route, Rule } from "./config/environment"; import type { LoggerLevel } from "./logger"; import type { EnablePagesAssetsServiceBindingOptions } from "./miniflare-cli/types"; -import type { CfWorkerInit } from "./worker"; +import type { CfWorkerInit, CfModule } from "./worker"; import type { CommonYargsArgv, StrictYargsOptionsToInterface, @@ -335,6 +335,7 @@ export type AdditionalDevProps = { }[]; d1Databases?: Environment["d1_databases"]; processEntrypoint?: boolean; + additionalModules?: CfModule[]; moduleRoot?: string; rules?: Rule[]; }; @@ -400,6 +401,8 @@ export async function startDev(args: StartDevOptions) { getInspectorPort, cliDefines, localPersistencePath, + processEntrypoint, + additionalModules, } = await validateDevServerSettings(args, config); await metrics.sendMetricsEvent( @@ -427,7 +430,8 @@ export async function startDev(args: StartDevOptions) { zone={zoneId} host={host} routes={routes} - processEntrypoint={!!args.processEntrypoint} + processEntrypoint={processEntrypoint} + additionalModules={additionalModules} rules={args.rules ?? getRules(configParam)} legacyEnv={isLegacyEnv(configParam)} minify={args.minify ?? configParam.minify} @@ -537,6 +541,8 @@ export async function startApiDev(args: StartDevOptions) { getInspectorPort, cliDefines, localPersistencePath, + processEntrypoint, + additionalModules, } = await validateDevServerSettings(args, config); await metrics.sendMetricsEvent( @@ -564,7 +570,8 @@ export async function startApiDev(args: StartDevOptions) { zone: zoneId, host: host, routes: routes, - processEntrypoint: !!args.processEntrypoint, + processEntrypoint, + additionalModules, rules: args.rules ?? getRules(configParam), legacyEnv: isLegacyEnv(configParam), minify: args.minify ?? configParam.minify, @@ -792,6 +799,8 @@ async function validateDevServerSettings( routes, cliDefines, localPersistencePath, + processEntrypoint: !!args.processEntrypoint, + additionalModules: args.additionalModules ?? [], }; } diff --git a/packages/wrangler/src/dev/dev.tsx b/packages/wrangler/src/dev/dev.tsx index 4ed39b251b2e..e40ccb530e9d 100644 --- a/packages/wrangler/src/dev/dev.tsx +++ b/packages/wrangler/src/dev/dev.tsx @@ -30,7 +30,7 @@ import type { WorkerRegistry } from "../dev-registry"; import type { Entry } from "../entry"; import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli/types"; import type { AssetPaths } from "../sites"; -import type { CfWorkerInit } from "../worker"; +import type { CfModule, CfWorkerInit } from "../worker"; /** * This hooks establishes a connection with the dev registry, @@ -117,6 +117,7 @@ export type DevProps = { initialIp: string; inspectorPort: number; processEntrypoint: boolean; + additionalModules: CfModule[]; rules: Config["rules"]; accountId: string | undefined; initialMode: "local" | "remote"; @@ -274,6 +275,7 @@ function DevSession(props: DevSessionProps) { destination: directory, jsxFactory: props.jsxFactory, processEntrypoint: props.processEntrypoint, + additionalModules: props.additionalModules, rules: props.rules, jsxFragment: props.jsxFragment, serveAssetsFromWorker: Boolean( diff --git a/packages/wrangler/src/dev/start-server.ts b/packages/wrangler/src/dev/start-server.ts index 6d976eff8563..a2445285b26a 100644 --- a/packages/wrangler/src/dev/start-server.ts +++ b/packages/wrangler/src/dev/start-server.ts @@ -30,6 +30,7 @@ import type { Config } from "../config"; import type { DurableObjectBindings } from "../config/environment"; import type { WorkerRegistry } from "../dev-registry"; import type { Entry } from "../entry"; +import type { CfModule } from "../worker"; import type { DevProps, DirectorySyncResult } from "./dev"; import type { LocalProps } from "./local"; import type { EsbuildBundle } from "./use-esbuild"; @@ -93,6 +94,7 @@ export async function startDevServer( destination: directory.name, jsxFactory: props.jsxFactory, processEntrypoint: props.processEntrypoint, + additionalModules: props.additionalModules, rules: props.rules, jsxFragment: props.jsxFragment, serveAssetsFromWorker: Boolean( @@ -208,6 +210,7 @@ async function runEsbuild({ jsxFactory, jsxFragment, processEntrypoint, + additionalModules, rules, assets, betaD1Shims, @@ -231,6 +234,7 @@ async function runEsbuild({ jsxFactory: string | undefined; jsxFragment: string | undefined; processEntrypoint: boolean; + additionalModules: CfModule[]; rules: Config["rules"]; assets: Config["assets"]; betaD1Shims?: string[]; @@ -287,6 +291,10 @@ async function runEsbuild({ local, experimentalLocal, doBindings, + additionalModules: [ + ...(traverseModuleGraphResult?.modules ?? []), + ...additionalModules, + ], }); } @@ -297,7 +305,9 @@ async function runEsbuild({ type: bundleResult?.bundleType ?? (entry.format === "modules" ? "esm" : "commonjs"), - modules: traverseModuleGraphResult?.modules ?? bundleResult?.modules ?? [], + modules: bundleResult + ? bundleResult.modules + : [...(traverseModuleGraphResult?.modules ?? []), ...additionalModules], dependencies: bundleResult?.dependencies ?? {}, sourceMapPath: bundleResult?.sourceMapPath, sourceMapMetadata: bundleResult?.sourceMapMetadata, diff --git a/packages/wrangler/src/dev/use-esbuild.ts b/packages/wrangler/src/dev/use-esbuild.ts index 1d07115d8697..cc34e243663c 100644 --- a/packages/wrangler/src/dev/use-esbuild.ts +++ b/packages/wrangler/src/dev/use-esbuild.ts @@ -29,6 +29,7 @@ export function useEsbuild({ jsxFactory, jsxFragment, processEntrypoint, + additionalModules, rules, assets, serveAssetsFromWorker, @@ -53,6 +54,7 @@ export function useEsbuild({ jsxFactory: string | undefined; jsxFragment: string | undefined; processEntrypoint: boolean; + additionalModules: CfModule[]; rules: Config["rules"]; assets: Config["assets"]; define: Config["define"]; @@ -141,6 +143,10 @@ export function useEsbuild({ targetConsumer, testScheduled, experimentalLocal, + additionalModules: [ + ...(traverseModuleGraphResult?.modules ?? []), + ...additionalModules, + ], }); } @@ -167,8 +173,12 @@ export function useEsbuild({ type: bundleResult?.bundleType ?? (entry.format === "modules" ? "esm" : "commonjs"), - modules: - traverseModuleGraphResult?.modules ?? bundleResult?.modules ?? [], + modules: bundleResult + ? bundleResult.modules + : [ + ...(traverseModuleGraphResult?.modules ?? []), + ...additionalModules, + ], dependencies: bundleResult?.dependencies ?? {}, sourceMapPath: bundleResult?.sourceMapPath, sourceMapMetadata: bundleResult?.sourceMapMetadata, @@ -192,6 +202,7 @@ export function useEsbuild({ jsxFragment, serveAssetsFromWorker, processEntrypoint, + additionalModules, rules, tsconfig, exit, diff --git a/packages/wrangler/src/pages/dev.ts b/packages/wrangler/src/pages/dev.ts index d4dc3afe8077..18726491b030 100644 --- a/packages/wrangler/src/pages/dev.ts +++ b/packages/wrangler/src/pages/dev.ts @@ -13,10 +13,15 @@ import { getBasePath } from "../paths"; import { buildFunctions } from "./buildFunctions"; import { ROUTES_SPEC_VERSION, SECONDS_TO_WAIT_FOR_PROXY } from "./constants"; import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors"; -import { buildRawWorker, checkRawWorker } from "./functions/buildWorker"; +import { + buildRawWorker, + checkRawWorker, + traverseAndBuildWorkerJSDirectory, +} from "./functions/buildWorker"; import { validateRoutes } from "./functions/routes-validation"; import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils"; import type { AdditionalDevProps } from "../dev"; +import type { CfModule } from "../worker"; import type { CommonYargsArgv, StrictYargsOptionsToInterface, @@ -268,8 +273,28 @@ export const Handler = async ({ let scriptPath = ""; const nodejsCompat = compatibilityFlags?.includes("nodejs_compat"); + let modules: CfModule[] = []; + + if (usingWorkerDirectory) { + const runBuild = async () => { + const bundleResult = await traverseAndBuildWorkerJSDirectory({ + workerJSDirectory: workerScriptPath, + buildOutputDirectory: directory ?? ".", + nodejsCompat, + }); + modules = bundleResult.modules; + scriptPath = bundleResult.resolvedEntryPointPath; + }; + + await runBuild().then(() => scriptReadyResolve()); - if (usingWorkerScript) { + watch([workerScriptPath], { + persistent: true, + ignoreInitial: true, + }).on("all", async () => { + await runBuild(); + }); + } else if (usingWorkerScript) { scriptPath = workerScriptPath; let runBuild = async () => { await checkRawWorker(workerScriptPath, () => scriptReadyResolve()); @@ -545,8 +570,6 @@ export const Handler = async ({ r2: r2s.map((binding) => { return { binding: binding.toString(), bucket_name: "" }; }), - processEntrypoint: true, - moduleRoot: workerScriptPath, rules: usingWorkerDirectory ? [ { @@ -561,6 +584,8 @@ export const Handler = async ({ inspect: undefined, logLevel, experimental: { + processEntrypoint: true, + additionalModules: modules, d1Databases: d1s.map((binding) => ({ binding: binding.toString(), database_id: "", // Required for types, but unused by dev diff --git a/packages/wrangler/src/pages/functions/buildWorker.ts b/packages/wrangler/src/pages/functions/buildWorker.ts index e5b8fc42c3b6..4cae35e36b05 100644 --- a/packages/wrangler/src/pages/functions/buildWorker.ts +++ b/packages/wrangler/src/pages/functions/buildWorker.ts @@ -10,6 +10,7 @@ import { getBasePath } from "../../paths"; import traverseModuleGraph from "../../traverse-module-graph"; import { D1_BETA_PREFIX } from "../../worker"; import type { BundleResult } from "../../bundle"; +import type { CfModule } from "../../worker"; import type { Plugin } from "esbuild"; export type Options = { @@ -174,6 +175,7 @@ export type RawOptions = { nodejsCompat?: boolean; local: boolean; betaD1Shims?: string[]; + additionalModules?: CfModule[]; }; /** @@ -198,6 +200,7 @@ export function buildRawWorker({ nodejsCompat, local, betaD1Shims, + additionalModules, }: RawOptions) { return bundleWorker( { @@ -229,6 +232,7 @@ export function buildRawWorker({ local, experimentalLocal: false, forPages: true, + additionalModules, } ); } @@ -273,6 +277,7 @@ export async function traverseAndBuildWorkerJSDirectory({ onEnd: () => {}, betaD1Shims: d1Databases, nodejsCompat, + additionalModules: traverseModuleGraphResult.modules, }); return { diff --git a/packages/wrangler/src/traverse-module-graph.ts b/packages/wrangler/src/traverse-module-graph.ts index 2454875435d0..2c9c8845c14b 100644 --- a/packages/wrangler/src/traverse-module-graph.ts +++ b/packages/wrangler/src/traverse-module-graph.ts @@ -36,7 +36,7 @@ export default async function traverseModuleGraph( const bundleType = entry.format === "modules" ? "esm" : "commonjs"; if (modules.length > 0) { - logger.info(`Uploading additional modules:`); + logger.info(`Attaching additional modules:`); modules.forEach(({ name, type }) => { logger.info(`- ${chalk.blue(name)} (${chalk.green(type ?? "")})`); });