Skip to content

Commit

Permalink
Allow marking externals in Pages Functions (#5172)
Browse files Browse the repository at this point in the history
  • Loading branch information
GregBrimble committed Apr 11, 2024
1 parent 3e57697 commit fbe1c9c
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 8 deletions.
7 changes: 7 additions & 0 deletions .changeset/unlucky-kangaroos-brake.md
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

feat: Allow marking external modules (with `--external`) to avoid bundling them when building Pages Functions

It's useful for Pages Plugins which want to declare a peer dependency.
3 changes: 3 additions & 0 deletions fixtures/pages-functions-app/package.json
Expand Up @@ -19,5 +19,8 @@
},
"engines": {
"node": ">=16.13"
},
"dependencies": {
"is-odd": "^3.0.1"
}
}
7 changes: 7 additions & 0 deletions fixtures/pages-functions-app/tests/index.test.ts
Expand Up @@ -124,6 +124,13 @@ describe("Pages Functions", () => {
expect(response.status).toBe(502);
});

it("should work with peer externals", async ({ expect }) => {
const response = await fetch(`http://${ip}:${port}/mounted-plugin/ext`);
const text = await response.text();
expect(text).toMatchInlineSnapshot(`"42 is even"`);
expect(response.status).toBe(200);
});

it("should mount a Plugin even if in a parameterized route", async ({
expect,
}) => {
Expand Down
5 changes: 5 additions & 0 deletions fixtures/pages-plugin-example/functions/ext.ts
@@ -0,0 +1,5 @@
import isOdd from "is-odd";

export const onRequest: PagesFunction = () => {
return new Response(`42 is ${isOdd(42) ? "odd" : "even"}`);
};
3 changes: 2 additions & 1 deletion fixtures/pages-plugin-example/package.json
Expand Up @@ -10,10 +10,11 @@
"public/"
],
"scripts": {
"build": "wrangler pages functions build --plugin --outdir=dist",
"build": "wrangler pages functions build --plugin --outdir=dist --external=is-odd",
"check:type": "tsc"
},
"devDependencies": {
"is-odd": "^3.0.1",
"wrangler": "workspace:*"
}
}
3 changes: 3 additions & 0 deletions fixtures/pages-plugin-mounted-on-root-app/package.json
Expand Up @@ -19,5 +19,8 @@
},
"engines": {
"node": ">=16.13"
},
"dependencies": {
"is-odd": "^3.0.1"
}
}
6 changes: 5 additions & 1 deletion packages/wrangler/src/deployment-bundle/bundle.ts
Expand Up @@ -85,6 +85,7 @@ export type BundleOptions = {
local: boolean;
projectRoot: string | undefined;
defineNavigatorUserAgent: boolean;
external?: string[];
};

/**
Expand Down Expand Up @@ -122,6 +123,7 @@ export async function bundleWorker(
local,
projectRoot,
defineNavigatorUserAgent,
external,
}: BundleOptions
): Promise<BundleResult> {
// We create a temporary directory for any one-off files we
Expand Down Expand Up @@ -284,7 +286,9 @@ export async function bundleWorker(
}
: {}),
inject,
external: bundle ? ["__STATIC_CONTENT_MANIFEST"] : undefined,
external: bundle
? ["__STATIC_CONTENT_MANIFEST", ...(external ? external : [])]
: undefined,
format: entry.format === "modules" ? "esm" : "iife",
target: COMMON_ESBUILD_OPTIONS.target,
sourcemap: sourcemap ?? true,
Expand Down
10 changes: 10 additions & 0 deletions packages/wrangler/src/pages/build.ts
Expand Up @@ -123,6 +123,11 @@ export function Options(yargs: CommonYargsArgv) {
deprecated: true,
hidden: true,
},
external: {
describe: "A list of module imports to exclude from bundling",
type: "string",
array: true,
},
});
}

Expand All @@ -146,6 +151,7 @@ export const Handler = async (args: PagesBuildArgs) => {
nodejsCompat,
legacyNodeCompat,
defineNavigatorUserAgent,
external,
} = validatedArgs;

try {
Expand All @@ -172,6 +178,7 @@ export const Handler = async (args: PagesBuildArgs) => {
routesOutputPath,
local: false,
defineNavigatorUserAgent,
external,
});
} catch (e) {
if (e instanceof FunctionsNoRoutesError) {
Expand Down Expand Up @@ -213,6 +220,7 @@ export const Handler = async (args: PagesBuildArgs) => {
legacyNodeCompat,
workerScriptPath,
defineNavigatorUserAgent,
external,
} = validatedArgs;

/**
Expand Down Expand Up @@ -244,6 +252,7 @@ export const Handler = async (args: PagesBuildArgs) => {
watch,
nodejsCompat,
defineNavigatorUserAgent,
externalModules: external,
});
}
} else {
Expand All @@ -269,6 +278,7 @@ export const Handler = async (args: PagesBuildArgs) => {
routesOutputPath,
local: false,
defineNavigatorUserAgent,
external,
});
} catch (e) {
if (e instanceof FunctionsNoRoutesError) {
Expand Down
4 changes: 4 additions & 0 deletions packages/wrangler/src/pages/buildFunctions.ts
Expand Up @@ -39,6 +39,7 @@ export async function buildFunctions({
`./functionsRoutes-${Math.random()}.mjs`
),
defineNavigatorUserAgent,
external,
}: Partial<
Pick<
PagesBuildArgs,
Expand All @@ -51,6 +52,7 @@ export async function buildFunctions({
| "watch"
| "plugin"
| "buildOutputDirectory"
| "external"
>
> & {
functionsDirectory: string;
Expand Down Expand Up @@ -120,6 +122,7 @@ export async function buildFunctions({
functionsDirectory: absoluteFunctionsDirectory,
local,
defineNavigatorUserAgent,
external,
});
} else {
bundle = await buildWorkerFromFunctions({
Expand All @@ -137,6 +140,7 @@ export async function buildFunctions({
legacyNodeCompat,
nodejsCompat,
defineNavigatorUserAgent,
external,
});
}

Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/pages/functions/buildPlugin.ts
Expand Up @@ -24,6 +24,7 @@ export function buildPluginFromFunctions({
functionsDirectory,
local,
defineNavigatorUserAgent,
external,
}: Options) {
const entry: Entry = {
file: resolve(getBasePath(), "templates/pages-template-plugin.ts"),
Expand Down Expand Up @@ -51,6 +52,7 @@ export function buildPluginFromFunctions({
nodejsCompat: true,
define: {},
doBindings: [], // Pages functions don't support internal Durable Objects
external,
plugins: [
buildNotifierPlugin(onEnd),
{
Expand Down
24 changes: 18 additions & 6 deletions packages/wrangler/src/pages/functions/buildWorker.ts
Expand Up @@ -32,6 +32,7 @@ export type Options = {
functionsDirectory: string;
local: boolean;
defineNavigatorUserAgent: boolean;
external?: string[];
};

export function buildWorkerFromFunctions({
Expand All @@ -49,6 +50,7 @@ export function buildWorkerFromFunctions({
functionsDirectory,
local,
defineNavigatorUserAgent,
external,
}: Options) {
const entry: Entry = {
file: resolve(getBasePath(), "templates/pages-template-worker.ts"),
Expand Down Expand Up @@ -76,6 +78,7 @@ export function buildWorkerFromFunctions({
__FALLBACK_SERVICE__: JSON.stringify(fallbackService),
},
doBindings: [], // Pages functions don't support internal Durable Objects
external,
plugins: [
buildNotifierPlugin(onEnd),
{
Expand Down Expand Up @@ -170,7 +173,7 @@ export type RawOptions = {
outdir?: string;
directory: string;
bundle?: boolean;
external?: string[];
externalModules?: string[];
minify?: boolean;
sourcemap?: boolean;
watch?: boolean;
Expand All @@ -182,6 +185,7 @@ export type RawOptions = {
local: boolean;
additionalModules?: CfModule[];
defineNavigatorUserAgent: boolean;
external?: string[];
};

/**
Expand All @@ -197,7 +201,7 @@ export function buildRawWorker({
outdir,
directory,
bundle = true,
external,
externalModules,
minify = false,
sourcemap = false,
watch = false,
Expand All @@ -208,14 +212,15 @@ export function buildRawWorker({
local,
additionalModules = [],
defineNavigatorUserAgent,
external,
}: RawOptions) {
const entry: Entry = {
file: workerScriptPath,
directory: resolve(directory),
format: "modules",
moduleRoot: resolve(directory),
};
const moduleCollector = external
const moduleCollector = externalModules
? noopModuleCollector
: createModuleCollector({ entry, findAdditionalModules: false });

Expand All @@ -230,18 +235,23 @@ export function buildRawWorker({
nodejsCompat,
define: {},
doBindings: [], // Pages functions don't support internal Durable Objects
external,
plugins: [
...plugins,
buildNotifierPlugin(onEnd),
...(external
...(externalModules
? [
// In some cases, we want to enable bundling in esbuild so that we can flatten a shim around the entrypoint, but we still don't want to actually bundle in all the chunks that a Worker references.
// This plugin allows us to mark those chunks as external so they are not inlined.
{
name: "external-fixer",
setup(pluginBuild) {
pluginBuild.onResolve({ filter: /.*/ }, async (args) => {
if (external.includes(resolve(args.resolveDir, args.path))) {
if (
externalModules.includes(
resolve(args.resolveDir, args.path)
)
) {
return { path: args.path, external: true };
}
});
Expand Down Expand Up @@ -309,7 +319,9 @@ export async function produceWorkerBundleForWorkerJSDirectory({
const bundleResult = await buildRawWorker({
workerScriptPath: entrypoint,
bundle: true,
external: additionalModules.map((m) => join(workerJSDirectory, m.name)),
externalModules: additionalModules.map((m) =>
join(workerJSDirectory, m.name)
),
outfile,
directory: buildOutputDirectory,
local: false,
Expand Down
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fbe1c9c

Please sign in to comment.