Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 2 additions & 20 deletions packages/wrangler/src/deployment-bundle/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as fs from "node:fs";
import * as path from "node:path";
import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
import chalk from "chalk";
import * as esbuild from "esbuild";
import {
Expand All @@ -18,12 +16,9 @@ import {
} from "./build-failures";
import { dedupeModulesByName } from "./dedupe-modules";
import { getEntryPointFromMetafile } from "./entry-point-from-metafile";
import { asyncLocalStoragePlugin } from "./esbuild-plugins/als-external";
import { cloudflareInternalPlugin } from "./esbuild-plugins/cloudflare-internal";
import { configProviderPlugin } from "./esbuild-plugins/config-provider";
import { nodejsHybridPlugin } from "./esbuild-plugins/hybrid-nodejs-compat";
import { nodejsCompatPlugin } from "./esbuild-plugins/nodejs-compat";
import { standardURLPlugin } from "./esbuild-plugins/standard-url";
import { getNodeJSCompatPlugins } from "./esbuild-plugins/nodejs-plugins";
import { writeAdditionalModules } from "./find-additional-modules";
import { noopModuleCollector } from "./module-collection";
import type { Config } from "../config";
Expand Down Expand Up @@ -440,20 +435,7 @@ export async function bundleWorker(
plugins: [
aliasPlugin,
moduleCollector.plugin,
...(nodejsCompatMode === "als" ? [asyncLocalStoragePlugin] : []),
...(nodejsCompatMode === "legacy"
? [
NodeGlobalsPolyfills({ buffer: true }),
standardURLPlugin(),
NodeModulesPolyfills(),
]
: []),
// Runtime Node.js compatibility (will warn if not using nodejs compat flag and are trying to import from a Node.js builtin).
...(nodejsCompatMode === "v1" || nodejsCompatMode !== "v2"
? [nodejsCompatPlugin(nodejsCompatMode === "v1")]
: []),
// Hybrid Node.js compatibility
...(nodejsCompatMode === "v2" ? [nodejsHybridPlugin()] : []),
...getNodeJSCompatPlugins(nodejsCompatMode ?? null),
cloudflareInternalPlugin,
buildResultPlugin,
...(plugins || []),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ function handleRequireCallsToNodeJSBuiltins(build: PluginBuild) {
({ path }) => {
return {
contents: dedent`
import libDefault from '${path}';
module.exports = libDefault;`,
import libDefault from '${path}';
module.exports = libDefault;`,
loader: "js",
};
}
Expand Down Expand Up @@ -150,8 +150,8 @@ function handleUnenvAliasedPackages(
({ path }) => {
return {
contents: dedent`
import * as esm from '${path}';
module.exports = __cf_cjs(esm);
import * as esm from '${path}';
module.exports = __cf_cjs(esm);
`,
loader: "js",
};
Expand Down Expand Up @@ -188,7 +188,10 @@ function handleNodeJSGlobals(
const { importStatement, exportName } = getGlobalInject(inject[globalName]);

return {
contents: `${importStatement}\nglobalThis.${globalName} = ${exportName};`,
contents: dedent`
${importStatement}
globalThis.${globalName} = ${exportName};
`,
};
});
}
Expand All @@ -213,38 +216,21 @@ function getGlobalInject(globalInject: string | string[]) {
}

/**
* Encodes a case sensitive string to lowercase string by prefixing all uppercase letters
* with $ and turning them into lowercase letters.
* Encodes a case sensitive string to lowercase string.
*
* - Escape $ with another $ ("$" -> "$$")
* - Escape uppercase letters with $ and turn them into lowercase letters ("L" -> "$L")
*
* This function exists because ESBuild requires that all resolved paths are case insensitive.
* Without this transformation, ESBuild will clobber /foo/bar.js with /foo/Bar.js
*
* This is important to support `inject` config for `performance` and `Performance` introduced
* in https://github.com/unjs/unenv/pull/257
*/
export function encodeToLowerCase(str: string): string {
return str
.replaceAll(/\$/g, () => "$$")
.replaceAll(/[A-Z]/g, (letter) => `$${letter.toLowerCase()}`);
return str.replace(/[A-Z$]/g, (escape) => `$${escape.toLowerCase()}`);
}

/**
* Decodes a string lowercased using `encodeToLowerCase` to the original strings
*/
export function decodeFromLowerCase(str: string): string {
let out = "";
let i = 0;
while (i < str.length - 1) {
if (str[i] === "$") {
i++;
out += str[i].toUpperCase();
} else {
out += str[i];
}
i++;
}
if (i < str.length) {
out += str[i];
}
return out;
return str.replace(/\$[a-z$]/g, (escaped) => escaped[1].toUpperCase());
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@ import chalk from "chalk";
import { logger } from "../../logger";
import { dedent } from "../../utils/dedent";
import type { Plugin } from "esbuild";
import type { NodeJSCompatMode } from "miniflare";

/**
* An esbuild plugin that will mark any `node:...` imports as external.
* An esbuild plugin that will:
* - mark any `node:...` imports as external
* - warn if there are node imports (if not in v1 mode)
*
* Applies to: null, als, legacy and v1 modes.
*/
export const nodejsCompatPlugin: (silenceWarnings: boolean) => Plugin = (
silenceWarnings
) => ({
export const nodejsCompatPlugin = (mode: NodeJSCompatMode): Plugin => ({
name: "nodejs_compat-imports",
setup(pluginBuild) {
// Infinite loop detection
const seen = new Set<string>();

// Prevent multiple warnings per package
const warnedPackaged = new Map<string, string[]>();
const warnedPackages = new Map<string, string[]>();

pluginBuild.onStart(() => {
seen.clear();
warnedPackaged.clear();
warnedPackages.clear();
});
pluginBuild.onResolve(
{ filter: /node:.*/ },
async ({ path, kind, resolveDir, ...opts }) => {
const specifier = `${path}:${kind}:${resolveDir}:${opts.importer}`;
async ({ path, kind, resolveDir, importer }) => {
const specifier = `${path}:${kind}:${resolveDir}:${importer}`;
if (seen.has(specifier)) {
return;
}
Expand All @@ -35,18 +38,15 @@ export const nodejsCompatPlugin: (silenceWarnings: boolean) => Plugin = (
const result = await pluginBuild.resolve(path, {
kind,
resolveDir,
importer: opts.importer,
importer,
});

if (result.errors.length > 0) {
// esbuild couldn't resolve the package
// We should warn the user, but not fail the build

let pathWarnedPackaged = warnedPackaged.get(path);
if (pathWarnedPackaged === undefined) {
warnedPackaged.set(path, (pathWarnedPackaged = []));
}
pathWarnedPackaged.push(opts.importer);
const pathWarnedPackages = warnedPackages.get(path) ?? [];
pathWarnedPackages.push(importer);
warnedPackages.set(path, pathWarnedPackages);

return { external: true };
}
Expand All @@ -64,10 +64,10 @@ export const nodejsCompatPlugin: (silenceWarnings: boolean) => Plugin = (
pluginBuild.onEnd(() => {
if (
pluginBuild.initialOptions.format === "iife" &&
warnedPackaged.size > 0
warnedPackages.size > 0
) {
const paths = new Intl.ListFormat("en-US").format(
Array.from(warnedPackaged.keys())
Array.from(warnedPackages.keys())
.map((p) => `"${p}"`)
.sort()
);
Expand All @@ -90,8 +90,8 @@ export const nodejsCompatPlugin: (silenceWarnings: boolean) => Plugin = (
// Wait until the build finishes to log warnings, so that all files which import a package
// can be collated
pluginBuild.onEnd(() => {
if (!silenceWarnings) {
warnedPackaged.forEach((importers: string[], path: string) => {
if (mode !== "v1") {
warnedPackages.forEach((importers: string[], path: string) => {
logger.warn(
dedent`
The package "${path}" wasn't found on the file system but is built into node.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
import { asyncLocalStoragePlugin } from "./als-external";
import { nodejsHybridPlugin } from "./hybrid-nodejs-compat";
import { nodejsCompatPlugin } from "./nodejs-compat";
import { standardURLPlugin } from "./standard-url";
import type { Plugin } from "esbuild";
import type { NodeJSCompatMode } from "miniflare";

/**
* Returns the list of ESBuild plugins to use for a given compat mode.
*/
export function getNodeJSCompatPlugins(mode: NodeJSCompatMode): Plugin[] {
switch (mode) {
case "als":
return [asyncLocalStoragePlugin, nodejsCompatPlugin(mode)];
case "legacy":
return [
NodeGlobalsPolyfills({ buffer: true }),
standardURLPlugin(),
NodeModulesPolyfills(),
nodejsCompatPlugin(mode),
];
case "v1":
return [nodejsCompatPlugin(mode)];
case "v2":
return [nodejsHybridPlugin()];
case null:
return [nodejsCompatPlugin(mode)];
}
}
Loading