mirror of
https://github.com/facebook/lexical.git
synced 2025-05-17 06:59:17 +08:00
[lexical] Chore: Change $getTextNodeOffset invariant to warn in prod (error in __DEV__) (#7397)
This commit is contained in:
@ -161,6 +161,7 @@
|
|||||||
"@lexical/yjs": ["../lexical-yjs/src/index.ts"],
|
"@lexical/yjs": ["../lexical-yjs/src/index.ts"],
|
||||||
"shared/canUseDOM": ["../shared/src/canUseDOM.ts"],
|
"shared/canUseDOM": ["../shared/src/canUseDOM.ts"],
|
||||||
"shared/caretFromPoint": ["../shared/src/caretFromPoint.ts"],
|
"shared/caretFromPoint": ["../shared/src/caretFromPoint.ts"],
|
||||||
|
"shared/devInvariant": ["../shared/src/devInvariant.ts"],
|
||||||
"shared/environment": ["../shared/src/environment.ts"],
|
"shared/environment": ["../shared/src/environment.ts"],
|
||||||
"shared/formatDevErrorMessage": [
|
"shared/formatDevErrorMessage": [
|
||||||
"../shared/src/formatDevErrorMessage.ts"
|
"../shared/src/formatDevErrorMessage.ts"
|
||||||
@ -168,6 +169,9 @@
|
|||||||
"shared/formatProdErrorMessage": [
|
"shared/formatProdErrorMessage": [
|
||||||
"../shared/src/formatProdErrorMessage.ts"
|
"../shared/src/formatProdErrorMessage.ts"
|
||||||
],
|
],
|
||||||
|
"shared/formatProdWarningMessage": [
|
||||||
|
"../shared/src/formatProdWarningMessage.ts"
|
||||||
|
],
|
||||||
"shared/invariant": ["../shared/src/invariant.ts"],
|
"shared/invariant": ["../shared/src/invariant.ts"],
|
||||||
"shared/normalizeClassNames": ["../shared/src/normalizeClassNames.ts"],
|
"shared/normalizeClassNames": ["../shared/src/normalizeClassNames.ts"],
|
||||||
"shared/react-test-utils": ["../shared/src/react-test-utils.ts"],
|
"shared/react-test-utils": ["../shared/src/react-test-utils.ts"],
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import type {LexicalNode, NodeKey} from '../LexicalNode';
|
import type {LexicalNode, NodeKey} from '../LexicalNode';
|
||||||
|
|
||||||
|
import devInvariant from 'shared/devInvariant';
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
|
|
||||||
import {$getRoot, $isRootOrShadowRoot} from '../LexicalUtils';
|
import {$getRoot, $isRootOrShadowRoot} from '../LexicalUtils';
|
||||||
@ -890,7 +891,7 @@ export function $getTextPointCaret(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a normalized offset into a TextNode given a numeric offset or a
|
* Get a normalized offset into a TextNode given a numeric offset or a
|
||||||
* direction for which end of the string to use. Throws if the offset
|
* direction for which end of the string to use. Throws in dev if the offset
|
||||||
* is not in the bounds of the text content size.
|
* is not in the bounds of the text content size.
|
||||||
*
|
*
|
||||||
* @param origin a TextNode
|
* @param origin a TextNode
|
||||||
@ -902,14 +903,19 @@ export function $getTextNodeOffset(
|
|||||||
offset: number | CaretDirection,
|
offset: number | CaretDirection,
|
||||||
): number {
|
): number {
|
||||||
const size = origin.getTextContentSize();
|
const size = origin.getTextContentSize();
|
||||||
const numericOffset =
|
let numericOffset =
|
||||||
offset === 'next' ? size : offset === 'previous' ? 0 : offset;
|
offset === 'next' ? size : offset === 'previous' ? 0 : offset;
|
||||||
invariant(
|
if (numericOffset < 0 || numericOffset > size) {
|
||||||
numericOffset >= 0 && numericOffset <= size,
|
devInvariant(
|
||||||
'$getTextNodeOffset: invalid offset %s for size %s',
|
false,
|
||||||
String(offset),
|
'$getTextNodeOffset: invalid offset %s for size %s at key %s',
|
||||||
String(size),
|
String(offset),
|
||||||
);
|
String(size),
|
||||||
|
origin.getKey(),
|
||||||
|
);
|
||||||
|
// Clamp invalid offsets in prod
|
||||||
|
numericOffset = numericOffset < 0 ? 0 : size;
|
||||||
|
}
|
||||||
return numericOffset;
|
return numericOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
packages/shared/src/__mocks__/devInvariant.ts
Normal file
21
packages/shared/src/__mocks__/devInvariant.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function devInvariant(
|
||||||
|
cond?: boolean,
|
||||||
|
message?: string,
|
||||||
|
...args: string[]
|
||||||
|
): void {
|
||||||
|
if (cond) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
args.reduce((msg, arg) => msg.replace('%s', String(arg)), message || ''),
|
||||||
|
);
|
||||||
|
}
|
26
packages/shared/src/devInvariant.ts
Normal file
26
packages/shared/src/devInvariant.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `!cond` throw in `__DEV__` like an invariant and warn in prod
|
||||||
|
*/
|
||||||
|
export default function devInvariant(
|
||||||
|
cond?: boolean,
|
||||||
|
message?: string,
|
||||||
|
...args: string[]
|
||||||
|
): void {
|
||||||
|
if (cond) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
'Internal Lexical error: devInvariant() is meant to be replaced at compile ' +
|
||||||
|
'time. There is no runtime version. Error: ' +
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
}
|
24
packages/shared/src/formatProdWarningMessage.ts
Normal file
24
packages/shared/src/formatProdWarningMessage.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function prodWarningMessage(
|
||||||
|
code: string,
|
||||||
|
...args: string[]
|
||||||
|
): void {
|
||||||
|
const url = new URL('https://lexical.dev/docs/error');
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('code', code);
|
||||||
|
for (const arg of args) {
|
||||||
|
params.append('v', arg);
|
||||||
|
}
|
||||||
|
url.search = params.toString();
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
`Minified Lexical warning #${code}; visit ${url.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`,
|
||||||
|
);
|
||||||
|
}
|
@ -53,10 +53,10 @@ function fmt(strings: TemplateStringsArray, ...keys: unknown[]) {
|
|||||||
.replace(/.use strict.;\n/g, '')
|
.replace(/.use strict.;\n/g, '')
|
||||||
.replace(/var _[^;]+;\n/g, '')
|
.replace(/var _[^;]+;\n/g, '')
|
||||||
.replace(/function _interopRequireDefault\([^)]*\) {[^;]+?;[\s\n]*}\n/g, '')
|
.replace(/function _interopRequireDefault\([^)]*\) {[^;]+?;[\s\n]*}\n/g, '')
|
||||||
.replace(/_format(Dev|Prod)ErrorMessage\d+/g, 'format$1ErrorMessage')
|
.replace(/_format(Dev|Prod)(Error|Warning)Message\d+/g, 'format$1$2Message')
|
||||||
.replace(
|
.replace(
|
||||||
/\(0,\s*format(Dev|Prod)ErrorMessage\.default\)/g,
|
/\(0,\s*format(Dev|Prod)(Error|Warning)Message\.default\)/g,
|
||||||
'format$1ErrorMessage',
|
'format$1$2Message',
|
||||||
)
|
)
|
||||||
.trim();
|
.trim();
|
||||||
return prettier.format(before, {
|
return prettier.format(before, {
|
||||||
@ -98,77 +98,155 @@ async function expectTransform(opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('transform-error-messages', () => {
|
describe('transform-error-messages', () => {
|
||||||
describe('{extractCodes: true, noMinify: false}', () => {
|
describe('invariant', () => {
|
||||||
const opts = {extractCodes: true, noMinify: false};
|
describe('{extractCodes: true, noMinify: false}', () => {
|
||||||
it('inserts known and extracts unknown message codes', async () => {
|
const opts = {extractCodes: true, noMinify: false};
|
||||||
await expectTransform({
|
it('inserts known and extracts unknown message codes', async () => {
|
||||||
codeBefore: `
|
await expectTransform({
|
||||||
|
codeBefore: `
|
||||||
invariant(condition, ${JSON.stringify(NEW_MSG)});
|
invariant(condition, ${JSON.stringify(NEW_MSG)});
|
||||||
invariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
invariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
||||||
`,
|
`,
|
||||||
codeExpect: `
|
codeExpect: `
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatProdErrorMessage(1);
|
formatProdErrorMessage(1);
|
||||||
}
|
}
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatProdErrorMessage(0, adj, noun);
|
formatProdErrorMessage(0, adj, noun);
|
||||||
}`,
|
}`,
|
||||||
messageMapBefore: KNOWN_MSG_MAP,
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
messageMapExpect: NEW_MSG_MAP,
|
messageMapExpect: NEW_MSG_MAP,
|
||||||
opts,
|
opts,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
describe('{extractCodes: true, noMinify: true}', () => {
|
||||||
describe('{extractCodes: true, noMinify: true}', () => {
|
const opts = {extractCodes: true, noMinify: true};
|
||||||
const opts = {extractCodes: true, noMinify: true};
|
it('inserts known and extracts unknown message codes', async () => {
|
||||||
it('inserts known and extracts unknown message codes', async () => {
|
await expectTransform({
|
||||||
await expectTransform({
|
codeBefore: `
|
||||||
codeBefore: `
|
|
||||||
invariant(condition, ${JSON.stringify(NEW_MSG)});
|
invariant(condition, ${JSON.stringify(NEW_MSG)});
|
||||||
invariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
invariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
||||||
`,
|
`,
|
||||||
codeExpect: `
|
codeExpect: `
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatDevErrorMessage(\`A new invariant\`);
|
formatDevErrorMessage(\`A new invariant\`);
|
||||||
}
|
}
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatDevErrorMessage(\`A \${adj} message that contains \${noun}\`);
|
formatDevErrorMessage(\`A \${adj} message that contains \${noun}\`);
|
||||||
}`,
|
}`,
|
||||||
messageMapBefore: KNOWN_MSG_MAP,
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
messageMapExpect: NEW_MSG_MAP,
|
messageMapExpect: NEW_MSG_MAP,
|
||||||
opts,
|
opts,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
describe('{extractCodes: false, noMinify: false}', () => {
|
||||||
describe('{extractCodes: false, noMinify: false}', () => {
|
const opts = {extractCodes: false, noMinify: false};
|
||||||
const opts = {extractCodes: false, noMinify: false};
|
it('inserts known message', async () => {
|
||||||
it('inserts known message', async () => {
|
await expectTransform({
|
||||||
await expectTransform({
|
codeBefore: `invariant(condition, ${JSON.stringify(
|
||||||
codeBefore: `invariant(condition, ${JSON.stringify(
|
KNOWN_MSG,
|
||||||
KNOWN_MSG,
|
)}, adj, noun)`,
|
||||||
)}, adj, noun)`,
|
codeExpect: `
|
||||||
codeExpect: `
|
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatProdErrorMessage(0, adj, noun);
|
formatProdErrorMessage(0, adj, noun);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
messageMapBefore: KNOWN_MSG_MAP,
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
messageMapExpect: KNOWN_MSG_MAP,
|
messageMapExpect: KNOWN_MSG_MAP,
|
||||||
opts,
|
opts,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
it('inserts warning comment for unknown messages', async () => {
|
||||||
it('inserts warning comment for unknown messages', async () => {
|
await expectTransform({
|
||||||
await expectTransform({
|
codeBefore: `invariant(condition, ${JSON.stringify(NEW_MSG)})`,
|
||||||
codeBefore: `invariant(condition, ${JSON.stringify(NEW_MSG)})`,
|
codeExpect: `
|
||||||
codeExpect: `
|
|
||||||
/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
|
/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
formatDevErrorMessage(\`A new invariant\`);
|
formatDevErrorMessage(\`A new invariant\`);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
messageMapBefore: KNOWN_MSG_MAP,
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
messageMapExpect: KNOWN_MSG_MAP,
|
messageMapExpect: KNOWN_MSG_MAP,
|
||||||
opts,
|
opts,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('devInvariant', () => {
|
||||||
|
describe('{extractCodes: true, noMinify: false}', () => {
|
||||||
|
const opts = {extractCodes: true, noMinify: false};
|
||||||
|
it('inserts known and extracts unknown message codes', async () => {
|
||||||
|
await expectTransform({
|
||||||
|
codeBefore: `
|
||||||
|
devInvariant(condition, ${JSON.stringify(NEW_MSG)});
|
||||||
|
devInvariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
||||||
|
`,
|
||||||
|
codeExpect: `
|
||||||
|
if (!condition) {
|
||||||
|
formatProdWarningMessage(1);
|
||||||
|
}
|
||||||
|
if (!condition) {
|
||||||
|
formatProdWarningMessage(0, adj, noun);
|
||||||
|
}`,
|
||||||
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
|
messageMapExpect: NEW_MSG_MAP,
|
||||||
|
opts,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('{extractCodes: true, noMinify: true}', () => {
|
||||||
|
const opts = {extractCodes: true, noMinify: true};
|
||||||
|
it('inserts known and extracts unknown message codes', async () => {
|
||||||
|
await expectTransform({
|
||||||
|
codeBefore: `
|
||||||
|
devInvariant(condition, ${JSON.stringify(NEW_MSG)});
|
||||||
|
devInvariant(condition, ${JSON.stringify(KNOWN_MSG)}, adj, noun);
|
||||||
|
`,
|
||||||
|
codeExpect: `
|
||||||
|
if (!condition) {
|
||||||
|
formatDevErrorMessage(\`A new invariant\`);
|
||||||
|
}
|
||||||
|
if (!condition) {
|
||||||
|
formatDevErrorMessage(\`A \${adj} message that contains \${noun}\`);
|
||||||
|
}`,
|
||||||
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
|
messageMapExpect: NEW_MSG_MAP,
|
||||||
|
opts,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('{extractCodes: false, noMinify: false}', () => {
|
||||||
|
const opts = {extractCodes: false, noMinify: false};
|
||||||
|
it('inserts known message', async () => {
|
||||||
|
await expectTransform({
|
||||||
|
codeBefore: `devInvariant(condition, ${JSON.stringify(
|
||||||
|
KNOWN_MSG,
|
||||||
|
)}, adj, noun)`,
|
||||||
|
codeExpect: `
|
||||||
|
if (!condition) {
|
||||||
|
formatProdWarningMessage(0, adj, noun);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
|
messageMapExpect: KNOWN_MSG_MAP,
|
||||||
|
opts,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('inserts warning comment for unknown messages', async () => {
|
||||||
|
await expectTransform({
|
||||||
|
codeBefore: `devInvariant(condition, ${JSON.stringify(NEW_MSG)})`,
|
||||||
|
codeExpect: `
|
||||||
|
/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
|
||||||
|
if (!condition) {
|
||||||
|
formatDevErrorMessage(\`A new invariant\`);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
messageMapBefore: KNOWN_MSG_MAP,
|
||||||
|
messageMapExpect: KNOWN_MSG_MAP,
|
||||||
|
opts,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -50,6 +50,19 @@ function getErrorMap(filepath) {
|
|||||||
* @property {boolean} noMinify
|
* @property {boolean} noMinify
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const invariantExpressions = [
|
||||||
|
{
|
||||||
|
dev: 'formatDevErrorMessage',
|
||||||
|
name: 'invariant',
|
||||||
|
prod: 'formatProdErrorMessage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dev: 'formatDevErrorMessage',
|
||||||
|
name: 'devInvariant',
|
||||||
|
prod: 'formatProdWarningMessage',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('@babel/core')} babel
|
* @param {import('@babel/core')} babel
|
||||||
* @param {Partial<TransformErrorMessagesOptions>} opts
|
* @param {Partial<TransformErrorMessagesOptions>} opts
|
||||||
@ -66,120 +79,117 @@ module.exports = function (babel, opts) {
|
|||||||
const node = path.node;
|
const node = path.node;
|
||||||
const {extractCodes, noMinify} =
|
const {extractCodes, noMinify} =
|
||||||
/** @type Partial<TransformErrorMessagesOptions> */ (file.opts);
|
/** @type Partial<TransformErrorMessagesOptions> */ (file.opts);
|
||||||
if (path.get('callee').isIdentifier({name: 'invariant'})) {
|
for (const {name, dev, prod} of invariantExpressions) {
|
||||||
// Turns this code:
|
if (path.get('callee').isIdentifier({name})) {
|
||||||
//
|
// Turns this code:
|
||||||
// invariant(condition, 'A %s message that contains %s', adj, noun);
|
|
||||||
//
|
|
||||||
// into something equivalent to this:
|
|
||||||
//
|
|
||||||
// if (!condition) {
|
|
||||||
// if (__DEV__ || ERR_CODE === undefined) {
|
|
||||||
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
|
||||||
// } else {
|
|
||||||
// formatProdErrorMessage(ERR_CODE, adj, noun)
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// where ERR_CODE is an error code: a unique identifier (a number
|
|
||||||
// string) that references a verbose error message. The mapping is
|
|
||||||
// stored in `scripts/error-codes/codes.json`.
|
|
||||||
const condition = node.arguments[0];
|
|
||||||
const errorMsgLiteral = evalToString(node.arguments[1]);
|
|
||||||
const errorMsgExpressions = Array.from(node.arguments.slice(2));
|
|
||||||
const errorMsgQuasis = errorMsgLiteral
|
|
||||||
.split('%s')
|
|
||||||
.map((raw) => t.templateElement({cooked: String.raw({raw}), raw}));
|
|
||||||
|
|
||||||
// Outputs:
|
|
||||||
// `A ${adj} message that contains ${noun}`;
|
|
||||||
const devMessage = t.templateLiteral(
|
|
||||||
errorMsgQuasis,
|
|
||||||
errorMsgExpressions,
|
|
||||||
);
|
|
||||||
|
|
||||||
const parentStatementPath = path.parentPath;
|
|
||||||
if (parentStatementPath.type !== 'ExpressionStatement') {
|
|
||||||
throw path.buildCodeFrameError(
|
|
||||||
'invariant() cannot be called from expression context. Move ' +
|
|
||||||
'the call to its own statement.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We extract the prodErrorId even if we are not using it
|
|
||||||
// so we can extractCodes in a non-production build.
|
|
||||||
let prodErrorId = errorMap.getOrAddToErrorMap(
|
|
||||||
errorMsgLiteral,
|
|
||||||
extractCodes,
|
|
||||||
);
|
|
||||||
|
|
||||||
/** @type {babel.types.CallExpression} */
|
|
||||||
let callExpression;
|
|
||||||
if (noMinify || prodErrorId === undefined) {
|
|
||||||
// Error minification is disabled for this build.
|
|
||||||
//
|
//
|
||||||
// Outputs:
|
// invariant(condition, 'A %s message that contains %s', adj, noun);
|
||||||
// if (!condition) {
|
//
|
||||||
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
// into something equivalent to this:
|
||||||
// }
|
|
||||||
const formatDevErrorMessageIdentifier =
|
|
||||||
helperModuleImports.addDefault(
|
|
||||||
path,
|
|
||||||
'shared/formatDevErrorMessage',
|
|
||||||
{
|
|
||||||
nameHint: 'formatDevErrorMessage',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
callExpression = t.callExpression(formatDevErrorMessageIdentifier, [
|
|
||||||
devMessage,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
// Error minification enabled for this build.
|
|
||||||
//
|
//
|
||||||
// Outputs:
|
|
||||||
// if (!condition) {
|
// if (!condition) {
|
||||||
// formatProdErrorMessage(ERR_CODE, adj, noun)
|
// if (__DEV__ || ERR_CODE === undefined) {
|
||||||
|
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
||||||
|
// } else {
|
||||||
|
// formatProdErrorMessage(ERR_CODE, adj, noun)
|
||||||
|
// };
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
// Import ReactErrorProd
|
// where ERR_CODE is an error code: a unique identifier (a number
|
||||||
const formatProdErrorMessageIdentifier =
|
// string) that references a verbose error message. The mapping is
|
||||||
helperModuleImports.addDefault(
|
// stored in `scripts/error-codes/codes.json`.
|
||||||
path,
|
const condition = node.arguments[0];
|
||||||
'shared/formatProdErrorMessage',
|
const errorMsgLiteral = evalToString(node.arguments[1]);
|
||||||
{
|
const errorMsgExpressions = Array.from(node.arguments.slice(2));
|
||||||
nameHint: 'formatProdErrorMessage',
|
const errorMsgQuasis = errorMsgLiteral
|
||||||
},
|
.split('%s')
|
||||||
|
.map((raw) =>
|
||||||
|
t.templateElement({cooked: String.raw({raw}), raw}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Outputs:
|
// Outputs:
|
||||||
// formatProdErrorMessage(ERR_CODE, adj, noun);
|
// `A ${adj} message that contains ${noun}`;
|
||||||
callExpression = t.callExpression(
|
const devMessage = t.templateLiteral(
|
||||||
formatProdErrorMessageIdentifier,
|
errorMsgQuasis,
|
||||||
[t.numericLiteral(prodErrorId), ...errorMsgExpressions],
|
errorMsgExpressions,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
parentStatementPath.replaceWith(
|
const parentStatementPath = path.parentPath;
|
||||||
t.ifStatement(
|
if (parentStatementPath.type !== 'ExpressionStatement') {
|
||||||
t.unaryExpression('!', condition),
|
throw path.buildCodeFrameError(
|
||||||
t.blockStatement([t.expressionStatement(callExpression)]),
|
`${name}() cannot be called from expression context. Move the call to its own statement.`,
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
if (!noMinify && prodErrorId === undefined) {
|
// We extract the prodErrorId even if we are not using it
|
||||||
// There is no error code for this message. Add an inline comment
|
// so we can extractCodes in a non-production build.
|
||||||
// that flags this as an unminified error. This allows the build
|
let prodErrorId = errorMap.getOrAddToErrorMap(
|
||||||
// to proceed, while also allowing a post-build linter to detect it.
|
errorMsgLiteral,
|
||||||
//
|
extractCodes,
|
||||||
// Outputs:
|
|
||||||
// /* FIXME (minify-errors-in-prod): Unminified error message in production build! */
|
|
||||||
// if (!condition) {
|
|
||||||
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
|
||||||
// }
|
|
||||||
parentStatementPath.addComment(
|
|
||||||
'leading',
|
|
||||||
'FIXME (minify-errors-in-prod): Unminified error message in production build!',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** @type {babel.types.CallExpression} */
|
||||||
|
let callExpression;
|
||||||
|
if (noMinify || prodErrorId === undefined) {
|
||||||
|
// Error minification is disabled for this build.
|
||||||
|
//
|
||||||
|
// Outputs:
|
||||||
|
// if (!condition) {
|
||||||
|
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
||||||
|
// }
|
||||||
|
const formatDevErrorMessageIdentifier =
|
||||||
|
helperModuleImports.addDefault(path, `shared/${dev}`, {
|
||||||
|
nameHint: dev,
|
||||||
|
});
|
||||||
|
callExpression = t.callExpression(
|
||||||
|
formatDevErrorMessageIdentifier,
|
||||||
|
[devMessage],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Error minification enabled for this build.
|
||||||
|
//
|
||||||
|
// Outputs:
|
||||||
|
// if (!condition) {
|
||||||
|
// formatProdErrorMessage(ERR_CODE, adj, noun)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Import ReactErrorProd
|
||||||
|
const formatProdErrorMessageIdentifier =
|
||||||
|
helperModuleImports.addDefault(path, `shared/${prod}`, {
|
||||||
|
nameHint: prod,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Outputs:
|
||||||
|
// formatProdErrorMessage(ERR_CODE, adj, noun);
|
||||||
|
callExpression = t.callExpression(
|
||||||
|
formatProdErrorMessageIdentifier,
|
||||||
|
[t.numericLiteral(prodErrorId), ...errorMsgExpressions],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentStatementPath.replaceWith(
|
||||||
|
t.ifStatement(
|
||||||
|
t.unaryExpression('!', condition),
|
||||||
|
t.blockStatement([t.expressionStatement(callExpression)]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!noMinify && prodErrorId === undefined) {
|
||||||
|
// There is no error code for this message. Add an inline comment
|
||||||
|
// that flags this as an unminified error. This allows the build
|
||||||
|
// to proceed, while also allowing a post-build linter to detect it.
|
||||||
|
//
|
||||||
|
// Outputs:
|
||||||
|
// /* FIXME (minify-errors-in-prod): Unminified error message in production build! */
|
||||||
|
// if (!condition) {
|
||||||
|
// formatDevErrorMessage(`A ${adj} message that contains ${noun}`);
|
||||||
|
// }
|
||||||
|
parentStatementPath.addComment(
|
||||||
|
'leading',
|
||||||
|
'FIXME (minify-errors-in-prod): Unminified error message in production build!',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -162,6 +162,7 @@
|
|||||||
"@lexical/yjs": ["./packages/lexical-yjs/src/index.ts"],
|
"@lexical/yjs": ["./packages/lexical-yjs/src/index.ts"],
|
||||||
"shared/canUseDOM": ["./packages/shared/src/canUseDOM.ts"],
|
"shared/canUseDOM": ["./packages/shared/src/canUseDOM.ts"],
|
||||||
"shared/caretFromPoint": ["./packages/shared/src/caretFromPoint.ts"],
|
"shared/caretFromPoint": ["./packages/shared/src/caretFromPoint.ts"],
|
||||||
|
"shared/devInvariant": ["./packages/shared/src/devInvariant.ts"],
|
||||||
"shared/environment": ["./packages/shared/src/environment.ts"],
|
"shared/environment": ["./packages/shared/src/environment.ts"],
|
||||||
"shared/formatDevErrorMessage": [
|
"shared/formatDevErrorMessage": [
|
||||||
"./packages/shared/src/formatDevErrorMessage.ts"
|
"./packages/shared/src/formatDevErrorMessage.ts"
|
||||||
@ -169,6 +170,9 @@
|
|||||||
"shared/formatProdErrorMessage": [
|
"shared/formatProdErrorMessage": [
|
||||||
"./packages/shared/src/formatProdErrorMessage.ts"
|
"./packages/shared/src/formatProdErrorMessage.ts"
|
||||||
],
|
],
|
||||||
|
"shared/formatProdWarningMessage": [
|
||||||
|
"./packages/shared/src/formatProdWarningMessage.ts"
|
||||||
|
],
|
||||||
"shared/invariant": ["./packages/shared/src/invariant.ts"],
|
"shared/invariant": ["./packages/shared/src/invariant.ts"],
|
||||||
"shared/normalizeClassNames": [
|
"shared/normalizeClassNames": [
|
||||||
"./packages/shared/src/normalizeClassNames.ts"
|
"./packages/shared/src/normalizeClassNames.ts"
|
||||||
|
@ -170,6 +170,7 @@
|
|||||||
"@lexical/yjs": ["./packages/lexical-yjs/src/index.ts"],
|
"@lexical/yjs": ["./packages/lexical-yjs/src/index.ts"],
|
||||||
"shared/canUseDOM": ["./packages/shared/src/canUseDOM.ts"],
|
"shared/canUseDOM": ["./packages/shared/src/canUseDOM.ts"],
|
||||||
"shared/caretFromPoint": ["./packages/shared/src/caretFromPoint.ts"],
|
"shared/caretFromPoint": ["./packages/shared/src/caretFromPoint.ts"],
|
||||||
|
"shared/devInvariant": ["./packages/shared/src/devInvariant.ts"],
|
||||||
"shared/environment": ["./packages/shared/src/environment.ts"],
|
"shared/environment": ["./packages/shared/src/environment.ts"],
|
||||||
"shared/formatDevErrorMessage": [
|
"shared/formatDevErrorMessage": [
|
||||||
"./packages/shared/src/formatDevErrorMessage.ts"
|
"./packages/shared/src/formatDevErrorMessage.ts"
|
||||||
@ -177,6 +178,9 @@
|
|||||||
"shared/formatProdErrorMessage": [
|
"shared/formatProdErrorMessage": [
|
||||||
"./packages/shared/src/formatProdErrorMessage.ts"
|
"./packages/shared/src/formatProdErrorMessage.ts"
|
||||||
],
|
],
|
||||||
|
"shared/formatProdWarningMessage": [
|
||||||
|
"./packages/shared/src/formatProdWarningMessage.ts"
|
||||||
|
],
|
||||||
"shared/invariant": ["./packages/shared/src/invariant.ts"],
|
"shared/invariant": ["./packages/shared/src/invariant.ts"],
|
||||||
"shared/normalizeClassNames": [
|
"shared/normalizeClassNames": [
|
||||||
"./packages/shared/src/normalizeClassNames.ts"
|
"./packages/shared/src/normalizeClassNames.ts"
|
||||||
|
Reference in New Issue
Block a user