diff --git a/.changeset/early-tigers-buy.md b/.changeset/early-tigers-buy.md new file mode 100644 index 000000000000..fd1edfad238a --- /dev/null +++ b/.changeset/early-tigers-buy.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +fix: tidy up error messaging for unexpected use of Node.js APIs + +Fixes #6822 diff --git a/.changeset/tender-items-exercise.md b/.changeset/tender-items-exercise.md new file mode 100644 index 000000000000..caa27aae2f3d --- /dev/null +++ b/.changeset/tender-items-exercise.md @@ -0,0 +1,7 @@ +--- +"miniflare": patch +--- + +fix: teach Miniflare about node_compat version date switch + +A compatibility of Sept 23, 2024 or later means that `nodejs_compat` is equivalent to `nodejs_compat_v2`. diff --git a/fixtures/nodejs-hybrid-app/wrangler.toml b/fixtures/nodejs-hybrid-app/wrangler.toml index c71823b5dba6..5accaab35a2e 100644 --- a/fixtures/nodejs-hybrid-app/wrangler.toml +++ b/fixtures/nodejs-hybrid-app/wrangler.toml @@ -1,7 +1,8 @@ name = "nodejs-hybrid-app" main = "src/index.ts" -compatibility_date = "2024-06-03" -compatibility_flags = ["nodejs_compat_v2"] +# Setting compat date to 2024/09/23 means we don't need to use `nodejs_compat_v2` +compatibility_date = "2024-09-23" +compatibility_flags = ["nodejs_compat"] [vars] # These DB connection values are to a public database containing information about diff --git a/packages/miniflare/src/plugins/core/index.ts b/packages/miniflare/src/plugins/core/index.ts index c03752d7688f..95c2fd823861 100644 --- a/packages/miniflare/src/plugins/core/index.ts +++ b/packages/miniflare/src/plugins/core/index.ts @@ -807,7 +807,10 @@ export function getGlobalServices({ } function getWorkerScript( - options: SourceOptions & { compatibilityFlags?: string[] }, + options: SourceOptions & { + compatibilityDate?: string; + compatibilityFlags?: string[]; + }, workerIndex: number, additionalModuleNames: string[] ): { serviceWorkerScript: string } | { modules: Worker_Module[] } { @@ -843,6 +846,7 @@ function getWorkerScript( modulesRoot, additionalModuleNames, options.modulesRules, + options.compatibilityDate, options.compatibilityFlags ); // If `script` and `scriptPath` are set, resolve modules in `script` @@ -862,3 +866,4 @@ export * from "./proxy"; export * from "./constants"; export * from "./modules"; export * from "./services"; +export * from "./node-compat"; diff --git a/packages/miniflare/src/plugins/core/modules.ts b/packages/miniflare/src/plugins/core/modules.ts index bc9d1ffb02f6..669dc84489d4 100644 --- a/packages/miniflare/src/plugins/core/modules.ts +++ b/packages/miniflare/src/plugins/core/modules.ts @@ -11,6 +11,7 @@ import { z } from "zod"; import { Worker_Module } from "../../runtime"; import { globsToRegExps, MiniflareCoreError, PathSchema } from "../../shared"; import { MatcherRegExps, testRegExps } from "../../workers"; +import { getNodeCompat, NodeJSCompatMode } from "./node-compat"; import type estree from "estree"; const SUGGEST_BUNDLE = @@ -156,7 +157,7 @@ function getResolveErrorPrefix(referencingPath: string): string { export class ModuleLocator { readonly #compiledRules: CompiledModuleRule[]; - readonly #nodejsCompat: boolean; + readonly #nodejsCompatMode: NodeJSCompatMode; readonly #visitedPaths = new Set(); readonly modules: Worker_Module[] = []; @@ -164,15 +165,16 @@ export class ModuleLocator { private readonly modulesRoot: string, private readonly additionalModuleNames: string[], rules: ModuleRule[] = [], + compatibilityDate?: string, compatibilityFlags?: string[] ) { // Implicit shallow-copy to avoid mutating argument rules = rules.concat(DEFAULT_MODULE_RULES); this.#compiledRules = compileModuleRules(rules); - // `nodejs_compat` doesn't have a default-on date, so we know whether it's - // enabled just by looking at flags: - // https://github.com/cloudflare/workerd/blob/edcd0300bc7b8f56040d090177db947edd22f91b/src/workerd/io/compatibility-date.capnp#L237-L240 - this.#nodejsCompat = compatibilityFlags?.includes("nodejs_compat") ?? false; + this.#nodejsCompatMode = getNodeCompat( + compatibilityDate, + compatibilityFlags ?? [] + ).mode; } visitEntrypoint(code: string, modulePath: string) { @@ -289,14 +291,19 @@ ${dim(modulesConfig)}`; } const spec = specExpression.value; - // `node:` (assuming `nodejs_compat` flag enabled), `cloudflare:` and - // `workerd:` imports don't need to be included explicitly const isNodeJsCompatModule = referencingType === "NodeJsCompatModule"; if ( - (this.#nodejsCompat && spec.startsWith("node:")) || + // `cloudflare:` and `workerd:` imports don't need to be included explicitly spec.startsWith("cloudflare:") || spec.startsWith("workerd:") || - (isNodeJsCompatModule && builtinModulesWithPrefix.includes(spec)) || + // Node.js compat v1 requires imports to be prefixed with `node:` + (this.#nodejsCompatMode === "v1" && spec.startsWith("node:")) || + // Node.js compat modules and v2 can also handle non-prefixed imports + ((this.#nodejsCompatMode === "v2" || isNodeJsCompatModule) && + builtinModulesWithPrefix.includes(spec)) || + // Async Local Storage mode (node_als) only deals with `node:async_hooks` imports + (this.#nodejsCompatMode === "als" && spec === "node:async_hooks") || + // Any "additional" external modules can be ignored this.additionalModuleNames.includes(spec) ) { return; diff --git a/packages/miniflare/src/plugins/core/node-compat.ts b/packages/miniflare/src/plugins/core/node-compat.ts new file mode 100644 index 000000000000..5c3a532756a2 --- /dev/null +++ b/packages/miniflare/src/plugins/core/node-compat.ts @@ -0,0 +1,78 @@ +/** + * We can provide Node.js compatibility in a number of different modes: + * - "legacy" - this mode adds compile-time polyfills that are not well maintained and cannot work with workerd runtime builtins. + * - "als": this mode tells the workerd runtime to enable only the Async Local Storage builtin library (accessible via `node:async_hooks`). + * - "v1" - this mode tells the workerd runtime to enable some Node.js builtin libraries (accessible only via `node:...` imports) but no globals. + * - "v2" - this mode tells the workerd runtime to enable more Node.js builtin libraries (accessible both with and without the `node:` prefix) + * and also some Node.js globals such as `Buffer`; it also turns on additional compile-time polyfills for those that are not provided by the runtime. + * - null - no Node.js compatibility. + */ +export type NodeJSCompatMode = "legacy" | "als" | "v1" | "v2" | null; + +/** + * Computes the Node.js compatibility mode we are running. + * + * NOTES: + * - The v2 mode is configured via `nodejs_compat_v2` compat flag or via `nodejs_compat` plus a compatibility date of Sept 23rd. 2024 or later. + * - See `EnvironmentInheritable` for `nodeCompat` and `noBundle`. + * + * @param compatibilityDateStr The compatibility date + * @param compatibilityFlags The compatibility flags + * @param opts.nodeCompat Whether the legacy node_compat arg is being used + * @returns the mode and flags to indicate specific configuration for validating. + */ +export function getNodeCompat( + compatibilityDate: string = "2000-01-01", // Default to some arbitrary old date + compatibilityFlags: string[], + opts?: { + nodeCompat?: boolean; + } +) { + const { nodeCompat = false } = opts ?? {}; + const { + hasNodejsAlsFlag, + hasNodejsCompatFlag, + hasNodejsCompatV2Flag, + hasNoNodejsCompatV2Flag, + hasExperimentalNodejsCompatV2Flag, + } = parseNodeCompatibilityFlags(compatibilityFlags); + + const nodeCompatSwitchOverDate = "2024-09-23"; + const legacy = nodeCompat === true; + let mode: NodeJSCompatMode = null; + if ( + hasNodejsCompatV2Flag || + (hasNodejsCompatFlag && + compatibilityDate >= nodeCompatSwitchOverDate && + !hasNoNodejsCompatV2Flag) + ) { + mode = "v2"; + } else if (hasNodejsCompatFlag) { + mode = "v1"; + } else if (hasNodejsAlsFlag) { + mode = "als"; + } else if (legacy) { + mode = "legacy"; + } + + return { + mode, + hasNodejsAlsFlag, + hasNodejsCompatFlag, + hasNodejsCompatV2Flag, + hasNoNodejsCompatV2Flag, + hasExperimentalNodejsCompatV2Flag, + }; +} + +function parseNodeCompatibilityFlags(compatibilityFlags: string[]) { + return { + hasNodejsAlsFlag: compatibilityFlags.includes("nodejs_als"), + hasNodejsCompatFlag: compatibilityFlags.includes("nodejs_compat"), + hasNodejsCompatV2Flag: compatibilityFlags.includes("nodejs_compat_v2"), + hasNoNodejsCompatV2Flag: compatibilityFlags.includes("no_nodejs_compat_v2"), + hasExperimentalNodejsCompatV2Flag: compatibilityFlags.includes( + "experimental:nodejs_compat_v2" + ), + }; +} diff --git a/packages/miniflare/src/plugins/index.ts b/packages/miniflare/src/plugins/index.ts index 3673ea7bcbf7..7175d6ebe9b0 100644 --- a/packages/miniflare/src/plugins/index.ts +++ b/packages/miniflare/src/plugins/index.ts @@ -103,6 +103,7 @@ export { ProxyClient, getFreshSourceMapSupport, kCurrentWorker, + getNodeCompat, } from "./core"; export type { CompiledModuleRule, @@ -111,6 +112,7 @@ export type { ModuleDefinition, GlobalServicesOptions, SourceOptions, + NodeJSCompatMode, } from "./core"; export type * from "./core/proxy/types"; export * from "./d1"; diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index fa0912fc2912..07709626448f 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { readConfig } from "../config"; import { normalizeAndValidateConfig } from "../config/validation"; -import { normalizeSlashes } from "./helpers/mock-console"; +import { normalizeString } from "./helpers/normalize"; import { runInTempDir } from "./helpers/run-in-tmp"; import { writeWranglerToml } from "./helpers/write-wrangler-toml"; import type { @@ -301,7 +301,7 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasErrors()).toBe(false); expect(diagnostics.hasWarnings()).toBe(true); - expect(normalizeSlashes(diagnostics.renderWarnings())) + expect(normalizeString(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing wrangler configuration: - Deprecation: \\"site.entry-point\\": @@ -332,7 +332,7 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasWarnings()).toBe(true); expect(diagnostics.hasErrors()).toBe(true); - expect(normalizeSlashes(diagnostics.renderWarnings())) + expect(normalizeString(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing wrangler configuration: - Deprecation: \\"site.entry-point\\": @@ -375,7 +375,7 @@ describe("normalizeAndValidateConfig()", () => { - Expected \\"site.entry-point\\" to be of type string but got 111." `); - expect(normalizeSlashes(diagnostics.renderWarnings())) + expect(normalizeString(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing wrangler configuration: - Deprecation: \\"site.entry-point\\": @@ -409,7 +409,7 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasWarnings()).toBe(true); expect(diagnostics.hasErrors()).toBe(false); - expect(normalizeSlashes(diagnostics.renderWarnings())) + expect(normalizeString(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing wrangler configuration: - Deprecation: \\"site.entry-point\\": @@ -881,7 +881,7 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasErrors()).toBe(false); expect(diagnostics.hasWarnings()).toBe(true); - expect(normalizeSlashes(diagnostics.renderWarnings())) + expect(normalizeString(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing project/wrangler.toml configuration: - \\"unsafe\\" fields are experimental and may change or break at any time. diff --git a/packages/wrangler/src/__tests__/deploy.test.ts b/packages/wrangler/src/__tests__/deploy.test.ts index 5111188eed90..c2abede00275 100644 --- a/packages/wrangler/src/__tests__/deploy.test.ts +++ b/packages/wrangler/src/__tests__/deploy.test.ts @@ -18,11 +18,7 @@ import { logger } from "../logger"; import { writeAuthConfigFile } from "../user"; import { mockAccountId, mockApiToken } from "./helpers/mock-account-id"; import { mockAuthDomain } from "./helpers/mock-auth-domain"; -import { - mockConsoleMethods, - normalizeSlashes, - normalizeTempDirs, -} from "./helpers/mock-console"; +import { mockConsoleMethods } from "./helpers/mock-console"; import { clearDialogs, mockConfirm } from "./helpers/mock-dialogs"; import { mockGetZoneFromHostRequest } from "./helpers/mock-get-zone-from-host"; import { useMockIsTTY } from "./helpers/mock-istty"; @@ -45,6 +41,7 @@ import { mswSuccessUserHandlers, } from "./helpers/msw"; import { mswListNewDeploymentsLatestFull } from "./helpers/msw/handlers/versions"; +import { normalizeString } from "./helpers/normalize"; import { runInTempDir } from "./helpers/run-in-tmp"; import { runWrangler } from "./helpers/run-wrangler"; import { writeWorkerSource } from "./helpers/write-worker-source"; @@ -2053,7 +2050,7 @@ addEventListener('fetch', event => {});` " `); - expect(normalizeSlashes(std.warn)).toMatchInlineSnapshot(` + expect(normalizeString(std.warn)).toMatchInlineSnapshot(` "▲ [WARNING] Processing wrangler.toml configuration: - Because you've defined a [site] configuration, we're defaulting to \\"workers-site\\" for the @@ -2169,7 +2166,7 @@ addEventListener('fetch', event => {});` Current Version ID: Galaxy-Class" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(normalizeSlashes(std.warn)).toMatchInlineSnapshot(` + expect(normalizeString(std.warn)).toMatchInlineSnapshot(` "▲ [WARNING] Processing my-site/wrangler.toml configuration: - Deprecation: \\"site.entry-point\\": @@ -5679,8 +5676,8 @@ addEventListener('fetch', event => {});` mockUploadWorkerRequest(); await runWrangler("build"); - const outFile = normalizeSlashes( - normalizeTempDirs(fs.readFileSync("dist/index.js", "utf-8")) + const outFile = normalizeString( + fs.readFileSync("dist/index.js", "utf-8") ); // We don't check against the whole file as there is middleware being injected @@ -5715,8 +5712,8 @@ addEventListener('fetch', event => {});` mockUploadWorkerRequest(); await runWrangler("build --env staging"); - const outFile = normalizeSlashes( - normalizeTempDirs(fs.readFileSync("dist/index.js", "utf-8")) + const outFile = normalizeString( + fs.readFileSync("dist/index.js", "utf-8") ); // We don't check against the whole file as there is middleware being injected @@ -9256,27 +9253,139 @@ export default{ `); }); - it("should recommend node compatibility mode when using node builtins and node-compat isn't enabled", async () => { + it("should recommend node compatibility flag when using node builtins and no node compat is enabled", async () => { writeWranglerToml(); - fs.writeFileSync( - "index.js", - ` - import path from 'path'; - console.log(path.join("some/path/to", "a/file.txt")); - export default {} - ` - ); - let err: esbuild.BuildFailure | undefined; - try { - await runWrangler("deploy index.js --dry-run"); // expecting this to throw, as node compatibility isn't enabled - } catch (e) { - err = e as esbuild.BuildFailure; - } - expect( - esbuild.formatMessagesSync(err?.errors ?? [], { kind: "error" }).join() - ).toMatch( - /The package "path" wasn't found on the file system but is built into node\.\s+Add "node_compat = true" to your wrangler\.toml file and make sure to prefix the module name with "node:" to enable Node.js compatibility\./ - ); + fs.writeFileSync("index.js", "import path from 'path';"); + + await expect( + runWrangler("deploy index.js --dry-run").catch((e) => + normalizeString( + esbuild + .formatMessagesSync(e?.errors ?? [], { kind: "error" }) + .join() + .trim() + ) + ) + ).resolves.toMatchInlineSnapshot(` + "X [ERROR] Could not resolve \\"path\\" + + index.js:1:17: + 1 │ import path from 'path'; + ╵ ~~~~~~ + + The package \\"path\\" wasn't found on the file system but is built into node. + - Add the \\"nodejs_compat\\" compatibility flag to your project." + `); + }); + + it("should recommend node compatibility flag when using node builtins and node compat is set only to nodejs_als", async () => { + writeWranglerToml({ + compatibility_flags: ["nodejs_als"], + }); + fs.writeFileSync("index.js", "import path from 'path';"); + + await expect( + runWrangler("deploy index.js --dry-run").catch((e) => + normalizeString( + esbuild + .formatMessagesSync(e?.errors ?? [], { kind: "error" }) + .join() + .trim() + ) + ) + ).resolves.toMatchInlineSnapshot(` + "X [ERROR] Could not resolve \\"path\\" + + index.js:1:17: + 1 │ import path from 'path'; + ╵ ~~~~~~ + + The package \\"path\\" wasn't found on the file system but is built into node. + - Add the \\"nodejs_compat\\" compatibility flag to your project." + `); + }); + + it("should recommend node compatibility flag when using node builtins and `node_compat` is true", async () => { + writeWranglerToml({ + node_compat: true, + }); + fs.writeFileSync("index.js", "import fs from 'diagnostics_channel';"); + + await expect( + runWrangler("deploy index.js --dry-run").catch((e) => + normalizeString( + esbuild + .formatMessagesSync(e?.errors ?? [], { kind: "error" }) + .join() + .trim() + ) + ) + ).resolves.toMatchInlineSnapshot(` + "X [ERROR] Could not resolve \\"diagnostics_channel\\" + + index.js:1:15: + 1 │ import fs from 'diagnostics_channel'; + ╵ ~~~~~~~~~~~~~~~~~~~~~ + + The package \\"diagnostics_channel\\" wasn't found on the file system but is built into node. + - Try removing the legacy \\"node_compat\\" setting and add the \\"nodejs_compat\\" compatibility flag in your project" + `); + }); + + it("should recommend updating the compatibility date when using node builtins and the `nodejs_compat` flag", async () => { + writeWranglerToml({ + compatibility_date: "2024-09-01", // older than Sept 23rd, 2024 + compatibility_flags: ["nodejs_compat"], + }); + fs.writeFileSync("index.js", "import fs from 'path';"); + + await expect( + runWrangler("deploy index.js --dry-run").catch((e) => + normalizeString( + esbuild + .formatMessagesSync(e?.errors ?? [], { kind: "error" }) + .join() + .trim() + ) + ) + ).resolves.toMatchInlineSnapshot(` + "X [ERROR] Could not resolve \\"path\\" + + index.js:1:15: + 1 │ import fs from 'path'; + ╵ ~~~~~~ + + The package \\"path\\" wasn't found on the file system but is built into node. + - Make sure to prefix the module name with \\"node:\\" or update your compatibility_date to 2024-09-23 or later." + `); + }); + + it("should recommend updating the compatibility date flag when using no_nodejs_compat and non-prefixed node builtins", async () => { + writeWranglerToml({ + compatibility_date: "2024-09-23", + compatibility_flags: ["nodejs_compat", "no_nodejs_compat_v2"], + }); + fs.writeFileSync("index.js", "import fs from 'path';"); + + await expect( + runWrangler("deploy index.js --dry-run").catch((e) => + normalizeString( + esbuild + .formatMessagesSync(e?.errors ?? [], { kind: "error" }) + .join() + .trim() + ) + ) + ).resolves.toMatchInlineSnapshot(` + "X [ERROR] Could not resolve \\"path\\" + + index.js:1:15: + 1 │ import fs from 'path'; + ╵ ~~~~~~ + + The package \\"path\\" wasn't found on the file system but is built into node. + - Make sure to prefix the module name with \\"node:\\" or update your compatibility_date to 2024-09-23 or later." + `); }); it("should polyfill node builtins when enabled", async () => { @@ -9330,13 +9439,45 @@ export default{ `); }); - it('when present, should support any "external" `node:*` imports', async () => { + it('when present, should support "external" `node:*` imports', async () => { writeWranglerToml(); fs.writeFileSync( "index.js", ` - import AsyncHooks from 'node:async_hooks'; - console.log(AsyncHooks); + import path from 'node:path'; + console.log(path); + export default {} + ` + ); + + await runWrangler( + "deploy index.js --dry-run --outdir=dist --compatibility-flag=nodejs_compat" + ); + + expect(std).toMatchInlineSnapshot(` + Object { + "debug": "", + "err": "", + "info": "", + "out": "Total Upload: xx KiB / gzip: xx KiB + --dry-run: exiting now.", + "warn": "", + } + `); + expect(fs.readFileSync("dist/index.js", { encoding: "utf-8" })).toContain( + `import path from "node:path";` + ); + }); + + it(`when present, and compat date is on or after 2024-09-23, should support "external" non-prefixed node imports`, async () => { + writeWranglerToml({ + compatibility_date: "2024-09-23", + }); + fs.writeFileSync( + "index.js", + ` + import path from 'path'; + console.log(path); export default {} ` ); @@ -9356,7 +9497,7 @@ export default{ } `); expect(fs.readFileSync("dist/index.js", { encoding: "utf-8" })).toContain( - `import AsyncHooks from "node:async_hooks";` + `import path from "path";` ); }); diff --git a/packages/wrangler/src/__tests__/helpers/mock-console.ts b/packages/wrangler/src/__tests__/helpers/mock-console.ts index fe115f4993a1..3e559f5c0dab 100644 --- a/packages/wrangler/src/__tests__/helpers/mock-console.ts +++ b/packages/wrangler/src/__tests__/helpers/mock-console.ts @@ -1,6 +1,7 @@ import * as util from "node:util"; import { afterEach, beforeEach, vi } from "vitest"; import { logger } from "../../logger"; +import { normalizeString } from "./normalize"; import type { MockInstance } from "vitest"; /** @@ -33,13 +34,7 @@ const std = { }; function normalizeOutput(spy: MockInstance): string { - return normalizeErrorMarkers( - replaceByte( - stripTrailingWhitespace( - normalizeSlashes(normalizeTempDirs(stripTimings(captureCalls(spy)))) - ) - ) - ); + return normalizeString(captureCalls(spy)); } function captureCalls(spy: MockInstance): string { @@ -66,49 +61,3 @@ export function mockConsoleMethods() { }); return std; } - -/** - * Normalize error `X` markers. - * - * Windows gets a different character. - */ -function normalizeErrorMarkers(str: string): string { - return str.replaceAll("✘", "X"); -} - -/** - * Ensure slashes in the `str` are OS file-system agnostic. - * - * Use this in snapshot tests to be resilient to file-system differences. - */ -export function normalizeSlashes(str: string): string { - return str.replace(/\\/g, "/"); -} - -/** - * Strip "timing data" out of the `stdout` string, since this is not always deterministic. - * - * Use this in snapshot tests to be resilient to slight changes in timing of processing. - */ -export function stripTimings(stdout: string): string { - return stdout.replace(/\(\d+\.\d+ sec\)/g, "(TIMINGS)"); -} - -export function stripTrailingWhitespace(str: string): string { - return str.replace(/[^\S\n]+\n/g, "\n"); -} - -/** - * Removing leading kilobit (tenth of a byte) from test output due to - * variation causing every few tests the value to change by Âħ .01 - */ -function replaceByte(stdout: string): string { - return stdout.replaceAll(/\d+\.\d+ KiB/g, "xx KiB"); -} - -/** - * Temp directories are created with random names, so we replace all comments temp dirs in them - */ -export function normalizeTempDirs(stdout: string): string { - return stdout.replaceAll(/\/\/.+\/tmp.+/g, "//tmpdir"); -} diff --git a/packages/wrangler/src/__tests__/helpers/normalize.ts b/packages/wrangler/src/__tests__/helpers/normalize.ts new file mode 100644 index 000000000000..b07664de31b7 --- /dev/null +++ b/packages/wrangler/src/__tests__/helpers/normalize.ts @@ -0,0 +1,58 @@ +/** + * Normalize the input string, to make it reliable to use in tests. + */ +export function normalizeString(input: string): string { + return normalizeErrorMarkers( + replaceByte( + stripTrailingWhitespace( + normalizeSlashes(normalizeTempDirs(stripTimings(input))) + ) + ) + ); +} + +/** + * Normalize error `X` markers. + * + * Windows gets a different character. + */ +function normalizeErrorMarkers(str: string): string { + return str.replaceAll("✘", "X"); +} + +/** + * Ensure slashes in the `str` are OS file-system agnostic. + * + * Use this in snapshot tests to be resilient to file-system differences. + */ +function normalizeSlashes(str: string): string { + return str.replace(/\\/g, "/"); +} + +/** + * Strip "timing data" out of the `stdout` string, since this is not always deterministic. + * + * Use this in snapshot tests to be resilient to slight changes in timing of processing. + */ +function stripTimings(stdout: string): string { + return stdout.replace(/\(\d+\.\d+ sec\)/g, "(TIMINGS)"); +} + +function stripTrailingWhitespace(str: string): string { + return str.replace(/[^\S\n]+\n/g, "\n"); +} + +/** + * Removing leading kilobit (tenth of a byte) from test output due to + * variation causing every few tests the value to change by Âħ .01 + */ +function replaceByte(stdout: string): string { + return stdout.replaceAll(/\d+\.\d+ KiB/g, "xx KiB"); +} + +/** + * Temp directories are created with random names, so we replace all comments temp dirs in them + */ +function normalizeTempDirs(stdout: string): string { + return stdout.replaceAll(/\/\/.+\/tmp.+/g, "//tmpdir"); +} diff --git a/packages/wrangler/src/__tests__/helpers/run-wrangler.ts b/packages/wrangler/src/__tests__/helpers/run-wrangler.ts index d189832401a6..6aa918377380 100644 --- a/packages/wrangler/src/__tests__/helpers/run-wrangler.ts +++ b/packages/wrangler/src/__tests__/helpers/run-wrangler.ts @@ -1,6 +1,6 @@ import { main } from "../../index"; import * as shellquote from "../../utils/shell-quote"; -import { normalizeSlashes, stripTimings } from "./mock-console"; +import { normalizeString } from "./normalize"; /** * A helper to 'run' wrangler commands for tests. @@ -16,7 +16,7 @@ export async function runWrangler( await main(argv); } catch (err) { if (err instanceof Error) { - err.message = normalizeSlashes(stripTimings(err.message)); + err.message = normalizeString(err.message); } throw err; } finally { diff --git a/packages/wrangler/src/__tests__/user.test.ts b/packages/wrangler/src/__tests__/user.test.ts index 8381c909c72a..24d9be6ab064 100644 --- a/packages/wrangler/src/__tests__/user.test.ts +++ b/packages/wrangler/src/__tests__/user.test.ts @@ -9,7 +9,7 @@ import { requireAuth, writeAuthConfigFile, } from "../user"; -import { mockConsoleMethods, normalizeSlashes } from "./helpers/mock-console"; +import { mockConsoleMethods } from "./helpers/mock-console"; import { useMockIsTTY } from "./helpers/mock-istty"; import { mockExchangeRefreshTokenForAccessToken, @@ -20,6 +20,7 @@ import { mswSuccessOauthHandlers, mswSuccessUserHandlers, } from "./helpers/msw"; +import { normalizeString } from "./helpers/normalize"; import { runInTempDir } from "./helpers/run-in-tmp"; import { runWrangler } from "./helpers/run-wrangler"; import type { Config } from "../config"; @@ -109,8 +110,8 @@ describe("User", () => { Successfully logged in." `); - expect(normalizeSlashes(getAuthConfigFilePath())).toBe( - normalizeSlashes(`${getGlobalWranglerConfigPath()}/config/staging.toml`) + expect(normalizeString(getAuthConfigFilePath())).toBe( + normalizeString(`${getGlobalWranglerConfigPath()}/config/staging.toml`) ); expect(readAuthConfigFile()).toEqual({ api_token: undefined, @@ -174,8 +175,8 @@ describe("User", () => { refresh_token: "Order 66", }); - expect(normalizeSlashes(getAuthConfigFilePath())).toBe( - normalizeSlashes(`${getGlobalWranglerConfigPath()}/config/staging.toml`) + expect(normalizeString(getAuthConfigFilePath())).toBe( + normalizeString(`${getGlobalWranglerConfigPath()}/config/staging.toml`) ); }); }); diff --git a/packages/wrangler/src/api/pages/deploy.tsx b/packages/wrangler/src/api/pages/deploy.tsx index b1e270881b95..e8b3333f294f 100644 --- a/packages/wrangler/src/api/pages/deploy.tsx +++ b/packages/wrangler/src/api/pages/deploy.tsx @@ -6,7 +6,7 @@ import { cwd } from "node:process"; import { File, FormData } from "undici"; import { fetchResult } from "../../cfetch"; import { readConfig } from "../../config"; -import { getNodeCompatMode } from "../../deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "../../deployment-bundle/node-compat"; import { FatalError } from "../../errors"; import { logger } from "../../logger"; import { isNavigatorDefined } from "../../navigator-user-agent"; @@ -174,7 +174,8 @@ export async function deploy({ } } - const nodejsCompatMode = getNodeCompatMode( + const nodejsCompatMode = validateNodeCompatMode( + config?.compatibility_date ?? deploymentConfig.compatibility_date, config?.compatibility_flags ?? deploymentConfig.compatibility_flags ?? [], { nodeCompat: false, diff --git a/packages/wrangler/src/api/startDevWorker/types.ts b/packages/wrangler/src/api/startDevWorker/types.ts index 433b4b4f2ba7..db760a95627f 100644 --- a/packages/wrangler/src/api/startDevWorker/types.ts +++ b/packages/wrangler/src/api/startDevWorker/types.ts @@ -7,7 +7,6 @@ import type { ZoneIdRoute, ZoneNameRoute, } from "../../config/environment"; -import type { NodeJSCompatMode } from "../../deployment-bundle/node-compat"; import type { CfAnalyticsEngineDataset, CfD1Database, @@ -35,6 +34,7 @@ import type { DispatchFetch, Json, Miniflare, + NodeJSCompatMode, Request, Response, } from "miniflare"; diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index 2c3c4bc21b18..4f355826ac7d 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -22,7 +22,7 @@ import { createModuleCollector, getWrangler1xLegacyModuleReferences, } from "../deployment-bundle/module-collection"; -import { getNodeCompatMode } from "../deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "../deployment-bundle/node-compat"; import { loadSourceMaps } from "../deployment-bundle/source-maps"; import { confirm } from "../dialogs"; import { getMigrationsToUpload } from "../durable"; @@ -440,12 +440,18 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m const minify = props.minify ?? config.minify; + const compatibilityDate = + props.compatibilityDate ?? config.compatibility_date; const compatibilityFlags = props.compatibilityFlags ?? config.compatibility_flags; - const nodejsCompatMode = getNodeCompatMode(compatibilityFlags, { - nodeCompat: props.nodeCompat ?? config.node_compat, - noBundle: props.noBundle ?? config.no_bundle, - }); + const nodejsCompatMode = validateNodeCompatMode( + compatibilityDate, + compatibilityFlags, + { + nodeCompat: props.nodeCompat ?? config.node_compat, + noBundle: props.noBundle ?? config.no_bundle, + } + ); // Warn if user tries minify with no-bundle if (props.noBundle && minify) { diff --git a/packages/wrangler/src/deployment-bundle/build-failures.ts b/packages/wrangler/src/deployment-bundle/build-failures.ts index 11a2bd23c2c0..3d1c508a6188 100644 --- a/packages/wrangler/src/deployment-bundle/build-failures.ts +++ b/packages/wrangler/src/deployment-bundle/build-failures.ts @@ -1,5 +1,6 @@ import { builtinModules } from "node:module"; import type * as esbuild from "esbuild"; +import type { NodeJSCompatMode } from "miniflare"; /** * RegExp matching against esbuild's error text when it is unable to resolve @@ -19,23 +20,25 @@ const nodeBuiltinResolveErrorText = new RegExp( */ export function rewriteNodeCompatBuildFailure( errors: esbuild.Message[], - forPages = false + compatMode: NodeJSCompatMode = null ) { for (const error of errors) { const match = nodeBuiltinResolveErrorText.exec(error.text); if (match !== null) { - const issue = `The package "${match[1]}" wasn't found on the file system but is built into node.`; + let text = `The package "${match[1]}" wasn't found on the file system but is built into node.\n`; - const instructionForUser = `${ - forPages - ? 'Add the "nodejs_compat" compatibility flag to your Pages project' - : 'Add "node_compat = true" to your wrangler.toml file' - } and make sure to prefix the module name with "node:" to enable Node.js compatibility.`; + if (compatMode === null || compatMode === "als") { + text += `- Add the "nodejs_compat" compatibility flag to your project.\n`; + } else if (compatMode === "legacy") { + text += `- Try removing the legacy "node_compat" setting and add the "nodejs_compat" compatibility flag in your project\n`; + } else if (compatMode === "v1" && !match[1].startsWith("node:")) { + text += `- Make sure to prefix the module name with "node:" or update your compatibility_date to 2024-09-23 or later.\n`; + } error.notes = [ { location: null, - text: `${issue}\n${instructionForUser}`, + text, }, ]; } diff --git a/packages/wrangler/src/deployment-bundle/bundle.ts b/packages/wrangler/src/deployment-bundle/bundle.ts index 24765fc39379..b676e906d87e 100644 --- a/packages/wrangler/src/deployment-bundle/bundle.ts +++ b/packages/wrangler/src/deployment-bundle/bundle.ts @@ -29,8 +29,8 @@ import type { DurableObjectBindings } from "../config/environment"; import type { MiddlewareLoader } from "./apply-middleware"; import type { Entry } from "./entry"; import type { ModuleCollector } from "./module-collection"; -import type { NodeJSCompatMode } from "./node-compat"; import type { CfModule, CfModuleType } from "./worker"; +import type { NodeJSCompatMode } from "miniflare"; // Taken from https://stackoverflow.com/a/3561711 // which is everything from the tc39 proposal, plus the following two characters: ^/ @@ -133,7 +133,6 @@ export type BundleOptions = { sourcemap?: esbuild.CommonOptions["sourcemap"]; plugins?: esbuild.Plugin[]; isOutfile?: boolean; - forPages?: boolean; local: boolean; projectRoot: string | undefined; defineNavigatorUserAgent: boolean; @@ -172,7 +171,6 @@ export async function bundleWorker( sourcemap, plugins, isOutfile, - forPages, local, projectRoot, defineNavigatorUserAgent, @@ -485,8 +483,8 @@ export async function bundleWorker( }; } } catch (e) { - if (nodejsCompatMode !== "legacy" && isBuildFailure(e)) { - rewriteNodeCompatBuildFailure(e.errors, forPages); + if (isBuildFailure(e)) { + rewriteNodeCompatBuildFailure(e.errors, nodejsCompatMode); } throw e; } diff --git a/packages/wrangler/src/deployment-bundle/esbuild-plugins/log-build-output.ts b/packages/wrangler/src/deployment-bundle/esbuild-plugins/log-build-output.ts index c59497588a9b..783f7cdca6cc 100644 --- a/packages/wrangler/src/deployment-bundle/esbuild-plugins/log-build-output.ts +++ b/packages/wrangler/src/deployment-bundle/esbuild-plugins/log-build-output.ts @@ -1,7 +1,7 @@ import { logBuildFailure, logBuildWarnings } from "../../logger"; import { rewriteNodeCompatBuildFailure } from "../build-failures"; -import type { NodeJSCompatMode } from "../node-compat"; import type { Plugin } from "esbuild"; +import type { NodeJSCompatMode } from "miniflare"; /** * Log esbuild warnings and errors @@ -22,7 +22,7 @@ export function logBuildOutput( build.onEnd(async ({ errors, warnings }) => { if (errors.length > 0) { if (nodejsCompatMode !== "legacy") { - rewriteNodeCompatBuildFailure(errors); + rewriteNodeCompatBuildFailure(errors, nodejsCompatMode); } logBuildFailure(errors, warnings); return; diff --git a/packages/wrangler/src/deployment-bundle/node-compat.ts b/packages/wrangler/src/deployment-bundle/node-compat.ts index d5a0baa04dd8..ed2893e48f9c 100644 --- a/packages/wrangler/src/deployment-bundle/node-compat.ts +++ b/packages/wrangler/src/deployment-bundle/node-compat.ts @@ -1,71 +1,40 @@ +import { getNodeCompat } from "miniflare"; import { UserError } from "../errors"; import { logger } from "../logger"; +import type { NodeJSCompatMode } from "miniflare"; /** - * Wrangler can provide Node.js compatibility in a number of different modes: - * - "legacy" - this mode adds compile-time polyfills that are not well maintained and cannot work with workerd runtime builtins. - * - "als": this mode tells the workerd runtime to enable only the Async Local Storage builtin library (accessible via `node:async_hooks`). - * - "v1" - this mode tells the workerd runtime to enable some Node.js builtin libraries (accessible only via `node:...` imports) but no globals. - * - "v2" - this mode tells the workerd runtime to enable more Node.js builtin libraries (accessible both with and without the `node:` prefix) - * and also some Node.js globals such as `Buffer`; it also turns on additional compile-time polyfills for those that are not provided by the runtime. - */ -export type NodeJSCompatMode = "legacy" | "als" | "v1" | "v2" | null; - -/** - * Computes the Node.js compatibility mode we are running. - * - * NOTE: - * Currently v2 mode is configured via `nodejs_compat_v2` compat flag. - * At a future compatibility date, the use of `nodejs_compat` flag will imply `nodejs_compat_v2`. + * Computes and validates the Node.js compatibility mode we are running. * - * see `EnvironmentInheritable` for `nodeCompat` and `noBundle`. + * NOTES: + * - The v2 mode is configured via `nodejs_compat_v2` compat flag or via `nodejs_compat` plus a compatibility date of Sept 23rd. 2024 or later. + * - See `EnvironmentInheritable` for `nodeCompat` and `noBundle`. * + * @param compatibilityDateStr The compatibility date * @param compatibilityFlags The compatibility flags - * @param validateConfig Whether to validate the config (logs and throws) * @param nodeCompat Whether to add polyfills for node builtin modules and globals * @param noBundle Whether to skip internal build steps and directly deploy script - * @returns one of: - * - "legacy": build-time polyfills, from `node_compat` flag - * - "als": nodejs_als compatibility flag - * - "v1": nodejs_compat compatibility flag - * - "v2": nodejs_compat_v2 compatibility flag - * - null: no Node.js compatibility - */ -export function getNodeCompatMode( + * + */ export function validateNodeCompatMode( + compatibilityDateStr: string = "2000-01-01", // Default to some arbitrary old date compatibilityFlags: string[], { - validateConfig = true, - nodeCompat = undefined, + nodeCompat: legacy = false, noBundle = undefined, }: { - validateConfig?: boolean; nodeCompat?: boolean; noBundle?: boolean; } ): NodeJSCompatMode { const { + mode, hasNodejsAlsFlag, hasNodejsCompatFlag, hasNodejsCompatV2Flag, hasExperimentalNodejsCompatV2Flag, - } = parseNodeCompatibilityFlags(compatibilityFlags); - - const legacy = nodeCompat === true; - let mode: NodeJSCompatMode = null; - if (hasNodejsCompatV2Flag) { - mode = "v2"; - } else if (hasNodejsCompatFlag) { - mode = "v1"; - } else if (hasNodejsAlsFlag) { - mode = "als"; - } else if (legacy) { - mode = "legacy"; - } - - if (validateConfig !== true) { - // Skip the validation. - return mode; - } + } = getNodeCompat(compatibilityDateStr, compatibilityFlags, { + nodeCompat: legacy, + }); if (hasExperimentalNodejsCompatV2Flag) { throw new UserError( @@ -113,14 +82,3 @@ export function getNodeCompatMode( return mode; } - -function parseNodeCompatibilityFlags(compatibilityFlags: string[]) { - return { - hasNodejsAlsFlag: compatibilityFlags.includes("nodejs_als"), - hasNodejsCompatFlag: compatibilityFlags.includes("nodejs_compat"), - hasNodejsCompatV2Flag: compatibilityFlags.includes("nodejs_compat_v2"), - hasExperimentalNodejsCompatV2Flag: compatibilityFlags.includes( - "experimental:nodejs_compat_v2" - ), - }; -} diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 53c98cb7482f..072b80ff4147 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -14,7 +14,7 @@ import { processAssetsArg, validateAssetsArgsAndConfig } from "./assets"; import { findWranglerToml, printBindings, readConfig } from "./config"; import { validateRoutes } from "./deploy/deploy"; import { getEntry } from "./deployment-bundle/entry"; -import { getNodeCompatMode } from "./deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "./deployment-bundle/node-compat"; import { getBoundRegisteredWorkers } from "./dev-registry"; import Dev, { devRegistry } from "./dev/dev"; import { getVarsForDev } from "./dev/dev-vars"; @@ -685,7 +685,8 @@ export async function startDev(args: StartDevOptions) { moduleRoot: args.moduleRoot, moduleRules: args.rules, nodejsCompatMode: (parsedConfig: Config) => - getNodeCompatMode( + validateNodeCompatMode( + args.compatibilityDate ?? parsedConfig.compatibility_date, args.compatibilityFlags ?? parsedConfig.compatibility_flags ?? [], { nodeCompat: args.nodeCompat ?? parsedConfig.node_compat, @@ -889,7 +890,8 @@ export async function startDev(args: StartDevOptions) { additionalModules, } = devServerSettings; - const nodejsCompatMode = getNodeCompatMode( + const nodejsCompatMode = validateNodeCompatMode( + args.compatibilityDate ?? config.compatibility_date, args.compatibilityFlags ?? config.compatibility_flags ?? [], { nodeCompat: args.nodeCompat ?? config.node_compat, @@ -1050,7 +1052,8 @@ export async function startApiDev(args: StartDevOptions) { additionalModules, } = await validateDevServerSettings(args, config); - const nodejsCompatMode = getNodeCompatMode( + const nodejsCompatMode = validateNodeCompatMode( + args.compatibilityDate ?? config.compatibility_date, args.compatibilityFlags ?? config.compatibility_flags, { nodeCompat: args.nodeCompat ?? config.node_compat, diff --git a/packages/wrangler/src/dev/dev.tsx b/packages/wrangler/src/dev/dev.tsx index 1cde8802c37f..989b7ea70e31 100644 --- a/packages/wrangler/src/dev/dev.tsx +++ b/packages/wrangler/src/dev/dev.tsx @@ -48,7 +48,6 @@ import type { AssetsOptions } from "../assets"; import type { Config } from "../config"; import type { Route } from "../config/environment"; import type { Entry } from "../deployment-bundle/entry"; -import type { NodeJSCompatMode } from "../deployment-bundle/node-compat"; import type { CfModule, CfWorkerInit } from "../deployment-bundle/worker"; import type { StartDevOptions } from "../dev"; import type { WorkerRegistry } from "../dev-registry"; @@ -56,6 +55,7 @@ import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli/ty import type { EphemeralDirectory } from "../paths"; import type { LegacyAssetPaths } from "../sites"; import type { EsbuildBundle } from "./use-esbuild"; +import type { NodeJSCompatMode } from "miniflare"; /** * This hooks establishes a connection with the dev registry, diff --git a/packages/wrangler/src/dev/start-server.ts b/packages/wrangler/src/dev/start-server.ts index 29ef74065922..4ba59da72251 100644 --- a/packages/wrangler/src/dev/start-server.ts +++ b/packages/wrangler/src/dev/start-server.ts @@ -39,12 +39,12 @@ import type { ProxyData, StartDevWorkerInput, Trigger } from "../api"; import type { Config } from "../config"; import type { DurableObjectBindings } from "../config/environment"; import type { Entry } from "../deployment-bundle/entry"; -import type { NodeJSCompatMode } from "../deployment-bundle/node-compat"; import type { CfModule } from "../deployment-bundle/worker"; import type { WorkerRegistry } from "../dev-registry"; import type { DevProps } from "./dev"; import type { LocalProps } from "./local"; import type { EsbuildBundle } from "./use-esbuild"; +import type { NodeJSCompatMode } from "miniflare"; export async function startDevServer( props: DevProps & { diff --git a/packages/wrangler/src/dev/use-esbuild.ts b/packages/wrangler/src/dev/use-esbuild.ts index 2ba0016c9358..7a073e27997b 100644 --- a/packages/wrangler/src/dev/use-esbuild.ts +++ b/packages/wrangler/src/dev/use-esbuild.ts @@ -17,9 +17,9 @@ import { import type { Config } from "../config"; import type { SourceMapMetadata } from "../deployment-bundle/bundle"; import type { Entry } from "../deployment-bundle/entry"; -import type { NodeJSCompatMode } from "../deployment-bundle/node-compat"; import type { CfModule, CfModuleType } from "../deployment-bundle/worker"; import type { Metafile } from "esbuild"; +import type { NodeJSCompatMode } from "miniflare"; export type EsbuildBundle = { id: number; diff --git a/packages/wrangler/src/pages/build.ts b/packages/wrangler/src/pages/build.ts index 006d26d83130..35f7c303b970 100644 --- a/packages/wrangler/src/pages/build.ts +++ b/packages/wrangler/src/pages/build.ts @@ -10,7 +10,7 @@ import path, { import { createUploadWorkerBundleContents } from "../api/pages/create-worker-bundle-contents"; import { readConfig } from "../config"; import { writeAdditionalModules } from "../deployment-bundle/find-additional-modules"; -import { getNodeCompatMode } from "../deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "../deployment-bundle/node-compat"; import { FatalError } from "../errors"; import { logger } from "../logger"; import * as metrics from "../metrics"; @@ -29,11 +29,11 @@ import { } from "./functions/buildWorker"; import type { Config } from "../config"; import type { BundleResult } from "../deployment-bundle/bundle"; -import type { NodeJSCompatMode } from "../deployment-bundle/node-compat"; import type { CommonYargsArgv, StrictYargsOptionsToInterface, } from "../yargs-types"; +import type { NodeJSCompatMode } from "miniflare"; export type PagesBuildArgs = StrictYargsOptionsToInterface; @@ -438,10 +438,14 @@ const validateArgs = async (args: PagesBuildArgs): Promise => { } const { nodeCompat: node_compat, ...argsExceptNodeCompat } = args; - const nodejsCompatMode = getNodeCompatMode(args.compatibilityFlags ?? [], { - nodeCompat: node_compat, - noBundle: config?.no_bundle, - }); + const nodejsCompatMode = validateNodeCompatMode( + args.compatibilityDate ?? config?.compatibility_date, + args.compatibilityFlags ?? config?.compatibility_flags ?? [], + { + nodeCompat: node_compat, + noBundle: config?.no_bundle, + } + ); const defineNavigatorUserAgent = isNavigatorDefined( args.compatibilityDate, diff --git a/packages/wrangler/src/pages/buildFunctions.ts b/packages/wrangler/src/pages/buildFunctions.ts index 9b43a2f0ad49..4f1a2263b695 100644 --- a/packages/wrangler/src/pages/buildFunctions.ts +++ b/packages/wrangler/src/pages/buildFunctions.ts @@ -10,9 +10,9 @@ import { writeRoutesModule } from "./functions/routes"; import { convertRoutesToRoutesJSONSpec } from "./functions/routes-transformation"; import { getPagesTmpDir, RUNNING_BUILDERS } from "./utils"; import type { BundleResult } from "../deployment-bundle/bundle"; -import type { NodeJSCompatMode } from "../deployment-bundle/node-compat"; import type { PagesBuildArgs } from "./build"; import type { Config } from "./functions/routes"; +import type { NodeJSCompatMode } from "miniflare"; /** * Builds a Functions worker based on the functions directory, with filepath and handler based routing. diff --git a/packages/wrangler/src/pages/dev.ts b/packages/wrangler/src/pages/dev.ts index 6d30ccce5050..630e4bc71a41 100644 --- a/packages/wrangler/src/pages/dev.ts +++ b/packages/wrangler/src/pages/dev.ts @@ -7,7 +7,7 @@ import { unstable_dev } from "../api"; import { readConfig } from "../config"; import { isBuildFailure } from "../deployment-bundle/build-failures"; import { esbuildAliasExternalPlugin } from "../deployment-bundle/esbuild-plugins/alias-external"; -import { getNodeCompatMode } from "../deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "../deployment-bundle/node-compat"; import { FatalError } from "../errors"; import { logger } from "../logger"; import * as metrics from "../metrics"; @@ -360,7 +360,8 @@ export const Handler = async (args: PagesDevArguments) => { let scriptPath = ""; - const nodejsCompatMode = getNodeCompatMode( + const nodejsCompatMode = validateNodeCompatMode( + args.compatibilityDate ?? config.compatibility_date, args.compatibilityFlags ?? config.compatibility_flags ?? [], { nodeCompat: args.nodeCompat, diff --git a/packages/wrangler/src/pages/functions/buildPlugin.ts b/packages/wrangler/src/pages/functions/buildPlugin.ts index 4f0d8346e075..867a8dfa3fa9 100644 --- a/packages/wrangler/src/pages/functions/buildPlugin.ts +++ b/packages/wrangler/src/pages/functions/buildPlugin.ts @@ -109,7 +109,6 @@ export function buildPluginFromFunctions({ // TODO: mock AE datasets in Pages functions for dev mockAnalyticsEngineDatasets: [], targetConsumer: local ? "dev" : "deploy", - forPages: true, local, projectRoot: getPagesProjectRoot(), defineNavigatorUserAgent, diff --git a/packages/wrangler/src/pages/functions/buildWorker.ts b/packages/wrangler/src/pages/functions/buildWorker.ts index 9792b4314b39..77a2ab984611 100644 --- a/packages/wrangler/src/pages/functions/buildWorker.ts +++ b/packages/wrangler/src/pages/functions/buildWorker.ts @@ -14,9 +14,9 @@ import { getBasePath } from "../../paths"; import { getPagesProjectRoot, getPagesTmpDir } from "../utils"; import type { BundleResult } from "../../deployment-bundle/bundle"; import type { Entry } from "../../deployment-bundle/entry"; -import type { NodeJSCompatMode } from "../../deployment-bundle/node-compat"; import type { CfModule } from "../../deployment-bundle/worker"; import type { Plugin } from "esbuild"; +import type { NodeJSCompatMode } from "miniflare"; export type Options = { routesModule: string; @@ -85,7 +85,6 @@ export function buildWorkerFromFunctions({ serveLegacyAssetsFromWorker: false, checkFetch: local, targetConsumer: local ? "dev" : "deploy", - forPages: true, local, projectRoot: getPagesProjectRoot(), defineNavigatorUserAgent, @@ -189,7 +188,6 @@ export function buildRawWorker({ serveLegacyAssetsFromWorker: false, checkFetch: local, targetConsumer: local ? "dev" : "deploy", - forPages: true, local, projectRoot: getPagesProjectRoot(), defineNavigatorUserAgent, diff --git a/packages/wrangler/src/type-generation/index.ts b/packages/wrangler/src/type-generation/index.ts index 4a1dd513af05..5ee58a73677a 100644 --- a/packages/wrangler/src/type-generation/index.ts +++ b/packages/wrangler/src/type-generation/index.ts @@ -2,9 +2,9 @@ import * as fs from "node:fs"; import { basename, dirname, extname, join, relative, resolve } from "node:path"; import * as esmLexer from "es-module-lexer"; import { findUpSync } from "find-up"; +import { getNodeCompat } from "miniflare"; import { findWranglerToml, readConfig } from "../config"; import { getEntry } from "../deployment-bundle/entry"; -import { getNodeCompatMode } from "../deployment-bundle/node-compat"; import { getVarsForDev } from "../dev/dev-vars"; import { UserError } from "../errors"; import { CommandLineArgsError } from "../index"; @@ -92,10 +92,13 @@ export async function typesHandler( const tsconfigPath = config.tsconfig ?? join(dirname(configPath), "tsconfig.json"); const tsconfigTypes = readTsconfigTypes(tsconfigPath); - const mode = getNodeCompatMode(config.compatibility_flags, { - validateConfig: false, - nodeCompat: config.node_compat, - }); + const { mode } = getNodeCompat( + config.compatibility_date, + config.compatibility_flags, + { + nodeCompat: config.node_compat, + } + ); logRuntimeTypesMessage(outFile, tsconfigTypes, mode !== null); } diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index 32163ce108cf..4f1dc4971d5c 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -19,7 +19,7 @@ import { createModuleCollector, getWrangler1xLegacyModuleReferences, } from "../deployment-bundle/module-collection"; -import { getNodeCompatMode } from "../deployment-bundle/node-compat"; +import { validateNodeCompatMode } from "../deployment-bundle/node-compat"; import { loadSourceMaps } from "../deployment-bundle/source-maps"; import { confirm } from "../dialogs"; import { getMigrationsToUpload } from "../durable"; @@ -185,7 +185,8 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m const minify = props.minify ?? config.minify; - const nodejsCompatMode = getNodeCompatMode( + const nodejsCompatMode = validateNodeCompatMode( + props.compatibilityDate ?? config.compatibility_date, props.compatibilityFlags ?? config.compatibility_flags, { nodeCompat: props.nodeCompat ?? config.node_compat,