diff --git a/.changeset/dirty-knives-exercise.md b/.changeset/dirty-knives-exercise.md new file mode 100644 index 000000000000..b8be4ee2eabd --- /dev/null +++ b/.changeset/dirty-knives-exercise.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +polish: improve consistency of warnings and errors + +Related to #377 diff --git a/.vscode/settings.json b/.vscode/settings.json index d09be865179d..2313fed83955 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "cfetch", "clipboardy", "cloudflared", + "Codespaces", "esbuild", "eslintcache", "execa", diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index fb7725bdb6e6..428a41250f99 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -323,7 +323,7 @@ describe("normalizeAndValidateConfig()", () => { `); expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` "Processing wrangler configuration: - - 🚨 NO LONGER SUPPORTED: \\"site.entry-point\\": + - NO LONGER SUPPORTED: \\"site.entry-point\\": The \`site.entry-point\` config field is no longer used. The entry-point should be specified via the command line or the \`main\` config field." `); @@ -534,9 +534,9 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasWarnings()).toBe(true); expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(` "Processing wrangler configuration: - - ⚠️ DEPRECATION: \\"type\\": + - DEPRECATION: \\"type\\": DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build. - - ⚠️ DEPRECATION: \\"webpack_config\\": + - DEPRECATION: \\"webpack_config\\": DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build." `); }); @@ -832,15 +832,15 @@ describe("normalizeAndValidateConfig()", () => { expect(normalizePath(diagnostics.renderWarnings())) .toMatchInlineSnapshot(` "Processing project/wrangler.toml configuration: - - ⚠️ DEPRECATION: \\"build.upload.format\\": + - DEPRECATION: \\"build.upload.format\\": The format is inferred automatically from the code. - - ⚠️ DEPRECATION: \\"build.upload.main\\": + - DEPRECATION: \\"build.upload.main\\": Delete the \`build.upload.main\` and \`build.upload.dir\` fields. Then add the top level \`main\` field to your configuration file: \`\`\` main = \\"src/index.ts\\" \`\`\` - - ⚠️ DEPRECATION: \\"build.upload.dir\\": + - DEPRECATION: \\"build.upload.dir\\": Use the top level \\"main\\" field or a command-line argument to specify the entry-point for the Worker. - DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this: \`\`\` @@ -1582,9 +1582,9 @@ describe("normalizeAndValidateConfig()", () => { expect(diagnostics.hasWarnings()).toBe(true); expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(` "Processing wrangler configuration: - - ⚠️ DEPRECATION: \\"zone_id\\": + - DEPRECATION: \\"zone_id\\": This is unnecessary since we can deduce this from routes directly. - - ⚠️ DEPRECATION: \\"experimental_services\\": + - DEPRECATION: \\"experimental_services\\": The \\"experimental_services\\" field is no longer supported. Instead, use [[unsafe.bindings]] to enable experimental features. Add this to your wrangler.toml: \`\`\` [[unsafe.bindings]] @@ -2806,9 +2806,9 @@ describe("normalizeAndValidateConfig()", () => { "Processing wrangler configuration: - \\"env.ENV1\\" environment configuration - - ⚠️ DEPRECATION: \\"zone_id\\": + - DEPRECATION: \\"zone_id\\": This is unnecessary since we can deduce this from routes directly. - - ⚠️ DEPRECATION: \\"experimental_services\\": + - DEPRECATION: \\"experimental_services\\": The \\"experimental_services\\" field is no longer supported. Instead, use [[unsafe.bindings]] to enable experimental features. Add this to your wrangler.toml: \`\`\` [[unsafe.bindings]] diff --git a/packages/wrangler/src/__tests__/dev.test.tsx b/packages/wrangler/src/__tests__/dev.test.tsx index 5dcb9ffe9e69..5bb3c680fffc 100644 --- a/packages/wrangler/src/__tests__/dev.test.tsx +++ b/packages/wrangler/src/__tests__/dev.test.tsx @@ -40,7 +40,7 @@ describe("wrangler dev", () => { expect(std.out).toMatchInlineSnapshot(`""`); expect(std.warn.replaceAll(currentDate, "")) .toMatchInlineSnapshot(` - "No compatibility_date was specified. Using today's date: . + "▲ [WARNING] No compatibility_date was specified. Using today's date: . Add one to your wrangler.toml file: \`\`\` compatibility_date = \\"\\" @@ -49,7 +49,9 @@ describe("wrangler dev", () => { \`\`\` --compatibility-date= \`\`\` - See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information." + See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information. + + " `); expect(std.err).toMatchInlineSnapshot(`""`); }); @@ -77,11 +79,14 @@ describe("wrangler dev", () => { `"Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler dev path/to/script\`) or the \`main\` config field."` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler dev path/to/script\`) or the \`main\` config field. + "X [ERROR] Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler dev path/to/script\`) or the \`main\` config field. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); @@ -389,7 +394,7 @@ describe("wrangler dev", () => { watch_dir: "src", }); expect(std.out).toMatchInlineSnapshot( - `"running: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\""` + `"Running custom build: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\""` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(`""`); @@ -411,7 +416,7 @@ describe("wrangler dev", () => { `); expect(std.out).toMatchInlineSnapshot( - `"running: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js"` + `"Running custom build: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js"` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(`""`); @@ -431,13 +436,15 @@ describe("wrangler dev", () => { ).rejects.toThrowErrorMatchingInlineSnapshot( `"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\""` ); - expect(std.out).toMatchInlineSnapshot( - `"running: node -e \\"console.log('custom build');\\""` - ); + expect(std.out).toMatchInlineSnapshot(` + "Running custom build: node -e \\"console.log('custom build');\\" + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\" + "X [ERROR] Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\" - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -469,9 +476,11 @@ describe("wrangler dev", () => { ); expect(std.out).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Setting upstream-protocol to http is not currently implemented. + "▲ [WARNING] Setting upstream-protocol to http is not currently implemented. If this is required in your project, please add your use case to the following issue: - https://github.com/cloudflare/wrangler2/issues/583." + https://github.com/cloudflare/wrangler2/issues/583. + + " `); expect(std.err).toMatchInlineSnapshot(`""`); }); @@ -595,11 +604,13 @@ describe("wrangler dev", () => { expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost"); expect(std.out).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "WARNING: You have Durable Object bindings, which are not defined locally in the worker being developed. + "▲ [WARNING] WARNING: You have Durable Object bindings that are not defined locally in the worker being developed. Be aware that changes to the data stored in these Durable Objects will be permanent and affect the live instances. Remote Durable Objects that are affected: - {\\"name\\":\\"NAME_2\\",\\"class_name\\":\\"CLASS_2\\",\\"script_name\\":\\"SCRIPT_A\\"} - - {\\"name\\":\\"NAME_4\\",\\"class_name\\":\\"CLASS_4\\",\\"script_name\\":\\"SCRIPT_B\\"}" + - {\\"name\\":\\"NAME_4\\",\\"class_name\\":\\"CLASS_4\\",\\"script_name\\":\\"SCRIPT_B\\"} + + " `); expect(std.err).toMatchInlineSnapshot(`""`); }); diff --git a/packages/wrangler/src/__tests__/guess-worker-format.test.ts b/packages/wrangler/src/__tests__/guess-worker-format.test.ts index 6c44b1ddcbcb..20b5bc909e25 100644 --- a/packages/wrangler/src/__tests__/guess-worker-format.test.ts +++ b/packages/wrangler/src/__tests__/guess-worker-format.test.ts @@ -76,8 +76,10 @@ describe("guess worker format", () => { undefined ); expect(guess).toBe("service-worker"); - expect(std.warn).toMatchInlineSnapshot( - `"The entrypoint index.ts has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using \\"service-worker\\" format..."` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] The entrypoint index.ts has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using \\"service-worker\\" format... + + " + `); }); }); diff --git a/packages/wrangler/src/__tests__/helpers/mock-console.ts b/packages/wrangler/src/__tests__/helpers/mock-console.ts index db2612d80e95..81c1b5fe5286 100644 --- a/packages/wrangler/src/__tests__/helpers/mock-console.ts +++ b/packages/wrangler/src/__tests__/helpers/mock-console.ts @@ -5,11 +5,15 @@ import * as util from "node:util"; * assert on the values they're called with in our tests. */ -let logSpy: jest.SpyInstance, +let debugSpy: jest.SpyInstance, + logSpy: jest.SpyInstance, errorSpy: jest.SpyInstance, warnSpy: jest.SpyInstance; const std = { + get debug() { + return normalizeOutput(debugSpy); + }, get out() { return normalizeOutput(logSpy); }, @@ -22,8 +26,8 @@ const std = { }; function normalizeOutput(spy: jest.SpyInstance): string { - return stripTrailingWhitespace( - normalizeSlashes(stripTimings(captureCalls(spy))) + return normalizeErrorMarkers( + stripTrailingWhitespace(normalizeSlashes(stripTimings(captureCalls(spy)))) ); } @@ -35,11 +39,13 @@ function captureCalls(spy: jest.SpyInstance): string { export function mockConsoleMethods() { beforeEach(() => { + debugSpy = jest.spyOn(console, "debug").mockImplementation(); logSpy = jest.spyOn(console, "log").mockImplementation(); errorSpy = jest.spyOn(console, "error").mockImplementation(); warnSpy = jest.spyOn(console, "warn").mockImplementation(); }); afterEach(() => { + debugSpy.mockRestore(); logSpy.mockRestore(); errorSpy.mockRestore(); warnSpy.mockRestore(); @@ -47,6 +53,15 @@ 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. * diff --git a/packages/wrangler/src/__tests__/https-options.test.ts b/packages/wrangler/src/__tests__/https-options.test.ts index b8d10c80062c..29f0c6f22fcd 100644 --- a/packages/wrangler/src/__tests__/https-options.test.ts +++ b/packages/wrangler/src/__tests__/https-options.test.ts @@ -97,8 +97,10 @@ describe("getHttpsOptions()", () => { `"Generating new self-signed certificate..."` ); expect(std.warn).toMatchInlineSnapshot(` - "Unable to cache generated self-signed certificate in home/.wrangler/local-cert. - ERROR: Cannot write file" + "▲ [WARNING] Unable to cache generated self-signed certificate in home/.wrangler/local-cert. + ERROR: Cannot write file + + " `); expect(std.err).toMatchInlineSnapshot(`""`); }); diff --git a/packages/wrangler/src/__tests__/index.test.ts b/packages/wrangler/src/__tests__/index.test.ts index 925143083d33..d942d27a04e1 100644 --- a/packages/wrangler/src/__tests__/index.test.ts +++ b/packages/wrangler/src/__tests__/index.test.ts @@ -92,8 +92,9 @@ describe("wrangler", () => { -h, --help Show help [boolean] -v, --version Show version number [boolean] --legacy-env Use legacy environments [boolean] + X [ERROR] Unknown argument: invalid-command - Unknown argument: invalid-command" + " `); }); }); @@ -845,14 +846,14 @@ describe("wrangler", () => { it("should throw an error if the deprecated command is used with positional arguments", async () => { await expect(runWrangler("preview GET")).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: The \`wrangler preview\` command has been deprecated. Try using \`wrangler dev\` to to try out a worker during development. " `); await expect(runWrangler(`preview GET "SomeBody"`)).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: The \`wrangler preview\` command has been deprecated. Try using \`wrangler dev\` to to try out a worker during development. " @@ -860,7 +861,7 @@ describe("wrangler", () => { }); }); - describe("subcommand implicit help ran on imcomplete command execution", () => { + describe("subcommand implicit help ran on incomplete command execution", () => { function endEventLoop() { return new Promise((resolve) => setImmediate(resolve)); } @@ -970,7 +971,7 @@ describe("wrangler", () => { it("should print a deprecation message for 'generate'", async () => { await runWrangler("generate").catch((err) => { expect(err.message).toMatchInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler generate\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#generate for alternatives" `); }); @@ -978,7 +979,7 @@ describe("wrangler", () => { it("should print a deprecation message for 'build'", async () => { await runWrangler("build").catch((err) => { expect(err.message).toMatchInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler build\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives" `); }); diff --git a/packages/wrangler/src/__tests__/kv.test.ts b/packages/wrangler/src/__tests__/kv.test.ts index 09ba75f164a8..b755fae2c269 100644 --- a/packages/wrangler/src/__tests__/kv.test.ts +++ b/packages/wrangler/src/__tests__/kv.test.ts @@ -63,8 +63,9 @@ describe("wrangler", () => { Options: -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] + X [ERROR] Not enough non-option arguments: got 0, need at least 1 - Not enough non-option arguments: got 0, need at least 1" + " `); }); @@ -92,8 +93,9 @@ describe("wrangler", () => { Options: -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] + X [ERROR] Unknown arguments: def, ghi - Unknown arguments: def, ghi" + " `); }); @@ -122,8 +124,9 @@ describe("wrangler", () => { Options: -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] + X [ERROR] The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number. - The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number." + " `); }); @@ -288,9 +291,10 @@ describe("wrangler", () => { --namespace-id The id of the namespace to delete [string] -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] + X [ERROR] Not able to delete namespace. + A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". - Not able to delete namespace. - A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"." + " `); }); @@ -365,7 +369,7 @@ describe("wrangler", () => { expect(requests.count).toEqual(1); expect(std.out).toMatchInlineSnapshot( - `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"` + `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); }); @@ -381,7 +385,7 @@ describe("wrangler", () => { ); expect(std.out).toMatchInlineSnapshot( - `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id"` + `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(requests.count).toEqual(1); @@ -399,7 +403,7 @@ describe("wrangler", () => { ); expect(std.out).toMatchInlineSnapshot( - `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id"` + `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(requests.count).toEqual(1); @@ -417,7 +421,7 @@ describe("wrangler", () => { ); expect(requests.count).toEqual(1); expect(std.out).toMatchInlineSnapshot( - `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"` + `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); }); @@ -432,7 +436,7 @@ describe("wrangler", () => { "kv:key put my-key my-value --binding someBinding --env some-environment --preview false" ); expect(std.out).toMatchInlineSnapshot( - `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id"` + `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(requests.count).toEqual(1); @@ -448,7 +452,7 @@ describe("wrangler", () => { "kv:key put my-key --namespace-id some-namespace-id --path foo.txt" ); expect(std.out).toMatchInlineSnapshot( - `"writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id"` + `"Writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(requests.count).toEqual(1); @@ -485,8 +489,9 @@ describe("wrangler", () => { --ttl Time for which the entries should be visible [number] --expiration Time since the UNIX epoch after which the entry expires [number] --path Read value from the file at a given path [string] + X [ERROR] Not enough non-option arguments: got 0, need at least 1 - Not enough non-option arguments: got 0, need at least 1" + " `); }); @@ -521,8 +526,9 @@ describe("wrangler", () => { --ttl Time for which the entries should be visible [number] --expiration Time since the UNIX epoch after which the entry expires [number] --path Read value from the file at a given path [string] + X [ERROR] Exactly one of the arguments binding and namespace-id is required - Exactly one of the arguments binding and namespace-id is required" + " `); }); @@ -557,8 +563,9 @@ describe("wrangler", () => { --ttl Time for which the entries should be visible [number] --expiration Time since the UNIX epoch after which the entry expires [number] --path Read value from the file at a given path [string] + X [ERROR] Arguments binding and namespace-id are mutually exclusive - Arguments binding and namespace-id are mutually exclusive" + " `); }); @@ -593,8 +600,9 @@ describe("wrangler", () => { --ttl Time for which the entries should be visible [number] --expiration Time since the UNIX epoch after which the entry expires [number] --path Read value from the file at a given path [string] + X [ERROR] Exactly one of the arguments value and path is required - Exactly one of the arguments value and path is required" + " `); }); @@ -629,8 +637,9 @@ describe("wrangler", () => { --ttl Time for which the entries should be visible [number] --expiration Time since the UNIX epoch after which the entry expires [number] --path Read value from the file at a given path [string] + X [ERROR] Arguments value and path are mutually exclusive - Arguments value and path are mutually exclusive" + " `); }); @@ -642,11 +651,14 @@ describe("wrangler", () => { `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". + "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); @@ -661,11 +673,14 @@ describe("wrangler", () => { ).rejects.toThrowErrorMatchingInlineSnapshot( `"someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace."` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace. + "X [ERROR] someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(requests.count).toEqual(0); }); @@ -825,11 +840,14 @@ describe("wrangler", () => { `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."` ); expect(std.err).toMatchInlineSnapshot(` - "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". + "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " + `); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." `); - expect(std.out).toMatchInlineSnapshot(`""`); }); }); @@ -919,8 +937,9 @@ describe("wrangler", () => { --namespace-id The id of the namespace to get from [string] -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] [default: false] + X [ERROR] Not enough non-option arguments: got 0, need at least 1 - Not enough non-option arguments: got 0, need at least 1" + " `); }); @@ -950,8 +969,9 @@ describe("wrangler", () => { --namespace-id The id of the namespace to get from [string] -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] [default: false] + X [ERROR] Exactly one of the arguments binding and namespace-id is required - Exactly one of the arguments binding and namespace-id is required" + " `); }); @@ -982,8 +1002,9 @@ describe("wrangler", () => { --namespace-id The id of the namespace to get from [string] -e, --env Perform on a specific environment [string] --preview Interact with a preview namespace [boolean] [default: false] + X [ERROR] Arguments binding and namespace-id are mutually exclusive - Arguments binding and namespace-id are mutually exclusive" + " `); }); @@ -994,11 +1015,14 @@ describe("wrangler", () => { ).rejects.toThrowErrorMatchingInlineSnapshot( `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". + "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); }); @@ -1058,9 +1082,9 @@ describe("wrangler", () => { ); expect(std.err).toMatchInlineSnapshot(` - "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". + "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\". - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); @@ -1071,7 +1095,7 @@ describe("wrangler", () => { `kv:key delete --binding someBinding --env some-environment --preview false someKey` ); expect(std.out).toMatchInlineSnapshot( - `"deleting the key \\"someKey\\" on namespace env-bound-id"` + `"Deleting the key \\"someKey\\" on namespace env-bound-id."` ); expect(std.err).toMatchInlineSnapshot(`""`); expect(requests.count).toEqual(1); @@ -1163,7 +1187,10 @@ describe("wrangler", () => { "Unexpected JSON input from \\"keys.json\\". Expected an array of key-value objects but got type \\"object\\"." `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -1197,10 +1224,15 @@ describe("wrangler", () => { The item at index 3 is {\\"value\\":\\"someValue\\"}" `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.warn).toMatchInlineSnapshot(` - "Unexpected key-value properties in \\"keys.json\\". - The item at index 4 contains unexpected properties: [\\"invalid\\"]." + "▲ [WARNING] Unexpected key-value properties in \\"keys.json\\". + The item at index 4 contains unexpected properties: [\\"invalid\\"]. + + " `); }); }); @@ -1332,7 +1364,10 @@ describe("wrangler", () => { Expected an array of strings but got: 12354" `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -1354,7 +1389,10 @@ describe("wrangler", () => { The item at index 2 is type: \\"object\\" - {\\"key\\":\\"someKey\\"} The item at index 3 is type: \\"object\\" - null" `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); }); diff --git a/packages/wrangler/src/__tests__/logger.test.ts b/packages/wrangler/src/__tests__/logger.test.ts new file mode 100644 index 000000000000..d2076942a955 --- /dev/null +++ b/packages/wrangler/src/__tests__/logger.test.ts @@ -0,0 +1,115 @@ +import { logger } from "../logger"; +import { mockConsoleMethods } from "./helpers/mock-console"; + +describe("logger", () => { + const std = mockConsoleMethods(); + + it("should add colored markers to error and warning messages", () => { + logger.loggerLevel = "debug"; + logger.debug("This is a debug message"); + logger.log("This is a log message"); + logger.warn("This is a warn message"); + logger.error("This is a error message"); + + expect(std.debug).toMatchInlineSnapshot(`"This is a debug message"`); + expect(std.out).toMatchInlineSnapshot(`"This is a log message"`); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] This is a warn message + + " + `); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] This is a error message + + " + `); + }); + + describe("loggerLevel=debug", () => { + it("should render messages that are at or above the log level set in the logger", () => { + logger.loggerLevel = "debug"; + logger.debug("This is a debug message"); + logger.log("This is a log message"); + logger.warn("This is a warn message"); + logger.error("This is a error message"); + + expect(std.debug).toMatchInlineSnapshot(`"This is a debug message"`); + expect(std.out).toMatchInlineSnapshot(`"This is a log message"`); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] This is a warn message + + " + `); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] This is a error message + + " + `); + }); + }); + + describe("loggerLevel=log", () => { + it("should render messages that are at or above the log level set in the logger", () => { + logger.loggerLevel = "log"; + logger.debug("This is a debug message"); + logger.log("This is a log message"); + logger.warn("This is a warn message"); + logger.error("This is a error message"); + + expect(std.debug).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(`"This is a log message"`); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] This is a warn message + + " + `); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] This is a error message + + " + `); + }); + }); + + describe("loggerLevel=warn", () => { + it("should render messages that are at or above the log level set in the logger", () => { + logger.loggerLevel = "warn"; + logger.debug("This is a debug message"); + logger.log("This is a log message"); + logger.warn("This is a warn message"); + logger.error("This is a error message"); + + expect(std.debug).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] This is a warn message + + " + `); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] This is a error message + + " + `); + }); + }); + + describe("loggerLevel=error", () => { + it("should render messages that are at or above the log level set in the logger", () => { + logger.loggerLevel = "error"; + logger.debug("This is a debug message"); + logger.log("This is a log message"); + logger.warn("This is a warn message"); + logger.error("This is a error message"); + + expect(std.debug).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.warn).toMatchInlineSnapshot(`""`); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] This is a error message + + " + `); + }); + }); +}); diff --git a/packages/wrangler/src/__tests__/pages.test.ts b/packages/wrangler/src/__tests__/pages.test.ts index c578ffbaabad..d16e1423177d 100644 --- a/packages/wrangler/src/__tests__/pages.test.ts +++ b/packages/wrangler/src/__tests__/pages.test.ts @@ -47,17 +47,21 @@ describe("pages", () => { `"Must specify a directory of static assets to serve or a command to run."` ); - expect(std.out).toMatchInlineSnapshot( - `"🚧 'wrangler pages ' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"` - ); + expect(std.out).toMatchInlineSnapshot(` + "🚧 'wrangler pages ' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); }); it("should display for pages:functions:build", async () => { await expect(runWrangler("pages functions build")).rejects.toThrowError(); - expect(std.out).toMatchInlineSnapshot( - `"🚧 'wrangler pages ' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"` - ); + expect(std.out).toMatchInlineSnapshot(` + "🚧 'wrangler pages ' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); }); }); diff --git a/packages/wrangler/src/__tests__/publish.test.ts b/packages/wrangler/src/__tests__/publish.test.ts index 454c8ff2b78e..adf2970582fb 100644 --- a/packages/wrangler/src/__tests__/publish.test.ts +++ b/packages/wrangler/src/__tests__/publish.test.ts @@ -108,9 +108,11 @@ describe("publish", () => { test-name.test-sub-domain.workers.dev" `); expect(std.warn).toMatchInlineSnapshot(` - "It looks like you have used Wrangler 1's \`config\` command to login with an API token. + "▲ [WARNING] It looks like you have used Wrangler 1's \`config\` command to login with an API token. This is no longer supported in the current version of Wrangler. - If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\` environment variable." + If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\` environment variable. + + " `); expect(std.err).toMatchInlineSnapshot(`""`); }); @@ -187,7 +189,7 @@ describe("publish", () => { `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: + "▲ [WARNING] Processing wrangler.toml configuration: - No environment found in configuration with name \\"some-env\\". Before using \`--env=some-env\` there should be an equivalent environment section in the configuration. @@ -195,6 +197,8 @@ describe("publish", () => { \`\`\` [env.some-env] \`\`\` +  + " `); }); @@ -251,9 +255,11 @@ describe("publish", () => { test-name.test-sub-domain.workers.dev" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(std.warn).toMatchInlineSnapshot( - `"Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION."` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION. + + " + `); }); it("publishes as an environment when provided", async () => { @@ -271,9 +277,11 @@ describe("publish", () => { some-env.test-name.test-sub-domain.workers.dev" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(std.warn).toMatchInlineSnapshot( - `"Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION."` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION. + + " + `); }); }); }); @@ -353,6 +361,7 @@ describe("publish", () => { await runWrangler("publish ./index"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -412,6 +421,7 @@ describe("publish", () => { await runWrangler("publish ./index --legacy-env false --env staging"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (staging) (TIMINGS) Published test-name (staging) (TIMINGS) @@ -420,7 +430,9 @@ describe("publish", () => { *another-boring-website.com (zone name: some-zone.com) example.com/some-route/* (zone id: JGHFHG654gjcj) more-examples.com/*", - "warn": "Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION.", + "warn": "▲ [WARNING] Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION. + + ", } `); }); @@ -556,13 +568,15 @@ describe("publish", () => { `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - ⚠️ DEPRECATION: \\"build.upload.main\\": + "▲ [WARNING] Processing wrangler.toml configuration: + - DEPRECATION: \\"build.upload.main\\": Delete the \`build.upload.main\` and \`build.upload.dir\` fields. Then add the top level \`main\` field to your configuration file: \`\`\` main = \\"dist/index.js\\" - \`\`\`" + \`\`\` + + " `); }); @@ -588,15 +602,17 @@ describe("publish", () => { `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing ../wrangler.toml configuration: - - ⚠️ DEPRECATION: \\"build.upload.main\\": + "▲ [WARNING] Processing ../wrangler.toml configuration: + - DEPRECATION: \\"build.upload.main\\": Delete the \`build.upload.main\` and \`build.upload.dir\` fields. Then add the top level \`main\` field to your configuration file: \`\`\` main = \\"foo/index.js\\" \`\`\` - - ⚠️ DEPRECATION: \\"build.upload.dir\\": - Use the top level \\"main\\" field or a command-line argument to specify the entry-point for the Worker." + - DEPRECATION: \\"build.upload.dir\\": + Use the top level \\"main\\" field or a command-line argument to specify the entry-point for the Worker. + + " `); }); @@ -732,12 +748,15 @@ export default{ - \\"site.bucket\\" is a required field." `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"site.bucket\\" is a required field. + "X [ERROR] Processing wrangler.toml configuration: + - \\"site.bucket\\" is a required field. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -769,23 +788,28 @@ export default{ await expect(runWrangler("publish ./index.js")).rejects .toThrowErrorMatchingInlineSnapshot(` "Processing wrangler.toml configuration: - - 🚨 NO LONGER SUPPORTED: \\"site.entry-point\\": + - NO LONGER SUPPORTED: \\"site.entry-point\\": The \`site.entry-point\` config field is no longer used. The entry-point should be specified via the command line or the \`main\` config field." `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - 🚨 NO LONGER SUPPORTED: \\"site.entry-point\\": + "X [ERROR] Processing wrangler.toml configuration: + - NO LONGER SUPPORTED: \\"site.entry-point\\": The \`site.entry-point\` config field is no longer used. - The entry-point should be specified via the command line or the \`main\` config field. + The entry-point should be specified via the command line or the \`main\` config field. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - Unexpected fields found in site field: \\"entry-point\\"" + "▲ [WARNING] Processing wrangler.toml configuration: + - Unexpected fields found in site field: \\"entry-point\\" + + " `); }); @@ -800,11 +824,14 @@ export default{ `"Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field."` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field. + "X [ERROR] Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); }); @@ -835,10 +862,10 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -891,10 +918,10 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -941,10 +968,10 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -989,10 +1016,10 @@ export default{ await runWrangler("publish --env some-env --legacy-env false"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (some-env) (TIMINGS) Published test-name (some-env) (TIMINGS) @@ -1038,10 +1065,10 @@ export default{ await runWrangler("publish --env some-env --legacy-env true"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name-some-env (TIMINGS) Published test-name-some-env (TIMINGS) @@ -1082,10 +1109,10 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - skipping - already uploaded - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... + "Reading assets/file-1.txt... + Skipping - already uploaded. + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1123,8 +1150,8 @@ export default{ await runWrangler("publish --site-include file-1.txt"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1162,8 +1189,8 @@ export default{ await runWrangler("publish --site-exclude file-2.txt"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1202,8 +1229,8 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1242,8 +1269,8 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1282,8 +1309,8 @@ export default{ await runWrangler("publish --site-include file-1.txt"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1322,8 +1349,8 @@ export default{ await runWrangler("publish --site-exclude file-2.txt"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - uploading as assets/file-1.2ca234f380.txt... + "Reading assets/file-1.txt... + Uploading as assets/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1364,8 +1391,8 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/directory-1/file-1.txt... - uploading as assets/directory-1/file-1.2ca234f380.txt... + "Reading assets/directory-1/file-1.txt... + Uploading as assets/directory-1/file-1.2ca234f380.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1410,8 +1437,8 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/.well-known/file-2.txt... - uploading as assets/.well-known/file-2.5938485188.txt... + "Reading assets/.well-known/file-2.txt... + Uploading as assets/.well-known/file-2.5938485188.txt... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -1457,13 +1484,15 @@ export default{ ); expect(std.out).toMatchInlineSnapshot(` - "reading assets/large-file.txt... - uploading as assets/large-file.0ea0637a45.txt..." + "Reading assets/large-file.txt... + Uploading as assets/large-file.0ea0637a45.txt... + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." `); expect(std.err).toMatchInlineSnapshot(` - "File assets/too-large-file.txt is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits + "X [ERROR] File assets/too-large-file.txt is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); @@ -1495,13 +1524,15 @@ export default{ `"The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\","` ); - expect(std.out).toMatchInlineSnapshot( - `"reading assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.txt..."` - ); + expect(std.out).toMatchInlineSnapshot(` + "Reading assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.txt... + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\", + "X [ERROR] The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\", - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); }); @@ -1547,12 +1578,12 @@ export default{ await runWrangler("publish"); expect(std.out).toMatchInlineSnapshot(` - "reading assets/file-1.txt... - skipping - already uploaded - reading assets/file-2.txt... - uploading as assets/file-2.5938485188.txt... - deleting assets/file-3.somehash.txt from the asset store... - deleting assets/file-4.anotherhash.txt from the asset store... + "Reading assets/file-1.txt... + Skipping - already uploaded. + Reading assets/file-2.txt... + Uploading as assets/file-2.5938485188.txt... + Deleting assets/file-3.somehash.txt from the asset store... + Deleting assets/file-4.anotherhash.txt from the asset store... ↗️ Done syncing assets Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -2157,7 +2188,7 @@ export default{ await runWrangler("publish index.js"); expect(std.out).toMatchInlineSnapshot(` - "running: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\" + "Running custom build: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\" Uploaded test-name (TIMINGS) Published test-name (TIMINGS) test-name.test-sub-domain.workers.dev" @@ -2181,7 +2212,7 @@ export default{ await runWrangler("publish index.js"); expect(std.out).toMatchInlineSnapshot(` - "running: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js + "Running custom build: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js Uploaded test-name (TIMINGS) Published test-name (TIMINGS) test-name.test-sub-domain.workers.dev" @@ -2204,13 +2235,15 @@ export default{ ).rejects.toThrowErrorMatchingInlineSnapshot( `"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\""` ); - expect(std.out).toMatchInlineSnapshot( - `"running: node -e \\"console.log('custom build');\\""` - ); + expect(std.out).toMatchInlineSnapshot(` + "Running custom build: node -e \\"console.log('custom build');\\" + + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\" + "X [ERROR] Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\" - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -2303,9 +2336,11 @@ export default{ test-name.test-sub-domain.workers.dev" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(std.warn).toMatchInlineSnapshot( - `"In wrangler.toml, you have configured [durable_objects] exported by this Worker (SomeClass), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details."` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] In wrangler.toml, you have configured [durable_objects] exported by this Worker (SomeClass), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details. + + " + `); }); it("does not warn if all the durable object bindings are to external classes", async () => { @@ -2409,6 +2444,7 @@ export default{ await runWrangler("publish index.js"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -2445,6 +2481,7 @@ export default{ await runWrangler("publish index.js"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -2479,11 +2516,15 @@ export default{ ); expect(std).toMatchInlineSnapshot(` Object { - "err": "Publishing Durable Objects to a service environment is not currently supported. This is being tracked at https://github.com/cloudflare/wrangler2/issues/739 + "debug": "", + "err": "X [ERROR] Publishing Durable Objects to a service environment is not currently supported. This is being tracked at https://github.com/cloudflare/wrangler2/issues/739 + + ", + "out": " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new.", + "warn": "▲ [WARNING] Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new.", - "out": "", - "warn": "Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION.", + ", } `); }); @@ -2643,8 +2684,10 @@ export default{ `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time." + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + + " `); }); @@ -2729,21 +2772,26 @@ export default{ - Bindings must have unique names, so that they can all be referenced in the worker. Please change your bindings to have unique names.] `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: + "X [ERROR] Processing wrangler.toml configuration: - CONFLICTING_NAME_ONE assigned to Durable Object, KV Namespace, and R2 Bucket bindings. - CONFLICTING_NAME_TWO assigned to Durable Object and KV Namespace bindings. - CONFLICTING_NAME_THREE assigned to R2 Bucket, Text Blob, Unsafe, Environment Variable, WASM Module, and Data Blob bindings. - CONFLICTING_NAME_FOUR assigned to Text Blob and Unsafe bindings. - Bindings must have unique names, so that they can all be referenced in the worker. - Please change your bindings to have unique names. + Please change your bindings to have unique names. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time." + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + + " `); }); @@ -2820,21 +2868,26 @@ export default{ - Bindings must have unique names, so that they can all be referenced in the worker. Please change your bindings to have unique names.] `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: + "X [ERROR] Processing wrangler.toml configuration: - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings. - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings. - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings. - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings. - Bindings must have unique names, so that they can all be referenced in the worker. - Please change your bindings to have unique names. + Please change your bindings to have unique names. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time." + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + + " `); }); @@ -2947,9 +3000,12 @@ export default{ - Bindings must have unique names, so that they can all be referenced in the worker. Please change your bindings to have unique names.] `); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: + "X [ERROR] Processing wrangler.toml configuration: - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings. - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings. - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings. @@ -2957,13 +3013,15 @@ export default{ - CONFLICTING_NAME_FOUR assigned to R2 Bucket, Text Blob, and Unsafe bindings. - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings. - Bindings must have unique names, so that they can all be referenced in the worker. - Please change your bindings to have unique names. + Please change your bindings to have unique names. - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time." + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + + " `); }); @@ -3010,12 +3068,15 @@ export default{ ).rejects.toThrowErrorMatchingInlineSnapshot( `"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code + "X [ERROR] You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." - `); + " + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -3138,11 +3199,14 @@ export default{ ).rejects.toThrowErrorMatchingInlineSnapshot( `"You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml"` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml + "X [ERROR] You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -3238,12 +3302,15 @@ export default{ ).rejects.toThrowErrorMatchingInlineSnapshot( `"You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml"` ); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml + "X [ERROR] You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." - `); + " + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -3521,9 +3588,11 @@ export default{ `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time." - `); + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + + " + `); }); it("should warn if using unsafe bindings already handled by wrangler", async () => { writeWranglerToml({ @@ -3557,13 +3626,15 @@ export default{ `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: - - \\"unsafe\\" fields are experimental and may change or break at any time. - - \\"unsafe.bindings[0]\\": {\\"name\\":\\"my-binding\\",\\"type\\":\\"plain_text\\",\\"text\\":\\"text\\"} - - The binding type \\"plain_text\\" is directly supported by wrangler. - Consider migrating this unsafe binding to a format for 'plain_text' bindings that is supported by wrangler for optimal support. - For more details, see https://developers.cloudflare.com/workers/cli-wrangler/configuration" - `); + "▲ [WARNING] Processing wrangler.toml configuration: + - \\"unsafe\\" fields are experimental and may change or break at any time. + - \\"unsafe.bindings[0]\\": {\\"name\\":\\"my-binding\\",\\"type\\":\\"plain_text\\",\\"text\\":\\"text\\"} + - The binding type \\"plain_text\\" is directly supported by wrangler. + Consider migrating this unsafe binding to a format for 'plain_text' bindings that is supported by wrangler for optimal support. + For more details, see https://developers.cloudflare.com/workers/cli-wrangler/configuration + + " + `); }); }); }); @@ -3658,14 +3729,16 @@ export default{ `); expect(std.err).toMatchInlineSnapshot(`""`); expect(std.warn).toMatchInlineSnapshot(` - "Processing wrangler.toml configuration: + "▲ [WARNING] Processing wrangler.toml configuration: - DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this: \`\`\` [[rules]] type = \\"Text\\" globs = [ \\"**/*.file\\" ] fallthrough = true - \`\`\`" + \`\`\` + + " `); }); @@ -3757,8 +3830,12 @@ export default{ ); // and the warnings because fallthrough was not explicitly set expect(std.warn).toMatchInlineSnapshot(` - "The module rule at position 1 ({\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.other\\"]}) has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning. - The default module rule {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.txt\\",\\"**/*.html\\"]} has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning." + "▲ [WARNING] The module rule at position 1 ({\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.other\\"]}) has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning. + + + ▲ [WARNING] The default module rule {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.txt\\",\\"**/*.html\\"]} has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning. + + " `); }); @@ -3821,9 +3898,11 @@ export default{ test-name.test-sub-domain.workers.dev" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(std.warn).toMatchInlineSnapshot( - `"Deprecation warning: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"text.file\\" with \\"./text.file\\";"` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] DEPRECATION: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"text.file\\" with \\"./text.file\\"; + + " + `); }); it("should work with legacy module specifiers, with a deprecation warning (2)", async () => { @@ -3847,9 +3926,11 @@ export default{ test-name.test-sub-domain.workers.dev" `); expect(std.err).toMatchInlineSnapshot(`""`); - expect(std.warn).toMatchInlineSnapshot( - `"Deprecation warning: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"index.wasm\\" with \\"./index.wasm\\";"` - ); + expect(std.warn).toMatchInlineSnapshot(` + "▲ [WARNING] DEPRECATION: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"index.wasm\\" with \\"./index.wasm\\"; + + " + `); }); it("should not match regular module specifiers when there aren't any possible legacy module matches", async () => { @@ -3906,6 +3987,7 @@ export default{ await runWrangler("publish index.ts"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -3934,6 +4016,7 @@ export default{ await runWrangler("publish index.js"); // this would throw if we tried to compile with es5 expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -3955,6 +4038,7 @@ export default{ expect(fs.existsSync("some-dir/index.js.map")).toBe(true); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "Uploaded test-name (TIMINGS) Published test-name (TIMINGS) @@ -3972,6 +4056,7 @@ export default{ await runWrangler("publish index.js --dry-run"); expect(std).toMatchInlineSnapshot(` Object { + "debug": "", "err": "", "out": "--dry-run: exiting now.", "warn": "", diff --git a/packages/wrangler/src/__tests__/r2.test.ts b/packages/wrangler/src/__tests__/r2.test.ts index f4b294e07060..5baa7c35dd92 100644 --- a/packages/wrangler/src/__tests__/r2.test.ts +++ b/packages/wrangler/src/__tests__/r2.test.ts @@ -81,8 +81,9 @@ describe("wrangler", () => { -h, --help Show help [boolean] -v, --version Show version number [boolean] --legacy-env Use legacy environments [boolean] + X [ERROR] Not enough non-option arguments: got 0, need at least 1 - Not enough non-option arguments: got 0, need at least 1" + " `); }); @@ -106,8 +107,9 @@ describe("wrangler", () => { -h, --help Show help [boolean] -v, --version Show version number [boolean] --legacy-env Use legacy environments [boolean] + X [ERROR] Unknown arguments: def, ghi - Unknown arguments: def, ghi" + " `); }); @@ -157,8 +159,9 @@ describe("wrangler", () => { -h, --help Show help [boolean] -v, --version Show version number [boolean] --legacy-env Use legacy environments [boolean] + X [ERROR] Not enough non-option arguments: got 0, need at least 1 - Not enough non-option arguments: got 0, need at least 1" + " `); }); @@ -182,8 +185,9 @@ describe("wrangler", () => { -h, --help Show help [boolean] -v, --version Show version number [boolean] --legacy-env Use legacy environments [boolean] + X [ERROR] Unknown arguments: def, ghi - Unknown arguments: def, ghi" + " `); }); diff --git a/packages/wrangler/src/__tests__/route.test.ts b/packages/wrangler/src/__tests__/route.test.ts index 71141276b0e5..69c7d44231e1 100644 --- a/packages/wrangler/src/__tests__/route.test.ts +++ b/packages/wrangler/src/__tests__/route.test.ts @@ -9,7 +9,7 @@ describe("wrangler route", () => { it("shows a deprecation notice when `wrangler route` is run", async () => { await expect(runWrangler("route")).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler route\` has been deprecated. Please use wrangler.toml and/or \`wrangler publish --routes\` to modify routes" `); @@ -18,7 +18,7 @@ describe("wrangler route", () => { it("shows a deprecation notice when `wrangler route delete` is run", async () => { await expect(runWrangler("route delete")).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler route delete\` has been deprecated. Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes." `); @@ -27,7 +27,7 @@ describe("wrangler route", () => { it("shows a deprecation notice when `wrangler route delete ` is run", async () => { await expect(runWrangler("route delete some-zone-id")).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler route delete\` has been deprecated. Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes." `); @@ -36,7 +36,7 @@ describe("wrangler route", () => { it("shows a deprecation notice when `wrangler route list` is run", async () => { await expect(runWrangler("route list")).rejects .toThrowErrorMatchingInlineSnapshot(` - "⚠️ DEPRECATION: + "DEPRECATION: \`wrangler route list\` has been deprecated. Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing. Refer to the Cloudflare Dashboard to see the routes this worker is currently running on." diff --git a/packages/wrangler/src/__tests__/secret.test.ts b/packages/wrangler/src/__tests__/secret.test.ts index 27f286a8770e..ab7a14bfe6ba 100644 --- a/packages/wrangler/src/__tests__/secret.test.ts +++ b/packages/wrangler/src/__tests__/secret.test.ts @@ -121,11 +121,14 @@ describe("wrangler secret", () => { } catch (e) { error = e as Error; } - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Missing script name + "X [ERROR] Missing script name - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`); }); @@ -155,7 +158,10 @@ describe("wrangler secret", () => { runWrangler("secret put the-key --name script-name") ).rejects.toThrowErrorMatchingInlineSnapshot(`"Error in stdin stream"`); - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.warn).toMatchInlineSnapshot(`""`); }); @@ -320,11 +326,14 @@ describe("wrangler secret", () => { } catch (e) { error = e as Error; } - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Missing script name + "X [ERROR] Missing script name - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`); }); @@ -413,11 +422,14 @@ describe("wrangler secret", () => { } catch (e) { error = e as Error; } - expect(std.out).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + " + If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + `); expect(std.err).toMatchInlineSnapshot(` - "Missing script name + "X [ERROR] Missing script name - %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." + " `); expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`); }); diff --git a/packages/wrangler/src/__tests__/tail.test.ts b/packages/wrangler/src/__tests__/tail.test.ts index 519f73eee194..df229ed163c2 100644 --- a/packages/wrangler/src/__tests__/tail.test.ts +++ b/packages/wrangler/src/__tests__/tail.test.ts @@ -263,7 +263,7 @@ describe("tail", () => { "[mock expiration date]" ) ).toMatchInlineSnapshot(` - "successfully created tail, expires at [mock expiration date] + "Successfully created tail, expires at [mock expiration date] Connected to test-worker, waiting for logs... GET https://example.org/ - Ok @ [mock event timestamp]" `); @@ -289,7 +289,7 @@ describe("tail", () => { "[mock expiration date]" ) ).toMatchInlineSnapshot(` - "successfully created tail, expires at [mock expiration date] + "Successfully created tail, expires at [mock expiration date] Connected to test-worker, waiting for logs... \\"* * * * *\\" @ [mock timestamp string] - Ok" `); @@ -316,7 +316,7 @@ describe("tail", () => { "[mock expiration date]" ) ).toMatchInlineSnapshot(` - "successfully created tail, expires at [mock expiration date] + "Successfully created tail, expires at [mock expiration date] Connected to test-worker, waiting for logs... GET https://example.org/ - Ok @ [mock event timestamp]" `); @@ -372,7 +372,7 @@ describe("tail", () => { "[mock expiration date]" ) ).toMatchInlineSnapshot(` - "successfully created tail, expires at [mock expiration date] + "Successfully created tail, expires at [mock expiration date] Connected to test-worker, waiting for logs... GET https://example.org/ - Ok @ [mock event timestamp] (log) some string @@ -380,8 +380,12 @@ describe("tail", () => { (error) 1234" `); expect(std.err).toMatchInlineSnapshot(` - " Error: some error - Error: { complex: 'error' }" + "X [ERROR]  Error: some error + + + X [ERROR]  Error: { complex: 'error' } + + " `); expect(std.warn).toMatchInlineSnapshot(`""`); }); diff --git a/packages/wrangler/src/__tests__/whoami.test.tsx b/packages/wrangler/src/__tests__/whoami.test.tsx index 6c6e613313e1..719ba381c2e5 100644 --- a/packages/wrangler/src/__tests__/whoami.test.tsx +++ b/packages/wrangler/src/__tests__/whoami.test.tsx @@ -54,9 +54,11 @@ describe("getUserInfo()", () => { writeAuthConfigFile({ api_token: "API_TOKEN" }); await getUserInfo(); expect(std.warn).toMatchInlineSnapshot(` - "It looks like you have used Wrangler 1's \`config\` command to login with an API token. + "▲ [WARNING] It looks like you have used Wrangler 1's \`config\` command to login with an API token. This is no longer supported in the current version of Wrangler. - If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\` environment variable." + If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\` environment variable. + + " `); }); }); diff --git a/packages/wrangler/src/config/README.md b/packages/wrangler/src/config/README.md index ab38a6751d39..e0299383a079 100644 --- a/packages/wrangler/src/config/README.md +++ b/packages/wrangler/src/config/README.md @@ -48,7 +48,7 @@ This function will return: - a `Diagnostics` object, which contains any errors or warnings from the validation process The field values may have been parsed directly from the `RawConfig`, inherited into a named environment from the top-level environment, or given a default value. -Generally, if there are any warnings they should be presented to the user via `console.warn()` messages, +Generally, if there are any warnings they should be presented to the user via `logger.warn()` messages, and if there are any errors then an `Error` should be thrown describing these errors. The `Diagnostics` object is hierarchical: each `Diagnostics` instance can contain a collection of child `Diagnostics` instance. diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index c9c9f9fbd740..06089e59d57d 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -1,4 +1,5 @@ import { findUpSync } from "find-up"; +import { logger } from "../logger"; import { parseTOML, readFileSync } from "../parse"; import { normalizeAndValidateConfig } from "./validation"; import type { Config, RawConfig } from "./config"; @@ -41,7 +42,7 @@ export function readConfig( ); if (diagnostics.hasWarnings()) { - console.warn(diagnostics.renderWarnings()); + logger.warn(diagnostics.renderWarnings()); } if (diagnostics.hasErrors()) { throw new Error(diagnostics.renderErrors()); diff --git a/packages/wrangler/src/config/validation-helpers.ts b/packages/wrangler/src/config/validation-helpers.ts index 0bc02e963db1..4b153fa44b47 100644 --- a/packages/wrangler/src/config/validation-helpers.ts +++ b/packages/wrangler/src/config/validation-helpers.ts @@ -14,7 +14,7 @@ export function deprecated( fieldPath: DeepKeyOf, message: string, remove: boolean, - title = "⚠️ DEPRECATION", + title = "DEPRECATION", type: "warning" | "error" = "warning" ): void { const diagnosticMessage = `${title}: "${fieldPath}":\n${message}`; diff --git a/packages/wrangler/src/config/validation.ts b/packages/wrangler/src/config/validation.ts index 71450953debc..12feea85dd30 100644 --- a/packages/wrangler/src/config/validation.ts +++ b/packages/wrangler/src/config/validation.ts @@ -1,5 +1,6 @@ import path from "node:path"; import TOML from "@iarna/toml"; +import { logger } from "../logger"; import { Diagnostics } from "./diagnostics"; import { deprecated, @@ -90,7 +91,7 @@ export function normalizeAndValidateConfig( `site.entry-point`, `The \`site.entry-point\` config field is no longer used.\nThe entry-point should be specified via the command line or the \`main\` config field.`, false, - "🚨 NO LONGER SUPPORTED", + "NO LONGER SUPPORTED", "error" ); @@ -118,7 +119,7 @@ export function normalizeAndValidateConfig( // TODO: remove this once service environments goes GA. if (!isLegacyEnv) { - console.warn( + logger.warn( "Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION." ); } diff --git a/packages/wrangler/src/dev/dev.tsx b/packages/wrangler/src/dev/dev.tsx index fdb005e0a408..04be4198f7b2 100644 --- a/packages/wrangler/src/dev/dev.tsx +++ b/packages/wrangler/src/dev/dev.tsx @@ -9,6 +9,7 @@ import onExit from "signal-exit"; import tmp from "tmp-promise"; import { fetch } from "undici"; import { runCustomBuild } from "../entry"; +import { logger } from "../logger"; import openInBrowser from "../open-in-browser"; import { reportError } from "../reporting"; import { getAPIToken } from "../user"; @@ -212,7 +213,9 @@ function useTmpDir(): string | undefined { setDirectory(dir); return; } catch (err) { - console.error("failed to create tmp dir"); + logger.error( + "Failed to create temporary directory to store built files." + ); handleError(err); } return () => { @@ -239,9 +242,9 @@ function useCustomBuild( ignoreInitial: true, }).on("all", (_event, filePath) => { //TODO: we should buffer requests to the proxy until this completes - console.log(`The file ${filePath} changed, restarting build...`); + logger.log(`The file ${filePath} changed, restarting build...`); runCustomBuild(expectedEntry.file, build).catch((err) => { - console.error("Custom build failed:", err); + logger.error("Custom build failed:", err); }); }); } @@ -288,12 +291,12 @@ function useTunnel(toggle: boolean) { try { await commandExists("cloudflared"); } catch (e) { - console.error( + logger.warn( "To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation" ); return; } - console.log("⎔ Starting a tunnel..."); + logger.log("⎔ Starting a tunnel..."); tunnel.current = spawn("cloudflared", [ "tunnel", "--url", @@ -304,30 +307,30 @@ function useTunnel(toggle: boolean) { tunnel.current.on("close", (code) => { if (code) { - console.log(`Tunnel process exited with code ${code}`); + logger.log(`Tunnel process exited with code ${code}`); } }); removeSignalExitListener.current = onExit((_code, _signal) => { - console.log("⎔ Shutting down local tunnel."); + logger.log("⎔ Shutting down local tunnel."); tunnel.current?.kill(); tunnel.current = undefined; }); const hostName = await findTunnelHostname(); await clipboardy.write(hostName); - console.log(`⬣ Sharing at ${hostName}, copied to clipboard.`); + logger.log(`⬣ Sharing at ${hostName}, copied to clipboard.`); } } startTunnel().catch(async (err) => { - console.error("tunnel:", err); + logger.error("tunnel:", err); await reportError(err); }); return () => { if (tunnel.current) { - console.log("⎔ Shutting down tunnel."); + logger.log("⎔ Shutting down tunnel."); tunnel.current?.kill(); tunnel.current = undefined; removeSignalExitListener.current && removeSignalExitListener.current(); diff --git a/packages/wrangler/src/dev/local.tsx b/packages/wrangler/src/dev/local.tsx index 6902baa3d637..0cb39fe64215 100644 --- a/packages/wrangler/src/dev/local.tsx +++ b/packages/wrangler/src/dev/local.tsx @@ -5,6 +5,7 @@ import path from "node:path"; import { useState, useEffect, useRef } from "react"; import onExit from "signal-exit"; import useInspector from "../inspect"; +import { logger } from "../logger"; import { DEFAULT_MODULE_RULES } from "../module-collection"; import { waitForPortToBeAvailable } from "../proxy"; import type { Config } from "../config"; @@ -219,7 +220,7 @@ function useLocalWorker({ ); const optionsArg = JSON.stringify(options, null); - console.log("⎔ Starting a local server..."); + logger.log("⎔ Starting a local server..."); local.current = spawn( "node", [ @@ -237,7 +238,7 @@ function useLocalWorker({ local.current.on("close", (code) => { if (code) { - console.log(`Miniflare process exited with code ${code}`); + logger.log(`Miniflare process exited with code ${code}`); } }); @@ -257,30 +258,30 @@ function useLocalWorker({ local.current.on("exit", (code) => { if (code) { - console.error(`Miniflare process exited with code ${code}`); + logger.error(`Miniflare process exited with code ${code}`); } }); local.current.on("error", (error: Error) => { - console.error(`Miniflare process failed to spawn`); - console.error(error); + logger.error(`Miniflare process failed to spawn`); + logger.error(error); }); removeSignalExitListener.current = onExit((_code, _signal) => { - console.log("⎔ Shutting down local server."); + logger.log("⎔ Shutting down local server."); local.current?.kill(); local.current = undefined; }); } startLocalWorker().catch((err) => { - console.error("local worker:", err); + logger.error("local worker:", err); }); return () => { abortController.abort(); if (local.current) { - console.log("⎔ Shutting down local server."); + logger.log("⎔ Shutting down local server."); local.current?.kill(); local.current = undefined; removeSignalExitListener.current && removeSignalExitListener.current(); diff --git a/packages/wrangler/src/dev/remote.tsx b/packages/wrangler/src/dev/remote.tsx index ede3a6d9f8df..48a6e6c2b7aa 100644 --- a/packages/wrangler/src/dev/remote.tsx +++ b/packages/wrangler/src/dev/remote.tsx @@ -4,6 +4,7 @@ import path from "node:path"; import { useState, useEffect, useRef } from "react"; import { createWorkerPreview } from "../create-worker-preview"; import useInspector from "../inspect"; +import { logger } from "../logger"; import { usePreviewServer } from "../proxy"; import { syncAssets } from "../sites"; import type { CfPreviewToken } from "../create-worker-preview"; @@ -115,7 +116,7 @@ export function useWorker(props: { if (!startedRef.current) { startedRef.current = true; } else { - console.log("⎔ Detected changes, restarted server."); + logger.log("⎔ Detected changes, restarted server."); } const assets = await syncAssets( @@ -184,7 +185,7 @@ export function useWorker(props: { // we want to log the error, but not end the process // since it could recover after the developer fixes whatever's wrong if ((err as { code: string }).code !== "ABORT_ERR") { - console.error("remote worker:", err); + logger.error("Error on remote worker:", err); } }); diff --git a/packages/wrangler/src/dev/use-esbuild.ts b/packages/wrangler/src/dev/use-esbuild.ts index 9b0c64769845..cc8c2dbbb287 100644 --- a/packages/wrangler/src/dev/use-esbuild.ts +++ b/packages/wrangler/src/dev/use-esbuild.ts @@ -2,6 +2,7 @@ import assert from "node:assert"; import { useApp } from "ink"; import { useState, useEffect } from "react"; import { bundleWorker } from "../bundle"; +import { logger } from "../logger"; import type { Config } from "../config"; import type { Entry } from "../entry"; import type { CfModule } from "../worker"; @@ -44,7 +45,7 @@ export function useEsbuild({ const watchMode: WatchMode = { async onRebuild(error) { - if (error) console.error("watch build failed:", error); + if (error) logger.error("Watch build failed:", error); else { // nothing really changes here, so let's increment the id // to change the return object's identity diff --git a/packages/wrangler/src/dialogs.tsx b/packages/wrangler/src/dialogs.tsx index 45d9d648d1c9..8afc0e895bf6 100644 --- a/packages/wrangler/src/dialogs.tsx +++ b/packages/wrangler/src/dialogs.tsx @@ -2,6 +2,7 @@ import { Box, Text, useInput, render } from "ink"; import TextInput from "ink-text-input"; import * as React from "react"; import { useState } from "react"; +import { logger } from "./logger"; type ConfirmProps = { text: string; onConfirm: (answer: boolean) => void; @@ -13,7 +14,7 @@ function Confirm(props: ConfirmProps) { } else if (input === "n") { props.onConfirm(false); } else { - console.log("Unrecognised input"); + logger.warn("Unrecognised input:", input); } }); return ( diff --git a/packages/wrangler/src/entry.ts b/packages/wrangler/src/entry.ts index c9e7991d7836..05c524a797ba 100644 --- a/packages/wrangler/src/entry.ts +++ b/packages/wrangler/src/entry.ts @@ -3,6 +3,7 @@ import { existsSync } from "node:fs"; import path from "node:path"; import * as esbuild from "esbuild"; import { execaCommand } from "execa"; +import { logger } from "./logger"; import type { Config } from "./config"; import type { CfScriptFormat } from "./worker"; import type { Metafile } from "esbuild"; @@ -54,8 +55,8 @@ export async function getEntry( partitionDurableObjectBindings(config); if (command === "dev" && remoteBindings.length > 0) { - console.warn( - "WARNING: You have Durable Object bindings, which are not defined locally in the worker being developed.\n" + + logger.warn( + "WARNING: You have Durable Object bindings that are not defined locally in the worker being developed.\n" + "Be aware that changes to the data stored in these Durable Objects will be permanent and affect the live instances.\n" + "Remote Durable Objects that are affected:\n" + remoteBindings.map((b) => `- ${JSON.stringify(b)}`).join("\n") @@ -86,7 +87,7 @@ export async function runCustomBuild( ) { if (build?.command) { // TODO: add a deprecation message here? - console.log("running:", build.command); + logger.log("Running custom build:", build.command); await execaCommand(build.command, { shell: true, // we keep these two as "inherit" so that @@ -159,7 +160,7 @@ export default async function guessWorkerFormat( if (scriptExports.includes("default")) { guessedWorkerFormat = "modules"; } else { - console.warn( + logger.warn( `The entrypoint ${path.relative( process.cwd(), entryFile diff --git a/packages/wrangler/src/environment-variables.ts b/packages/wrangler/src/environment-variables.ts index 0b8529dac799..bc77ba7a70b4 100644 --- a/packages/wrangler/src/environment-variables.ts +++ b/packages/wrangler/src/environment-variables.ts @@ -1,3 +1,5 @@ +import { logger } from "./logger"; + /** * Create a function used to access an environment variable. * @@ -21,7 +23,7 @@ export function getEnvironmentVariableFactory({ if (!hasWarned) { // Only show the warning once. hasWarned = true; - console.warn( + logger.warn( `Using "${deprecatedName}" environment variable. This is deprecated. Please use "${variableName}", instead.` ); } diff --git a/packages/wrangler/src/errors.ts b/packages/wrangler/src/errors.ts index 675008b1fe7a..d183dfab2905 100644 --- a/packages/wrangler/src/errors.ts +++ b/packages/wrangler/src/errors.ts @@ -1,6 +1,6 @@ export class DeprecationError extends Error { constructor(message: string) { - super(`⚠️ DEPRECATION:\n${message}`); + super(`DEPRECATION:\n${message}`); } } diff --git a/packages/wrangler/src/https-options.ts b/packages/wrangler/src/https-options.ts index 7ed083626d7d..09eeab03aed9 100644 --- a/packages/wrangler/src/https-options.ts +++ b/packages/wrangler/src/https-options.ts @@ -2,6 +2,7 @@ import * as fs from "node:fs"; import { homedir, networkInterfaces } from "node:os"; import * as path from "node:path"; import { promisify } from "node:util"; +import { logger } from "./logger"; import type { Attributes, Options } from "selfsigned"; // Most of this file has been borrowed from the implementation in Miniflare. @@ -26,7 +27,7 @@ export async function getHttpsOptions() { hasCertificateExpired(keyPath, certPath); if (regenerate) { - console.log("Generating new self-signed certificate..."); + logger.log("Generating new self-signed certificate..."); const { key, cert } = await generateCertificate(); try { // Write certificate files so we can reuse them later. @@ -35,7 +36,7 @@ export async function getHttpsOptions() { fs.writeFileSync(certPath, cert, "utf8"); } catch (e) { const message = e instanceof Error ? e.message : `${e}`; - console.warn( + logger.warn( `Unable to cache generated self-signed certificate in ${certDirectory}.\n${message}` ); } diff --git a/packages/wrangler/src/index.tsx b/packages/wrangler/src/index.tsx index 469958e49238..ce94387f55e0 100644 --- a/packages/wrangler/src/index.tsx +++ b/packages/wrangler/src/index.tsx @@ -32,6 +32,7 @@ import { isKeyValue, unexpectedKeyValueProps, } from "./kv"; +import { logger } from "./logger"; import { getPackageManager } from "./package-manager"; import { pages, pagesBetaWarning } from "./pages"; import { @@ -81,8 +82,8 @@ function getRules(config: Config): Config["rules"] { } if (config.build?.upload?.rules) { - console.warn( - `Deprecation notice: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this: + logger.warn( + `DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this: ${TOML.stringify({ rules: config.build.upload.rules })}` ); @@ -98,7 +99,7 @@ async function printWranglerBanner() { const text = ` ⛅️ wrangler ${wranglerVersion} ${await updateCheck()}`; - console.log( + logger.log( text + "\n" + (supportsColor.stdout @@ -324,7 +325,7 @@ export async function main(argv: string[]): Promise { const workerName = args.name || path.basename(path.resolve(process.cwd())); if (fs.existsSync(wranglerTomlDestination)) { - console.warn(`${wranglerTomlDestination} file already exists!`); + logger.warn(`${wranglerTomlDestination} file already exists!`); const shouldContinue = await confirm( "Do you want to continue initializing this project?" ); @@ -342,7 +343,7 @@ export async function main(argv: string[]): Promise { compatibility_date: compatibilityDate, }) + "\n" ); - console.log(`✨ Successfully created wrangler.toml`); + logger.log(`✨ Successfully created wrangler.toml`); justCreatedWranglerToml = true; } catch (err) { throw new Error( @@ -379,7 +380,7 @@ export async function main(argv: string[]): Promise { ) + "\n" ); await packageManager.install(); - console.log(`✨ Created package.json`); + logger.log(`✨ Created package.json`); pathToPackageJson = path.join(creationDirectory, "package.json"); } else { return; @@ -404,7 +405,7 @@ export async function main(argv: string[]): Promise { )); if (shouldInstall) { await packageManager.addDevDeps(`wrangler@${wranglerVersion}`); - console.log(`✨ Installed wrangler`); + logger.log(`✨ Installed wrangler`); } } } @@ -425,7 +426,7 @@ export async function main(argv: string[]): Promise { "typescript" ); - console.log( + logger.log( `✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies` ); pathToTSConfig = path.join(creationDirectory, "tsconfig.json"); @@ -453,7 +454,7 @@ export async function main(argv: string[]): Promise { // it could be complicated in existing projects // and we don't want to break them. Instead, we simply // tell the user that they need to update their tsconfig.json - console.log( + logger.log( `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in your tsconfig.json` ); } @@ -512,21 +513,21 @@ export async function main(argv: string[]): Promise { 2 ) + "\n" ); - console.log( + logger.log( `\nTo start developing your Worker, run \`${ isNamedWorker ? `cd ${args.name} && ` : "" }npm start\`` ); - console.log( + logger.log( `To publish your Worker to the Internet, run \`npm run publish\`` ); } else { - console.log( + logger.log( `\nTo start developing your Worker, run \`npx wrangler dev\`${ isCreatingWranglerToml ? "" : ` ${scriptPath}` }` ); - console.log( + logger.log( `To publish your Worker to the Internet, run \`npx wrangler publish\`${ isCreatingWranglerToml ? "" : ` ${scriptPath}` }` @@ -553,7 +554,7 @@ export async function main(argv: string[]): Promise { readFileSync(path.join(__dirname, "../templates/new-worker.ts")) ); - console.log(`✨ Created src/index.ts`); + logger.log(`✨ Created src/index.ts`); await writePackageJsonScriptsAndUpdateWranglerToml( shouldWritePackageJsonScripts, @@ -577,7 +578,7 @@ export async function main(argv: string[]): Promise { readFileSync(path.join(__dirname, "../templates/new-worker.js")) ); - console.log(`✨ Created src/index.js`); + logger.log(`✨ Created src/index.js`); await writePackageJsonScriptsAndUpdateWranglerToml( shouldWritePackageJsonScripts, @@ -748,21 +749,21 @@ export async function main(argv: string[]): Promise { const entry = await getEntry(args, config, "dev"); if (args["experimental-public"]) { - console.warn( - "🚨 The --experimental-public field is experimental and will change in the future." + logger.warn( + "The --experimental-public field is experimental and will change in the future." ); } if (args.public) { throw new Error( - "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future." + "The --public field has been renamed to --experimental-public, and will change behaviour in the future." ); } const upstreamProtocol = args["upstream-protocol"] || config.dev.upstream_protocol; if (upstreamProtocol === "http") { - console.warn( + logger.warn( "Setting upstream-protocol to http is not currently implemented.\n" + "If this is required in your project, please add your use case to the following issue:\n" + "https://github.com/cloudflare/wrangler2/issues/583." @@ -1032,13 +1033,13 @@ export async function main(argv: string[]): Promise { async (args) => { await printWranglerBanner(); if (args["experimental-public"]) { - console.warn( - "🚨 The --experimental-public field is experimental and will change in the future." + logger.warn( + "The --experimental-public field is experimental and will change in the future." ); } if (args.public) { throw new Error( - "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future." + "The --public field has been renamed to --experimental-public, and will change behaviour in the future." ); } @@ -1049,8 +1050,8 @@ export async function main(argv: string[]): Promise { const entry = await getEntry(args, config, "publish"); if (args.latest) { - console.warn( - "⚠️ Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n" + logger.warn( + "Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n" ); } @@ -1184,8 +1185,8 @@ export async function main(argv: string[]): Promise { }`; if (args.format === "pretty") { - console.log( - `successfully created tail, expires at ${expiration.toLocaleString()}` + logger.log( + `Successfully created tail, expires at ${expiration.toLocaleString()}` ); } @@ -1215,7 +1216,7 @@ export async function main(argv: string[]): Promise { } if (args.format === "pretty") { - console.log(`Connected to ${scriptDisplayName}, waiting for logs...`); + logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`); } tail.on("close", async () => { @@ -1258,7 +1259,7 @@ export async function main(argv: string[]): Promise { } // Delegate to `wrangler dev` - console.warn( + logger.warn( "***************************************************\n" + "The `wrangler preview` command has been deprecated.\n" + "Attempting to run `wrangler dev` instead.\n" + @@ -1466,7 +1467,7 @@ export async function main(argv: string[]): Promise { ? await prompt("Enter a secret value:", "password") : await readFromStdin(); - console.log( + logger.log( `🌀 Creating the secret for script ${scriptName} ${ args.env && !isLegacyEnv(config) ? `(${args.env})` : "" }` @@ -1545,7 +1546,7 @@ export async function main(argv: string[]): Promise { } } - console.log(`✨ Success! Uploaded secret ${args.key}`); + logger.log(`✨ Success! Uploaded secret ${args.key}`); } ) .command( @@ -1588,7 +1589,7 @@ export async function main(argv: string[]): Promise { }?` ) ) { - console.log( + logger.log( `🌀 Deleting the secret ${args.key} on script ${scriptName}${ args.env && !isLegacyEnv(config) ? ` (${args.env})` : "" }` @@ -1600,7 +1601,7 @@ export async function main(argv: string[]): Promise { : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`; await fetchResult(`${url}/${args.key}`, { method: "DELETE" }); - console.log(`✨ Success! Deleted secret ${args.key}`); + logger.log(`✨ Success! Deleted secret ${args.key}`); } } ) @@ -1635,7 +1636,7 @@ export async function main(argv: string[]): Promise { ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets` : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`; - console.log(JSON.stringify(await fetchResult(url), null, " ")); + logger.log(JSON.stringify(await fetchResult(url), null, " ")); } ); } @@ -1680,7 +1681,7 @@ export async function main(argv: string[]): Promise { const config = readConfig(args.config as ConfigPath, args); if (!config.name) { - console.warn( + logger.warn( "No configured name present, using `worker` as a prefix for the title" ); } @@ -1694,16 +1695,16 @@ export async function main(argv: string[]): Promise { // TODO: generate a binding name stripping non alphanumeric chars - console.log(`🌀 Creating namespace with title "${title}"`); + logger.log(`🌀 Creating namespace with title "${title}"`); const namespaceId = await createNamespace(accountId, title); - console.log("✨ Success!"); + logger.log("✨ Success!"); const envString = args.env ? ` under [env.${args.env}]` : ""; const previewString = args.preview ? "preview_" : ""; - console.log( + logger.log( `Add the following to your configuration file in your kv_namespaces array${envString}:` ); - console.log( + logger.log( `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }` ); @@ -1721,7 +1722,7 @@ export async function main(argv: string[]): Promise { // TODO: we should show bindings if they exist for given ids - console.log( + logger.log( JSON.stringify(await listNamespaces(accountId), null, " ") ); } @@ -1857,12 +1858,12 @@ export async function main(argv: string[]): Promise { args.value!; if (args.path) { - console.log( - `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}` + logger.log( + `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.` ); } else { - console.log( - `writing the value "${value}" to key "${key}" on namespace ${namespaceId}` + logger.log( + `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.` ); } @@ -1918,7 +1919,7 @@ export async function main(argv: string[]): Promise { namespaceId, prefix ); - console.log(JSON.stringify(results, undefined, 2)); + logger.log(JSON.stringify(results, undefined, 2)); } ) .command( @@ -1962,7 +1963,7 @@ export async function main(argv: string[]): Promise { const accountId = await requireAuth(config); - console.log(await getKeyValue(accountId, namespaceId, key)); + logger.log(await getKeyValue(accountId, namespaceId, key)); } ) .command( @@ -1999,8 +2000,8 @@ export async function main(argv: string[]): Promise { const config = readConfig(args.config as ConfigPath, args); const namespaceId = getNamespaceId(args, config); - console.log( - `deleting the key "${key}" on namespace ${namespaceId}` + logger.log( + `Deleting the key "${key}" on namespace ${namespaceId}.` ); const accountId = await requireAuth(config); @@ -2093,7 +2094,7 @@ export async function main(argv: string[]): Promise { } } if (warnings.length > 0) { - console.warn( + logger.warn( `Unexpected key-value properties in "${filename}".\n` + warnings.join("\n") ); @@ -2120,11 +2121,11 @@ export async function main(argv: string[]): Promise { namespaceId, content, (index, total) => { - console.log(`Uploaded ${index} of ${total}.`); + logger.log(`Uploaded ${index} of ${total}.`); } ); - console.log("Success!"); + logger.log("Success!"); } ) .command( @@ -2171,7 +2172,7 @@ export async function main(argv: string[]): Promise { `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?` ); if (!result) { - console.log(`Not deleting keys read from "${filename}".`); + logger.log(`Not deleting keys read from "${filename}".`); return; } } @@ -2214,11 +2215,11 @@ export async function main(argv: string[]): Promise { namespaceId, content, (index, total) => { - console.log(`Deleted ${index} of ${total}.`); + logger.log(`Deleted ${index} of ${total}.`); } ); - console.log("Success!"); + logger.log("Success!"); } ); } @@ -2253,9 +2254,9 @@ export async function main(argv: string[]): Promise { const accountId = await requireAuth(config); - console.log(`Creating bucket ${args.name}.`); + logger.log(`Creating bucket ${args.name}.`); await createR2Bucket(accountId, args.name); - console.log(`Created bucket ${args.name}.`); + logger.log(`Created bucket ${args.name}.`); } ); @@ -2264,7 +2265,7 @@ export async function main(argv: string[]): Promise { const accountId = await requireAuth(config); - console.log(JSON.stringify(await listR2Buckets(accountId), null, 2)); + logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2)); }); r2BucketYargs.command( @@ -2284,9 +2285,9 @@ export async function main(argv: string[]): Promise { const accountId = await requireAuth(config); - console.log(`Deleting bucket ${args.name}.`); + logger.log(`Deleting bucket ${args.name}.`); await deleteR2Bucket(accountId, args.name); - console.log(`Deleted bucket ${args.name}.`); + logger.log(`Deleted bucket ${args.name}.`); } ); return r2BucketYargs; @@ -2387,20 +2388,18 @@ export async function main(argv: string[]): Promise { try { await wrangler.parse(); } catch (e) { + logger.log(""); // Just adds a bit of space if (e instanceof CommandLineArgsError) { wrangler.showHelp("error"); - console.error(""); // Just adds a bit of space - console.error(e.message); + logger.error(e.message); } else if (e instanceof ParseError) { - console.error(""); e.notes.push({ text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new", }); - console.error(formatMessage(e)); + logger.error(formatMessage(e)); } else { - console.error(e instanceof Error ? e.message : e); - console.error(""); // Just adds a bit of space - console.error( + logger.error(e instanceof Error ? e.message : e); + logger.log( `${fgGreenColor}%s${resetColor}`, "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new." ); @@ -2415,7 +2414,7 @@ function getDevCompatibilityDate( ) { const currentDate = new Date().toISOString().substring(0, 10); if (config.configPath !== undefined && compatibilityDate === undefined) { - console.warn( + logger.warn( `No compatibility_date was specified. Using today's date: ${currentDate}.\n` + "Add one to your wrangler.toml file:\n" + "```\n" + diff --git a/packages/wrangler/src/inspect.ts b/packages/wrangler/src/inspect.ts index f57045561583..d92a086a6122 100644 --- a/packages/wrangler/src/inspect.ts +++ b/packages/wrangler/src/inspect.ts @@ -5,6 +5,7 @@ import { URL } from "node:url"; import { useEffect, useRef, useState } from "react"; import WebSocket, { WebSocketServer } from "ws"; import { version } from "../package.json"; +import { logger } from "./logger"; import { waitForPortToBeAvailable } from "./proxy"; import type Protocol from "devtools-protocol"; import type { IncomingMessage, Server, ServerResponse } from "node:http"; @@ -134,7 +135,7 @@ export default function useInspector(props: InspectorProps) { wsServer.on("connection", (ws: WebSocket) => { if (wsServer.clients.size > 1) { /** We only want to have one active Devtools instance at a time. */ - console.error( + logger.error( "Tried to open a new devtools window when a previous one was already open." ); ws.close(1013, "Too many clients; only one can be connected at a time"); @@ -165,7 +166,7 @@ export default function useInspector(props: InspectorProps) { } startInspectorProxy().catch((err) => { if ((err as { code: string }).code !== "ABORT_ERR") { - console.error(err); + logger.error("Failed to start inspector:", err); } }); return () => { @@ -254,9 +255,7 @@ export default function useInspector(props: InspectorProps) { const evt = JSON.parse(event.data); if (evt.method === "Runtime.exceptionThrown") { const params = evt.params as Protocol.Runtime.ExceptionThrownEvent; - console.error( - "🚨", // cheesy, but it works - // maybe we could use color here too. + logger.error( params.exceptionDetails.text, params.exceptionDetails.exception?.description ?? "" ); @@ -268,7 +267,7 @@ export default function useInspector(props: InspectorProps) { } } else { // We should never get here, but who know is 2022... - console.error("unrecognised devtools event:", event); + logger.error("Unrecognised devtools event:", event); } }); } @@ -287,7 +286,7 @@ export default function useInspector(props: InspectorProps) { }); ws.on("unexpected-response", () => { - console.log("waiting for connection..."); + logger.log("Waiting for connection..."); /** * This usually means the worker is not "ready" yet * so we'll just retry the connection process @@ -393,7 +392,7 @@ export default function useInspector(props: InspectorProps) { * happens on the first request. Maybe we should buffer * these messages too? */ - console.error(e); + logger.error(e); } } } @@ -618,7 +617,7 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void { break; } } else { - console.warn(`Unsupported console method: ${method}`); - console.log("console event:", evt); + logger.warn(`Unsupported console method: ${method}`); + logger.warn("console event:", evt); } } diff --git a/packages/wrangler/src/logger.ts b/packages/wrangler/src/logger.ts new file mode 100644 index 000000000000..895b8ceafb28 --- /dev/null +++ b/packages/wrangler/src/logger.ts @@ -0,0 +1,56 @@ +import { format } from "node:util"; +import { formatMessagesSync } from "esbuild"; + +const LOGGER_LEVELS = { + error: 0, + warn: 1, + log: 2, + debug: 3, +} as const; + +type LoggerLevel = keyof typeof LOGGER_LEVELS; + +/** A map from LOGGER_LEVEL to the error `kind` needed by `formatMessagesSync()`. */ +const LOGGER_LEVEL_FORMAT_TYPE_MAP = { + error: "error", + warn: "warning", + log: undefined, + debug: undefined, +} as const; + +class Logger { + constructor(public loggerLevel: LoggerLevel = "log") {} + + debug = (...args: unknown[]) => this.doLog("debug", args); + log = (...args: unknown[]) => this.doLog("log", args); + warn = (...args: unknown[]) => this.doLog("warn", args); + error = (...args: unknown[]) => this.doLog("error", args); + + private doLog(messageLevel: LoggerLevel, args: unknown[]) { + if (LOGGER_LEVELS[this.loggerLevel] >= LOGGER_LEVELS[messageLevel]) { + console[messageLevel](this.formatMessage(messageLevel, format(...args))); + } + } + + formatMessage(level: LoggerLevel, text: string): string { + const kind = LOGGER_LEVEL_FORMAT_TYPE_MAP[level]; + if (kind) { + return formatMessagesSync([{ text }], { + color: true, + kind, + terminalWidth: process.stdout.columns, + })[0]; + } else { + return text; + } + } +} + +/** + * A drop-in replacement for `console` for outputting logging messages. + * + * Errors and Warnings will get additional formatting to highlight them to the user. + * You can also set a `logger.loggerLevel` value to one of "debug", "log", "warn" or "error", + * to filter out logging messages. + */ +export const logger = new Logger(); diff --git a/packages/wrangler/src/module-collection.ts b/packages/wrangler/src/module-collection.ts index a97e221dafd4..b813ee658972 100644 --- a/packages/wrangler/src/module-collection.ts +++ b/packages/wrangler/src/module-collection.ts @@ -2,6 +2,7 @@ import crypto from "node:crypto"; import { readFile } from "node:fs/promises"; import path from "node:path"; import globToRegExp from "glob-to-regexp"; +import { logger } from "./logger"; import type { Config, ConfigModuleRuleType } from "./config"; import type { CfModule, CfModuleType, CfScriptFormat } from "./worker"; import type esbuild from "esbuild"; @@ -58,7 +59,7 @@ export default function createModuleCollector(props: { if (rule.type in completedRuleLocations) { if (rules[completedRuleLocations[rule.type]].fallthrough !== false) { if (index < (props.rules || []).length) { - console.warn( + logger.warn( `The module rule at position ${index} (${JSON.stringify( rule )}) has the same type as a previous rule (at position ${ @@ -68,7 +69,7 @@ export default function createModuleCollector(props: { )}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning.` ); } else { - console.warn( + logger.warn( `The default module rule ${JSON.stringify( rule )} has the same type as a previous rule (at position ${ @@ -136,8 +137,8 @@ export default function createModuleCollector(props: { return; } // In the future, this will simply throw an error - console.warn( - `Deprecation warning: detected a legacy module import in "./${path.relative( + logger.warn( + `DEPRECATION: detected a legacy module import in "./${path.relative( process.cwd(), args.importer )}". This will stop working in the future. Replace references to "${ diff --git a/packages/wrangler/src/open-in-browser.ts b/packages/wrangler/src/open-in-browser.ts index 03103ea23335..cea1bbe9e8b4 100644 --- a/packages/wrangler/src/open-in-browser.ts +++ b/packages/wrangler/src/open-in-browser.ts @@ -1,13 +1,14 @@ import open from "open"; +import { logger } from "./logger"; /** * An extremely simple wrapper around the open command. * Specifically, it adds an 'error' event handler so that when this function - * is called in environments where we can't open the browser (e.g. github codespaces, - * stackblitz, remote servers), it doesn't just crash the process. + * is called in environments where we can't open the browser (e.g. GitHub Codespaces, + * StackBlitz, remote servers), it doesn't just crash the process. * * @param url the URL to point the browser at - * @param options open a chromium-based browser instead of the default + * @param options open a Chromium-based browser instead of the default */ export default async function openInBrowser( url: string, @@ -20,6 +21,6 @@ export default async function openInBrowser( : undefined; const childProcess = await open(url, options); childProcess.on("error", () => { - console.warn(`Failed to open ${url} in a browser`); + logger.warn(`Failed to open ${url} in a browser.`); }); } diff --git a/packages/wrangler/src/package-manager.ts b/packages/wrangler/src/package-manager.ts index 31e66fc3ba8a..d390045b944a 100644 --- a/packages/wrangler/src/package-manager.ts +++ b/packages/wrangler/src/package-manager.ts @@ -1,6 +1,7 @@ import { existsSync } from "node:fs"; import { join } from "node:path"; import { execa, execaCommandSync } from "execa"; +import { logger } from "./logger"; export interface PackageManager { cwd: string; @@ -16,26 +17,26 @@ export async function getPackageManager(cwd: string): Promise { if (hasNpmLock) { if (hasNpm) { - console.log( + logger.log( "Using npm as package manager, as there is already a package-lock.json file." ); return { ...NpmPackageManager, cwd }; } else if (hasYarn) { - console.log("Using yarn as package manager."); - console.warn( + logger.log("Using yarn as package manager."); + logger.warn( "There is already a package-lock.json file but could not find npm on the PATH." ); return { ...YarnPackageManager, cwd }; } } else if (hasYarnLock) { if (hasYarn) { - console.log( + logger.log( "Using yarn as package manager, as there is already a yarn.lock file." ); return { ...YarnPackageManager, cwd }; } else if (hasNpm) { - console.log("Using npm as package manager."); - console.warn( + logger.log("Using npm as package manager."); + logger.warn( "There is already a yarn.lock file but could not find yarn on the PATH." ); return { ...NpmPackageManager, cwd }; @@ -43,10 +44,10 @@ export async function getPackageManager(cwd: string): Promise { } if (hasNpm) { - console.log("Using npm as package manager."); + logger.log("Using npm as package manager."); return { ...NpmPackageManager, cwd }; } else if (hasYarn) { - console.log("Using yarn as package manager."); + logger.log("Using yarn as package manager."); return { ...YarnPackageManager, cwd }; } else { throw new Error( diff --git a/packages/wrangler/src/pages.tsx b/packages/wrangler/src/pages.tsx index 523b0e8f43b0..eec088348c95 100644 --- a/packages/wrangler/src/pages.tsx +++ b/packages/wrangler/src/pages.tsx @@ -24,6 +24,7 @@ import { writeRoutesModule } from "../pages/functions/routes"; import { fetchResult } from "./cfetch"; import { readConfig } from "./config"; import { FatalError } from "./errors"; +import { logger } from "./logger"; import openInBrowser from "./open-in-browser"; import { toUrlPath } from "./paths"; import { requireAuth } from "./user"; @@ -146,7 +147,7 @@ function getPort(pid: number) { const match = matches[0]; if (match) return parseInt(match[1]); } catch (thrown) { - console.error( + logger.error( `Error scanning for ports of process with PID ${pid}: ${thrown}` ); } @@ -167,7 +168,7 @@ async function spawnProxyProcess({ ); } - console.log(`Running ${command.join(" ")}...`); + logger.log(`Running ${command.join(" ")}...`); const proxy = spawn( command[0].toString(), command.slice(1).map((value) => value.toString()), @@ -184,25 +185,25 @@ async function spawnProxyProcess({ }); proxy.stdout.on("data", (data) => { - console.log(`[proxy]: ${data}`); + logger.log(`[proxy]: ${data}`); }); proxy.stderr.on("data", (data) => { - console.error(`[proxy]: ${data}`); + logger.error(`[proxy]: ${data}`); }); proxy.on("close", (code) => { - console.error(`Proxy exited with status ${code}.`); + logger.error(`Proxy exited with status ${code}.`); }); // Wait for proxy process to start... while (!proxy.pid) {} if (port === undefined) { - console.log( + logger.log( `Sleeping ${SECONDS_TO_WAIT_FOR_PROXY} seconds to allow proxy process to start before attempting to automatically determine port...` ); - console.log("To skip, specify the proxy port with --proxy."); + logger.log("To skip, specify the proxy port with --proxy."); await sleep(SECONDS_TO_WAIT_FOR_PROXY * 1000); port = getPids(proxy.pid) @@ -216,7 +217,7 @@ async function spawnProxyProcess({ 1 ); } else { - console.log(`Automatically determined the proxy port to be ${port}.`); + logger.log(`Automatically determined the proxy port to be ${port}.`); } } @@ -483,12 +484,12 @@ async function generateAssetsFetch(directory: string): Promise { }).on("change", (path) => { switch (path) { case headersFile: { - console.log("_headers modified. Re-evaluating..."); + logger.log("_headers modified. Re-evaluating..."); headersMatcher = generateHeadersMatcher(headersFile); break; } case redirectsFile: { - console.log("_redirects modified. Re-evaluating..."); + logger.log("_redirects modified. Re-evaluating..."); redirectsMatcher = generateRedirectsMatcher(redirectsFile); break; } @@ -1180,10 +1181,10 @@ export const pages: BuilderCallback = (yargs) => { _: [_pages, _dev, ...remaining], }) => { // Beta message for `wrangler pages ` usage - console.log(pagesBetaWarning); + logger.log(pagesBetaWarning); if (!local) { - console.error("Only local mode is supported at the moment."); + logger.error("Only local mode is supported at the moment."); return; } @@ -1212,7 +1213,7 @@ export const pages: BuilderCallback = (yargs) => { if (usingFunctions) { const outfile = join(tmpdir(), "./functionsWorker.js"); - console.log(`Compiling worker to "${outfile}"...`); + logger.log(`Compiling worker to "${outfile}"...`); try { await buildFunctions({ @@ -1254,7 +1255,7 @@ export const pages: BuilderCallback = (yargs) => { scriptPath, }; } else { - console.log("No functions. Shimming..."); + logger.log("No functions. Shimming..."); miniflareArgs = { // TODO: The fact that these request/response hacks are necessary is ridiculous. // We need to eliminate them from env.ASSETS.fetch (not sure if just local or prod as well) @@ -1321,7 +1322,7 @@ export const pages: BuilderCallback = (yargs) => { url.host = `localhost:${proxyPort}`; return await fetch(url, request); } catch (thrown) { - console.error(`Could not proxy request: ${thrown}`); + logger.error(`Could not proxy request: ${thrown}`); // TODO: Pretty error page return new Response( @@ -1333,7 +1334,7 @@ export const pages: BuilderCallback = (yargs) => { try { return await assetsFetch(request); } catch (thrown) { - console.error(`Could not serve static asset: ${thrown}`); + logger.error(`Could not serve static asset: ${thrown}`); // TODO: Pretty error page return new Response( @@ -1356,7 +1357,7 @@ export const pages: BuilderCallback = (yargs) => { try { // `startServer` might throw if user code contains errors const server = await miniflare.startServer(); - console.log(`Serving at http://localhost:${port}/`); + logger.log(`Serving at http://localhost:${port}/`); if (process.env.BROWSER !== "none") { await openInBrowser(`http://localhost:${port}/`); @@ -1446,7 +1447,7 @@ export const pages: BuilderCallback = (yargs) => { plugin, }) => { // Beta message for `wrangler pages ` usage - console.log(pagesBetaWarning); + logger.log(pagesBetaWarning); await buildFunctions({ outfile, @@ -1526,10 +1527,10 @@ export const pages: BuilderCallback = (yargs) => { }), } ); - console.log( + logger.log( `✨ Successfully created the '${name}' project. It will be available at https://${subdomain}/ once you create your first deployment.` ); - console.log( + logger.log( `To deploy a folder of assets, run 'wrangler pages publish [directory]'.` ); } diff --git a/packages/wrangler/src/proxy.ts b/packages/wrangler/src/proxy.ts index d2ad9865e432..204670f395bb 100644 --- a/packages/wrangler/src/proxy.ts +++ b/packages/wrangler/src/proxy.ts @@ -5,6 +5,7 @@ import WebSocket from "faye-websocket"; import { useEffect, useRef, useState } from "react"; import serveStatic from "serve-static"; import { getHttpsOptions } from "./https-options"; +import { logger } from "./logger"; import { reportError } from "./reporting"; import type { CfPreviewToken } from "./create-worker-preview"; import type { @@ -92,7 +93,7 @@ export function usePreviewServer({ createProxyServer(localProtocol) .then((proxy) => setProxyServer(proxy)) .catch(async (err) => { - console.error("Failed to create proxy server.", err); + logger.error("Failed to create proxy server:", err); await reportError(err); }); } @@ -301,11 +302,11 @@ export function usePreviewServer({ }) .then(() => { proxyServer.listen(port, ip); - console.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`); + logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`); }) .catch((err) => { if ((err as { code: string }).code !== "ABORT_ERR") { - console.error(`⬣ Failed to start server: ${err}`); + logger.error(`Failed to start server: ${err}`); } }); @@ -352,7 +353,7 @@ async function createProxyServer( return server .on("request", function (req, res) { // log all requests - console.log( + logger.log( new Date().toLocaleTimeString(), req.method, req.url, @@ -361,7 +362,7 @@ async function createProxyServer( }) .on("upgrade", (req) => { // log all websocket connections - console.log( + logger.log( new Date().toLocaleTimeString(), req.method, req.url, @@ -371,7 +372,7 @@ async function createProxyServer( }) .on("error", (err) => { // log all connection errors - console.error(new Date().toLocaleTimeString(), err); + logger.error(new Date().toLocaleTimeString(), err); }); } diff --git a/packages/wrangler/src/publish.ts b/packages/wrangler/src/publish.ts index 5b2b2327a5f7..154fa61d9772 100644 --- a/packages/wrangler/src/publish.ts +++ b/packages/wrangler/src/publish.ts @@ -6,6 +6,7 @@ import tmp from "tmp-promise"; import { bundleWorker } from "./bundle"; import { fetchResult } from "./cfetch"; import { createWorkerUploadForm } from "./create-worker-upload-form"; +import { logger } from "./logger"; import { syncAssets } from "./sites"; import type { Config } from "./config"; import type { Entry } from "./entry"; @@ -147,7 +148,7 @@ export default async function publish(props: Props): Promise { (binding) => !binding.script_name ); if (exportedDurableObjects.length > 0 && config.migrations.length === 0) { - console.warn( + logger.warn( `In wrangler.toml, you have configured [durable_objects] exported by this Worker (${exportedDurableObjects.map( (durable) => durable.class_name )}), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details.` @@ -173,7 +174,7 @@ export default async function publish(props: Props): Promise { (migration) => migration.tag === script.migration_tag ); if (foundIndex === -1) { - console.warn( + logger.warn( `The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already deleted it. Applying all available migrations to the script...` ); migrations = { @@ -284,7 +285,7 @@ export default async function publish(props: Props): Promise { } if (props.dryRun) { - console.log(`--dry-run: exiting now.`); + logger.log(`--dry-run: exiting now.`); return; } @@ -334,7 +335,7 @@ export default async function publish(props: Props): Promise { } } - console.log("Uploaded", workerName, formatTime(uploadMs)); + logger.log("Uploaded", workerName, formatTime(uploadMs)); // Update routing table for the script. if (routes.length > 0) { @@ -393,12 +394,12 @@ export default async function publish(props: Props): Promise { const deployMs = Date.now() - start - uploadMs; if (deployments.length > 0) { - console.log("Published", workerName, formatTime(deployMs)); + logger.log("Published", workerName, formatTime(deployMs)); for (const target of targets.flat()) { - console.log(" ", target); + logger.log(" ", target); } } else { - console.log("No publish targets for", workerName, formatTime(deployMs)); + logger.log("No publish targets for", workerName, formatTime(deployMs)); } } diff --git a/packages/wrangler/src/reporting.ts b/packages/wrangler/src/reporting.ts index 0fbc19a3f18b..aa75e4ab02fa 100644 --- a/packages/wrangler/src/reporting.ts +++ b/packages/wrangler/src/reporting.ts @@ -14,6 +14,7 @@ import { import { execaSync } from "execa"; import prompts from "prompts"; import * as pkj from "../package.json"; +import { logger } from "./logger"; import { parseTOML } from "./parse"; export function initReporting() { @@ -75,7 +76,7 @@ async function appendReportingDecision(userInput: "true" | "false") { "utf-8" ); } catch (err) { - console.warn( + logger.warn( `Unable to create wrangler's error reporting configuration file at ${reportingConfigLocation}: `, err ); diff --git a/packages/wrangler/src/sites.tsx b/packages/wrangler/src/sites.tsx index 37477b352de8..7bdcfc5b8b08 100644 --- a/packages/wrangler/src/sites.tsx +++ b/packages/wrangler/src/sites.tsx @@ -9,6 +9,7 @@ import { putBulkKeyValue, deleteBulkKeyValue, } from "./kv"; +import { logger } from "./logger"; import type { Config } from "./config"; import type { KeyValue } from "./kv"; import type { XXHashAPI } from "xxhash-wasm"; @@ -82,7 +83,7 @@ async function createKVNamespaceIfNotAlreadyExisting( // else we make the namespace const id = await createNamespace(accountId, title); - console.log(`🌀 Created namespace for Workers Site "${title}"`); + logger.log(`🌀 Created namespace for Workers Site "${title}"`); return { created: true, @@ -116,7 +117,7 @@ export async function syncAssets( } if (dryRun) { - console.log("(note: doing a dry run, not uploading or deleting anything.)"); + logger.log("(Note: doing a dry run, not uploading or deleting anything.)"); return { manifest: undefined, namespace: undefined }; } @@ -154,7 +155,7 @@ export async function syncAssets( } await validateAssetSize(absAssetFile, assetFile); - console.log(`reading ${assetFile}...`); + logger.log(`Reading ${assetFile}...`); const content = await readFile(absAssetFile, "base64"); const assetKey = hashAsset(hasher, assetFile, content); @@ -162,14 +163,14 @@ export async function syncAssets( // now put each of the files into kv if (!namespaceKeys.has(assetKey)) { - console.log(`uploading as ${assetKey}...`); + logger.log(`Uploading as ${assetKey}...`); toUpload.push({ key: assetKey, value: content, base64: true, }); } else { - console.log(`skipping - already uploaded`); + logger.log(`Skipping - already uploaded.`); } // remove the key from the set so we know what we've already uploaded @@ -179,7 +180,7 @@ export async function syncAssets( // keys now contains all the files we're deleting for (const key of namespaceKeys) { - console.log(`deleting ${key} from the asset store...`); + logger.log(`Deleting ${key} from the asset store...`); } await Promise.all([ @@ -194,7 +195,7 @@ export async function syncAssets( ), ]); - console.log("↗️ Done syncing assets"); + logger.log("↗️ Done syncing assets"); return { manifest, namespace }; } diff --git a/packages/wrangler/src/tail/printing.ts b/packages/wrangler/src/tail/printing.ts index 0f7327f72a1c..1db9ce684007 100644 --- a/packages/wrangler/src/tail/printing.ts +++ b/packages/wrangler/src/tail/printing.ts @@ -1,3 +1,4 @@ +import { logger } from "../logger"; import type { RequestEvent, ScheduledEvent, TailEventMessage } from "."; import type { Outcome } from "./filters"; import type WebSocket from "ws"; @@ -12,25 +13,25 @@ export function prettyPrintLogs(data: WebSocket.RawData): void { ).toLocaleString(); const outcome = prettifyOutcome(eventMessage.outcome); - console.log(`"${cronPattern}" @ ${datetime} - ${outcome}`); + logger.log(`"${cronPattern}" @ ${datetime} - ${outcome}`); } else { const requestMethod = eventMessage.event.request.method.toUpperCase(); const url = eventMessage.event.request.url; const outcome = prettifyOutcome(eventMessage.outcome); const datetime = new Date(eventMessage.eventTimestamp).toLocaleString(); - console.log(`${requestMethod} ${url} - ${outcome} @ ${datetime}`); + logger.log(`${requestMethod} ${url} - ${outcome} @ ${datetime}`); } if (eventMessage.logs.length > 0) { eventMessage.logs.forEach(({ level, message }) => { - console.log(` (${level})`, ...message); + logger.log(` (${level})`, ...message); }); } if (eventMessage.exceptions.length > 0) { eventMessage.exceptions.forEach(({ name, message }) => { - console.error(` ${name}:`, message); + logger.error(` ${name}:`, message); }); } } diff --git a/packages/wrangler/src/user.tsx b/packages/wrangler/src/user.tsx index 7fc5a997f2e8..297b18dbe69a 100644 --- a/packages/wrangler/src/user.tsx +++ b/packages/wrangler/src/user.tsx @@ -221,6 +221,7 @@ import React from "react"; import { fetch } from "undici"; import { getCloudflareApiBaseUrl } from "./cfetch"; import { getEnvironmentVariableFactory } from "./environment-variables"; +import { logger } from "./logger"; import openInBrowser from "./open-in-browser"; import { parseTOML, readFileSync } from "./parse"; import type { Config } from "./config"; @@ -403,7 +404,7 @@ export function reinitialiseAuthTokens(config?: UserAuthConfig): void { export function getAPIToken(): string | undefined { if (LocalState.apiToken) { - console.warn( + logger.warn( "It looks like you have used Wrangler 1's `config` command to login with an API token.\n" + "This is no longer supported in the current version of Wrangler.\n" + "If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable." @@ -596,8 +597,8 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean { const stateQueryParam = query.state; if (stateQueryParam !== state.stateQueryParam) { - console.warn( - "state query string parameter doesn't match the one sent! Possible malicious activity somewhere." + logger.warn( + "Received query string parameter doesn't match the one sent! Possible malicious activity somewhere." ); throw new ErrorInvalidReturnedStateParam(); } @@ -646,7 +647,7 @@ type TokenResponse = */ async function exchangeRefreshTokenForAccessToken(): Promise { if (!LocalState.refreshToken) { - console.warn("No refresh token is present."); + logger.warn("No refresh token is present."); } const body = @@ -734,9 +735,9 @@ async function exchangeAuthCodeForAccessToken(): Promise { const { authorizationCode, codeVerifier = "" } = LocalState; if (!codeVerifier) { - console.warn("No code verifier is being sent."); + logger.warn("No code verifier is being sent."); } else if (!authorizationCode) { - console.warn("No authorization grant code is being passed."); + logger.warn("No authorization grant code is being passed."); } const body = @@ -757,7 +758,7 @@ async function exchangeAuthCodeForAccessToken(): Promise { const { error } = (await response.json()) as { error: string }; // .catch((_) => ({ error: "invalid_json" })); if (error === "invalid_grant") { - console.log("Expired! Auth code or refresh token needs to be renewed."); + logger.log("Expired! Auth code or refresh token needs to be renewed."); // alert("Redirecting to auth server to obtain a new auth grant code."); // TODO: return refreshAuthCodeOrRefreshToken(); } @@ -905,13 +906,13 @@ export async function loginOrRefreshIfRequired( } export async function login(props?: LoginProps): Promise { - console.log("Attempting to login via OAuth..."); + logger.log("Attempting to login via OAuth..."); const urlToOpen = await getAuthURL(props?.scopes); let server: http.Server; let loginTimeoutHandle: NodeJS.Timeout; const timerPromise = new Promise((resolve) => { loginTimeoutHandle = setTimeout(() => { - console.error( + logger.error( "Timed out waiting for authorization code, please try again." ); server.close(); @@ -947,7 +948,7 @@ export async function login(props?: LoginProps): Promise { res.end(() => { finish(false); }); - console.log( + logger.error( "Error: Consent denied. You must grant consent to Wrangler in order to login.\n" + "If you don't want to do this consider passing an API token via the `CLOUDFLARE_API_TOKEN` environment variable" ); @@ -976,7 +977,7 @@ export async function login(props?: LoginProps): Promise { res.end(() => { finish(true); }); - console.log(`Successfully logged in.`); + logger.log(`Successfully logged in.`); return; } @@ -1020,7 +1021,7 @@ async function refreshToken(): Promise { export async function logout(): Promise { if (!LocalState.accessToken) { if (!LocalState.refreshToken) { - console.log("Not logged in, exiting..."); + logger.log("Not logged in, exiting..."); return; } @@ -1037,7 +1038,7 @@ export async function logout(): Promise { }, }); await response.text(); // blank text? would be nice if it was something meaningful - console.log( + logger.log( "💁 Wrangler is configured with an OAuth token. The token has been successfully revoked" ); } @@ -1055,11 +1056,11 @@ export async function logout(): Promise { }); await response.text(); // blank text? would be nice if it was something meaningful rmSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE)); - console.log(`Successfully logged out.`); + logger.log(`Successfully logged out.`); } export function listScopes(message = "💁 Available scopes:"): void { - console.log(message); + logger.log(message); const data = ScopeKeys.map((scope: Scope) => ({ Scope: scope, Description: Scopes[scope], diff --git a/packages/wrangler/src/whoami.tsx b/packages/wrangler/src/whoami.tsx index 241e88566ccc..8f38c41d024f 100644 --- a/packages/wrangler/src/whoami.tsx +++ b/packages/wrangler/src/whoami.tsx @@ -2,10 +2,11 @@ import { Text, render } from "ink"; import Table from "ink-table"; import React from "react"; import { fetchListResult, fetchResult } from "./cfetch"; +import { logger } from "./logger"; import { getAPIToken } from "./user"; export async function whoami() { - console.log("Getting User settings..."); + logger.log("Getting User settings..."); const user = await getUserInfo(); render(); }