Skip to content

Commit

Permalink
Fixes argument handling for invariant log messages. (#11266)
Browse files Browse the repository at this point in the history
Co-authored-by: Jerel Miller <jerelmiller@gmail.com>
  • Loading branch information
phryneas and jerelmiller authored Oct 5, 2023
1 parent 366e2b1 commit 5192cf6
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .api-reports/api-report-utilities_globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type WrappedInvariant = {

// Warnings were encountered during analysis:
//
// src/utilities/globals/invariantWrappers.ts:58:3 - (ae-forgotten-export) The symbol "LogFunction" needs to be exported by the entry point index.d.ts
// src/utilities/globals/invariantWrappers.ts:62:3 - (ae-forgotten-export) The symbol "LogFunction" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
5 changes: 5 additions & 0 deletions .changeset/cyan-laws-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Fixes argument handling for invariant log messages.
4 changes: 2 additions & 2 deletions .size-limit.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const checks = [
{
path: "dist/apollo-client.min.cjs",
limit: "37914",
limit: "37940",
},
{
path: "dist/main.cjs",
Expand All @@ -10,7 +10,7 @@ const checks = [
{
path: "dist/index.js",
import: "{ ApolloClient, InMemoryCache, HttpLink }",
limit: "31947",
limit: "31970",
},
...[
"ApolloProvider",
Expand Down
2 changes: 1 addition & 1 deletion src/dev/loadErrorMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function loadErrorMessageHandler(...errorCodes: ErrorCodes[]) {
message = definition.message;
}
return args.reduce<string>(
(msg, arg) => msg.replace("%s", String(arg)),
(msg, arg) => msg.replace(/%[sdfo]/, String(arg)),
String(message)
);
}
Expand Down
101 changes: 101 additions & 0 deletions src/utilities/globals/__tests__/invariantWrappers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { loadErrorMessageHandler } from "../../../dev";
import { spyOnConsole, withCleanup } from "../../../testing/internal";
import {
ApolloErrorMessageHandler,
InvariantError,
invariant,
} from "../invariantWrappers";

function disableErrorMessageHandler() {
const original = window[ApolloErrorMessageHandler];
delete window[ApolloErrorMessageHandler];
return withCleanup({ original }, ({ original }) => {
window[ApolloErrorMessageHandler] = original;
});
}

function mockErrorMessageHandler() {
const original = window[ApolloErrorMessageHandler];
delete window[ApolloErrorMessageHandler];

loadErrorMessageHandler({
5: { file: "foo", message: "Replacing %s, %d, %f, %o" },
});

return withCleanup({ original }, ({ original }) => {
window[ApolloErrorMessageHandler] = original;
});
}

test("base invariant(false, 5, ...), no handlers", () => {
using _ = disableErrorMessageHandler();
expect(() => {
invariant(false, 5, "string", 1, 1.1, { a: 1 });
}).toThrow(
new InvariantError(
"An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#" +
encodeURIComponent(
JSON.stringify({
version: "local",
message: 5,
args: [
"string",
"1",
"1.1",
JSON.stringify({ a: 1 }, undefined, 2),
],
})
)
)
);
});

test("base invariant(false, 5, ...), handlers in place", () => {
using _ = mockErrorMessageHandler();
expect(() => {
invariant(false, 5, "string", 1, 1.1, { a: 1 });
}).toThrow(new InvariantError('Replacing string, 1, 1.1, {\n "a": 1\n}'));
});

test("base invariant(false, undefined), no handlers", () => {
using _ = disableErrorMessageHandler();
expect(() => {
invariant(false);
}).toThrow(new InvariantError("Invariant Violation"));
});

test("base invariant(false, undefined), handlers in place", () => {
using _ = mockErrorMessageHandler();
expect(() => {
invariant(false);
}).toThrow(new InvariantError("Invariant Violation"));
});

test("invariant.log(5, ...), no handlers", () => {
using _ = disableErrorMessageHandler();
using consoleSpy = spyOnConsole("log");
invariant.log(5, "string", 1, 1.1, { a: 1 });
expect(consoleSpy.log).toHaveBeenCalledWith(
"An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#" +
encodeURIComponent(
JSON.stringify({
version: "local",
message: 5,
args: ["string", "1", "1.1", JSON.stringify({ a: 1 }, undefined, 2)],
})
)
);
});

test("invariant.log(5, ...), with handlers", () => {
using _ = mockErrorMessageHandler();
using consoleSpy = spyOnConsole("log");
invariant.log(5, "string", 1, 1.1, { a: 1 });
expect(consoleSpy.log).toHaveBeenCalledWith(
"Replacing %s, %d, %f, %o",
"string",
1,
1.1,
{ a: 1 }
);
});
61 changes: 42 additions & 19 deletions src/utilities/globals/invariantWrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import type { ErrorCodes } from "../../invariantErrorCodes.js";
import { stringifyForDisplay } from "../common/stringifyForDisplay.js";

function wrap(fn: (msg?: string, ...args: any[]) => void) {
return function (message: string | number, ...args: any[]) {
return function (message?: string | number, ...args: any[]) {
if (typeof message === "number") {
fn(getErrorMsg(message, args));
} else {
fn(message, ...args);
const arg0 = message;
message = getHandledErrorMsg(arg0);
if (!message) {
message = getFallbackErrorMsg(arg0, args);
args = [];
}
}
fn(...[message].concat(args));
};
}

Expand Down Expand Up @@ -67,7 +71,10 @@ const invariant: WrappedInvariant = Object.assign(
...args: unknown[]
): asserts condition {
if (!condition) {
originalInvariant(condition, getErrorMsg(message, args));
originalInvariant(
condition,
getHandledErrorMsg(message, args) || getFallbackErrorMsg(message, args)
);
}
},
{
Expand All @@ -92,7 +99,10 @@ function newInvariantError(
message?: string | number,
...optionalParams: unknown[]
) {
return new InvariantError(getErrorMsg(message, optionalParams));
return new InvariantError(
getHandledErrorMsg(message, optionalParams) ||
getFallbackErrorMsg(message, optionalParams)
);
}

const ApolloErrorMessageHandler = Symbol.for(
Expand All @@ -106,24 +116,37 @@ declare global {
}
}

function getErrorMsg(message?: string | number, messageArgs: unknown[] = []) {
function stringify(arg: any) {
return typeof arg == "string"
? arg
: stringifyForDisplay(arg, 2).slice(0, 1000);
}

function getHandledErrorMsg(
message?: string | number,
messageArgs: unknown[] = []
) {
if (!message) return;
const args = messageArgs.map((arg) =>
typeof arg == "string" ? arg : stringifyForDisplay(arg, 2).slice(0, 1000)
);
return (
(global[ApolloErrorMessageHandler] &&
global[ApolloErrorMessageHandler](message, args)) ||
`An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#${encodeURIComponent(
JSON.stringify({
version,
message,
args,
})
)}`
global[ApolloErrorMessageHandler] &&
global[ApolloErrorMessageHandler](message, messageArgs.map(stringify))
);
}

function getFallbackErrorMsg(
message?: string | number,
messageArgs: unknown[] = []
) {
if (!message) return;
return `An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#${encodeURIComponent(
JSON.stringify({
version,
message,
args: messageArgs.map(stringify),
})
)}`;
}

export {
invariant,
InvariantError,
Expand Down

0 comments on commit 5192cf6

Please sign in to comment.