From c8588ba2194cb189eb16fbeb6b761b956ba906da Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Wed, 1 Jun 2016 14:56:39 -0700 Subject: [PATCH 01/11] added reactProdInvariant and the corresponding babel pass --- .babelrc | 2 +- .../dev-expression-with-codes-test.js | 109 +++++++++++ scripts/error-codes/constants.js | 15 ++ .../error-codes/dev-expression-with-codes.js | 180 ++++++++++++++++++ .../__tests__/reactProdInvariant-test.js | 47 +++++ src/shared/utils/reactProdInvariant.js | 47 +++++ 6 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 scripts/error-codes/__tests__/dev-expression-with-codes-test.js create mode 100644 scripts/error-codes/constants.js create mode 100644 scripts/error-codes/dev-expression-with-codes.js create mode 100644 src/shared/utils/__tests__/reactProdInvariant-test.js create mode 100644 src/shared/utils/reactProdInvariant.js diff --git a/.babelrc b/.babelrc index 2880552916426..3bd0b6761edd1 100644 --- a/.babelrc +++ b/.babelrc @@ -2,7 +2,7 @@ "presets": ["react"], "ignore": ["third_party"], "plugins": [ - "fbjs-scripts/babel-6/dev-expression", + "./scripts/error-codes/dev-expression-with-codes", "syntax-trailing-function-commas", "babel-plugin-transform-object-rest-spread", "transform-es2015-template-literals", diff --git a/scripts/error-codes/__tests__/dev-expression-with-codes-test.js b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js new file mode 100644 index 0000000000000..e21809682a170 --- /dev/null +++ b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +/* eslint-disable quotes */ +'use strict'; + +let babel = require('babel-core'); +let devExpressionWithCodes = require('../dev-expression-with-codes'); +let prodInvariantName = require('../constants').prodInvariantName; + +function transform(input) { + return babel.transform(input, { + plugins: [devExpressionWithCodes], + }).code; +} + +function compare(input, output) { + var compiled = transform(input); + expect(compiled).toEqual(output); +} + +var oldEnv; + +describe('dev-expression', function() { + beforeEach(() => { + oldEnv = process.env.NODE_ENV; + process.env.NODE_ENV = ''; + }); + + afterEach(() => { + process.env.NODE_ENV = oldEnv; + }); + + it('should replace __DEV__ in if', () => { + compare( +` +if (__DEV__) { + console.log('foo') +}`, +` +if (process.env.NODE_ENV !== 'production') { + console.log('foo'); +}` + ); + }); + + it('should replace warning calls', () => { + compare( + "warning(condition, 'a %s b', 'c');", + "process.env.NODE_ENV !== 'production' ? warning(condition, 'a %s b', 'c') : void 0;" + ); + }); + + it("should add `reactProdInvariant` when it finds `require('invariant')`", () => { + compare( +"var invariant = require('invariant');", + +`var invariant = require('invariant'); + +var ${prodInvariantName} = require('reactProdInvariant');` + ); + }); + + it('should replace simple invariant calls', () => { + compare( + "invariant(condition, 'Do not override existing functions.');", + "!condition ? " + + "process.env.NODE_ENV !== 'production' ? " + + "invariant(false, 'Do not override existing functions.') : " + + `${prodInvariantName}('16') : void 0;` + ); + }); + + it('should support invariant calls with args', () => { + compare( + "invariant(condition, 'Expected %s target to be an array; got %s', 'foo', 'bar');", + "!condition ? " + + "process.env.NODE_ENV !== 'production' ? " + + "invariant(false, 'Expected %s target to be an array; got %s', 'foo', 'bar') : " + + `${prodInvariantName}('7', 'foo', 'bar') : void 0;` + ); + }); + + it('should support invariant calls with a concatenated template string and args', () => { + compare( + "invariant(condition, 'Expected a component class, ' + 'got %s.' + '%s', 'Foo', 'Bar');", + "!condition ? " + + "process.env.NODE_ENV !== 'production' ? " + + "invariant(false, 'Expected a component class, got %s.%s', 'Foo', 'Bar') : " + + `${prodInvariantName}('18', 'Foo', 'Bar') : void 0;` + ); + }); + + it('should throw if the error message cannot be found', () => { + expect(() => { + transform("invariant(condition, 'a %s b', 'c');"); + }).toThrowError( + 'unknown: Error message "a %s b" ' + + 'cannot be found. The current React version ' + + 'and the error map are probably out of sync. ' + + 'Please run `gulp react:extract-errors` before building React.' + ); + }); +}); diff --git a/scripts/error-codes/constants.js b/scripts/error-codes/constants.js new file mode 100644 index 0000000000000..ed1087d91e15c --- /dev/null +++ b/scripts/error-codes/constants.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +var prodInvariantName = '_prod$Invariant_'; + +module.exports = { + prodInvariantName, +}; diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js new file mode 100644 index 0000000000000..4d316953d88b6 --- /dev/null +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +var evalToString = require('./evalToString'); +var existingErrorMap = require('./codes.json'); +var invertObject = require('./invertObject'); +var prodInvariantName = require('./constants').prodInvariantName; + +var errorMap = invertObject(existingErrorMap); +var prodInvariantModuleName = 'reactProdInvariant'; + +module.exports = function(babel) { + var t = babel.types; + + var SEEN_SYMBOL = Symbol(); + + var buildRequire = babel.template(`var IMPORT_NAME = require(SOURCE);`); + + var REQUIRE_PROD_INVARIANT = buildRequire({ + IMPORT_NAME: t.identifier(prodInvariantName), + SOURCE: t.stringLiteral(prodInvariantModuleName), + }); + + var DEV_EXPRESSION = t.binaryExpression( + '!==', + t.memberExpression( + t.memberExpression( + t.identifier('process'), + t.identifier('env'), + false + ), + t.identifier('NODE_ENV'), + false + ), + t.stringLiteral('production') + ); + + return { + visitor: { + Identifier: { + enter: function(path) { + // Do nothing when testing + if (process.env.NODE_ENV === 'test') { + return; + } + // replace __DEV__ with process.env.NODE_ENV !== 'production' + if (path.isIdentifier({name: '__DEV__'})) { + path.replaceWith(DEV_EXPRESSION); + } + }, + }, + CallExpression: { + exit: function(path) { + var node = path.node; + // Do nothing when testing + if (process.env.NODE_ENV === 'test') { + return; + } + // Ignore if it's already been processed + if (node[SEEN_SYMBOL]) { + return; + } + // Insert require('reactProdInvariant') after all `require('invariant')`s. + // NOTE currently it only supports the format of + // `var invariant = require('invariant');` (VariableDeclaration) + // and NOT ES6 imports/assignments. + if ( + path.get('callee').isIdentifier({name: 'require'}) && + path.get('arguments')[0] && + path.get('arguments')[0].isStringLiteral({value: 'invariant'}) && + path.parentPath.parentPath.isVariableDeclaration() + ) { + node[SEEN_SYMBOL] = true; + path.parentPath.parentPath.insertAfter(REQUIRE_PROD_INVARIANT); + } else if (path.get('callee').isIdentifier({name: 'invariant'})) { + // Turns this code: + // + // invariant(condition, argument, 'foo', 'bar'); + // + // into this: + // + // if (!condition) { + // if ("production" !== process.env.NODE_ENV) { + // invariant(false, argument, 'foo', 'bar'); + // } else { + // PROD_INVARIANT('XYZ', 'foo', 'bar'); + // } + // } + // + // where + // - `XYZ` is an error code: an unique identifier (a number string) + // that references a verbose error message. + // The mapping is stored in `scripts/error-codes/codes.json`. + // - PROD_INVARIANT is the `reactProdInvariant` function that always throw with a error URL like + // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args="foo"&args="bar" + // + // Specifically this does 3 things: + // 1. Checks the condition first, preventing an extra function call. + // 2. Adds an environment check so that verbose error messages aren't + // shipped to production. + // 3. Rewrite the call to `invariant` in production to `reactProdInvariant` + // - `reactProdInvariant` is always renamed to avoid shadowing + // The generated code is longer than the original code but will dead + // code removal in a minifier will strip that out. + var condition = node.arguments[0]; + var errorMsgLiteral = evalToString(node.arguments[1]); + + var prodErrorId = errorMap[errorMsgLiteral]; + if (prodErrorId === undefined) { + throw new Error( + 'Error message "' + errorMsgLiteral + + '" cannot be found. The current React version ' + + 'and the error map are probably out of sync. ' + + 'Please run `gulp react:extract-errors` before building React.' + ); + } + + var devInvariant = t.callExpression(node.callee, [ + t.booleanLiteral(false), + t.stringLiteral(errorMsgLiteral), + ].concat(node.arguments.slice(2))); + + devInvariant[SEEN_SYMBOL] = true; + + var prodInvariant = t.callExpression(t.identifier(prodInvariantName), [ + t.stringLiteral(prodErrorId), + ].concat(node.arguments.slice(2))); + + prodInvariant[SEEN_SYMBOL] = true; + path.replaceWith(t.ifStatement( + t.unaryExpression('!', condition), + t.blockStatement([ + t.ifStatement( + DEV_EXPRESSION, + t.blockStatement([ + t.expressionStatement(devInvariant), + ]), + t.blockStatement([ + t.expressionStatement(prodInvariant), + ]) + ), + ]) + )); + } else if (path.get('callee').isIdentifier({name: 'warning'})) { + // Turns this code: + // + // warning(condition, argument, argument); + // + // into this: + // + // if ("production" !== process.env.NODE_ENV) { + // warning(condition, argument, argument); + // } + // + // The goal is to strip out warning calls entirely in production. We + // don't need the same optimizations for conditions that we use for + // invariant because we don't care about an extra call in __DEV__ + + node[SEEN_SYMBOL] = true; + path.replaceWith(t.ifStatement( + DEV_EXPRESSION, + t.blockStatement([ + t.expressionStatement( + node + ), + ]) + )); + } + }, + }, + }, + }; +}; diff --git a/src/shared/utils/__tests__/reactProdInvariant-test.js b/src/shared/utils/__tests__/reactProdInvariant-test.js new file mode 100644 index 0000000000000..be41889bdb503 --- /dev/null +++ b/src/shared/utils/__tests__/reactProdInvariant-test.js @@ -0,0 +1,47 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ +/* eslint-disable max-len */ +'use strict'; + +var reactProdInvariant; + +describe('reactProdInvariant', function() { + beforeEach(function() { + jest.resetModuleRegistry(); + reactProdInvariant = require('reactProdInvariant'); + }); + + it('should throw with the correct number of `%s`s in the URL', function() { + expect(function() { + reactProdInvariant(124, 'foo', 'bar'); + }).toThrowError( + 'React: production error #124. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=124&args=%22foo%22&args=%22bar%22 ' + + 'for more details.' + ); + + expect(function() { + reactProdInvariant(20); + }).toThrowError( + 'React: production error #20. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=20 ' + + 'for more details.' + ); + + expect(function() { + reactProdInvariant(77, 'foo', 'bar', 'and', 'what', 'else'); + }).toThrowError( + 'React: production error #77. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=77&args=%22foo%22&args=%22bar%22&args=%22and%22&args=%22what%22&args=%22else%22 ' + + 'for more details.' + ); + }); +}); diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js new file mode 100644 index 0000000000000..7857b77cee0e4 --- /dev/null +++ b/src/shared/utils/reactProdInvariant.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule reactProdInvariant + */ +'use strict'; + +/** + * WARNING: DO NOT manually require this module. + * This is a replacement for `invariant(...)` used by the error code system + * and will _only_ be required by the corresponding babel pass. + * It always throw. + */ +function reactProdInvariant(code, a, b, c, d, e, f) { + var argCount = arguments.length - 1; + + var format = ( + 'React: production error #' + code + '. ' + + 'Visit http://facebook.github.io/react/docs/' + + 'error-codes.html?invariant=' + code + ); + + while (argCount > 0) { + format += '&args=%22%s%22'; + argCount--; + } + + format += ' for more details.'; + var error; + + var args = [a, b, c, d, e, f]; + var argIndex = 0; + error = new Error(format.replace(/%s/g, function() { + return args[argIndex++]; + })); + error.name = 'Invariant Violation'; + + error.framesToPop = 1; // we don't care about reactProdInvariant's own frame + throw error; +} + +module.exports = reactProdInvariant; From 60aa80adffb9b25c8533b3038c1552e16ba33fcc Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Wed, 1 Jun 2016 22:16:18 -0700 Subject: [PATCH 02/11] added stack info to the query params --- src/shared/utils/reactProdInvariant.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index 7857b77cee0e4..33e12e4b4011b 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -18,6 +18,7 @@ */ function reactProdInvariant(code, a, b, c, d, e, f) { var argCount = arguments.length - 1; + var error = new Error(''); var format = ( 'React: production error #' + code + '. ' + @@ -30,14 +31,15 @@ function reactProdInvariant(code, a, b, c, d, e, f) { argCount--; } + format += '&stack=%22' + encodeURIComponent(error.stack) + '%22'; format += ' for more details.'; - var error; var args = [a, b, c, d, e, f]; var argIndex = 0; - error = new Error(format.replace(/%s/g, function() { + error.message = format.replace(/%s/g, function() { return args[argIndex++]; - })); + }); + error.name = 'Invariant Violation'; error.framesToPop = 1; // we don't care about reactProdInvariant's own frame From ef1f5d6944d29d6e04f87901b8cbe4528a8a7e45 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Wed, 1 Jun 2016 22:16:47 -0700 Subject: [PATCH 03/11] fixed actual module names --- scripts/error-codes/dev-expression-with-codes.js | 5 ++--- scripts/jest/preprocessor.js | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index 4d316953d88b6..88bacabb8aa87 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -25,7 +25,7 @@ module.exports = function(babel) { var REQUIRE_PROD_INVARIANT = buildRequire({ IMPORT_NAME: t.identifier(prodInvariantName), - SOURCE: t.stringLiteral(prodInvariantModuleName), + SOURCE: t.stringLiteral('./' + prodInvariantModuleName), }); var DEV_EXPRESSION = t.binaryExpression( @@ -74,8 +74,7 @@ module.exports = function(babel) { if ( path.get('callee').isIdentifier({name: 'require'}) && path.get('arguments')[0] && - path.get('arguments')[0].isStringLiteral({value: 'invariant'}) && - path.parentPath.parentPath.isVariableDeclaration() + path.get('arguments')[0].isStringLiteral({value: 'fbjs/lib/invariant'}) ) { node[SEEN_SYMBOL] = true; path.parentPath.parentPath.insertAfter(REQUIRE_PROD_INVARIANT); diff --git a/scripts/jest/preprocessor.js b/scripts/jest/preprocessor.js index 0cbc46dd90c1c..727bd20cf23eb 100644 --- a/scripts/jest/preprocessor.js +++ b/scripts/jest/preprocessor.js @@ -20,7 +20,7 @@ var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction') // Use require.resolve to be resilient to file moves, npm updates, etc var pathToBabel = path.join(require.resolve('babel-core'), '..', 'package.json'); var pathToModuleMap = require.resolve('fbjs/module-map'); -var pathToBabelPluginDev = require.resolve('fbjs-scripts/babel-6/dev-expression'); +var pathToBabelPluginDevWithCode = require.resolve('../error-codes/dev-expression-with-codes'); var pathToBabelPluginModules = require.resolve('fbjs-scripts/babel-6/rewrite-modules'); var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc'); @@ -68,7 +68,7 @@ module.exports = { pathToBabel, pathToBabelrc, pathToModuleMap, - pathToBabelPluginDev, + pathToBabelPluginDevWithCode, pathToBabelPluginModules, ]), }; From cfcd0a19b74ccc6426790f19a1bcbe7f138757db Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Wed, 1 Jun 2016 22:17:00 -0700 Subject: [PATCH 04/11] WIP added a prod test (failing) --- .../dom/__tests__/ReactDOMProduction-test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index 718909ed86894..4abeb96f3bdeb 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -87,4 +87,19 @@ describe('ReactDOMProduction', function() { expect(container.childNodes.length).toBe(0); }); + it('should throw with an error code in production', function() { + expect(function() { + var Component = React.createClass({ + render: function() { + return ['this is wrong']; + }, + }); + var container = document.createElement('div'); + ReactDOM.render(, container); + }).toThrowError( + 'React: production error #109. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=109&args=%22Component%22 ' + + 'for more details.' + ); + }); }); From 81bf26652652357f81ff286586df0df19240f730 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Wed, 1 Jun 2016 22:41:51 -0700 Subject: [PATCH 05/11] error.stack null check --- src/shared/utils/reactProdInvariant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index 33e12e4b4011b..c80a5cd53097c 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -31,7 +31,7 @@ function reactProdInvariant(code, a, b, c, d, e, f) { argCount--; } - format += '&stack=%22' + encodeURIComponent(error.stack) + '%22'; + format += '&stack=%22' + !error.stack ? '' : encodeURIComponent(error.stack) + '%22'; format += ' for more details.'; var args = [a, b, c, d, e, f]; From 6bfaa6d24fb09c975895cce9ebf85a43c20494e0 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Thu, 2 Jun 2016 14:29:04 -0700 Subject: [PATCH 06/11] fixed failing reactProdInvariant stack-related tests --- .../utils/__tests__/reactProdInvariant-test.js | 12 +++--------- src/shared/utils/reactProdInvariant.js | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/shared/utils/__tests__/reactProdInvariant-test.js b/src/shared/utils/__tests__/reactProdInvariant-test.js index be41889bdb503..accefc0bd947b 100644 --- a/src/shared/utils/__tests__/reactProdInvariant-test.js +++ b/src/shared/utils/__tests__/reactProdInvariant-test.js @@ -23,25 +23,19 @@ describe('reactProdInvariant', function() { expect(function() { reactProdInvariant(124, 'foo', 'bar'); }).toThrowError( - 'React: production error #124. Visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=124&args=%22foo%22&args=%22bar%22 ' + - 'for more details.' + /React: production error #124\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=124&args=%22foo%22&args=%22bar%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ ); expect(function() { reactProdInvariant(20); }).toThrowError( - 'React: production error #20. Visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=20 ' + - 'for more details.' + /React: production error #20\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=20&stack=%22Error.*reactProdInvariant.*%22 for more details\./ ); expect(function() { reactProdInvariant(77, 'foo', 'bar', 'and', 'what', 'else'); }).toThrowError( - 'React: production error #77. Visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=77&args=%22foo%22&args=%22bar%22&args=%22and%22&args=%22what%22&args=%22else%22 ' + - 'for more details.' + /React: production error #77\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=77&args=%22foo%22&args=%22bar%22&args=%22and%22&args=%22what%22&args=%22else%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ ); }); }); diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index c80a5cd53097c..1136f03b21411 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -31,7 +31,7 @@ function reactProdInvariant(code, a, b, c, d, e, f) { argCount--; } - format += '&stack=%22' + !error.stack ? '' : encodeURIComponent(error.stack) + '%22'; + format += '&stack=%22' + (error.stack ? encodeURIComponent(error.stack) : '') + '%22'; format += ' for more details.'; var args = [a, b, c, d, e, f]; From a6d48582b1e500c6cb69b1207ce1947e131276a8 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Thu, 2 Jun 2016 17:24:25 -0700 Subject: [PATCH 07/11] moved pass from .babelrc to gulpfile and jest/preprocessor --- .babelrc | 1 - gulpfile.js | 2 ++ .../dev-expression-with-codes-test.js | 23 +++++++++++++---- .../error-codes/dev-expression-with-codes.js | 25 ++++++++++--------- scripts/jest/preprocessor.js | 1 + .../dom/__tests__/ReactDOMProduction-test.js | 5 +--- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/.babelrc b/.babelrc index 3bd0b6761edd1..a976990ee9e0e 100644 --- a/.babelrc +++ b/.babelrc @@ -2,7 +2,6 @@ "presets": ["react"], "ignore": ["third_party"], "plugins": [ - "./scripts/error-codes/dev-expression-with-codes", "syntax-trailing-function-commas", "babel-plugin-transform-object-rest-spread", "transform-es2015-template-literals", diff --git a/gulpfile.js b/gulpfile.js index cb287ff2aa6fd..103f608e208f9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,6 +16,7 @@ var del = require('del'); var babelPluginModules = require('fbjs-scripts/babel-6/rewrite-modules'); var extractErrors = require('./scripts/error-codes/gulp-extract-errors'); +var devExpressionWithCodes = require('./scripts/error-codes/dev-expression-with-codes'); var paths = { react: { @@ -53,6 +54,7 @@ var errorCodeOpts = { var babelOpts = { plugins: [ + devExpressionWithCodes, // this pass has to run before `rewrite-modules` [babelPluginModules, {map: moduleMap}], ], }; diff --git a/scripts/error-codes/__tests__/dev-expression-with-codes-test.js b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js index e21809682a170..4e13660fd3bb1 100644 --- a/scripts/error-codes/__tests__/dev-expression-with-codes-test.js +++ b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js @@ -96,14 +96,27 @@ var ${prodInvariantName} = require('reactProdInvariant');` ); }); - it('should throw if the error message cannot be found', () => { - expect(() => { - transform("invariant(condition, 'a %s b', 'c');"); - }).toThrowError( - 'unknown: Error message "a %s b" ' + + it('should warn in non-test envs if the error message cannot be found', () => { + spyOn(console, 'warn'); + transform("invariant(condition, 'a %s b', 'c');"); + + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toBe( + 'Error message "a %s b" ' + 'cannot be found. The current React version ' + 'and the error map are probably out of sync. ' + 'Please run `gulp react:extract-errors` before building React.' ); }); + + it('should not warn in test env if the error message cannot be found', () => { + process.env.NODE_ENV = 'test'; + + spyOn(console, 'warn'); + transform("invariant(condition, 'a %s b', 'c');"); + + expect(console.warn.calls.count()).toBe(0); + + process.env.NODE_ENV = ''; + }); }); diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index 88bacabb8aa87..c33fdb63534c5 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -25,7 +25,7 @@ module.exports = function(babel) { var REQUIRE_PROD_INVARIANT = buildRequire({ IMPORT_NAME: t.identifier(prodInvariantName), - SOURCE: t.stringLiteral('./' + prodInvariantModuleName), + SOURCE: t.stringLiteral(prodInvariantModuleName), }); var DEV_EXPRESSION = t.binaryExpression( @@ -59,10 +59,6 @@ module.exports = function(babel) { CallExpression: { exit: function(path) { var node = path.node; - // Do nothing when testing - if (process.env.NODE_ENV === 'test') { - return; - } // Ignore if it's already been processed if (node[SEEN_SYMBOL]) { return; @@ -74,7 +70,7 @@ module.exports = function(babel) { if ( path.get('callee').isIdentifier({name: 'require'}) && path.get('arguments')[0] && - path.get('arguments')[0].isStringLiteral({value: 'fbjs/lib/invariant'}) + path.get('arguments')[0].isStringLiteral({value: 'invariant'}) ) { node[SEEN_SYMBOL] = true; path.parentPath.parentPath.insertAfter(REQUIRE_PROD_INVARIANT); @@ -113,12 +109,17 @@ module.exports = function(babel) { var prodErrorId = errorMap[errorMsgLiteral]; if (prodErrorId === undefined) { - throw new Error( - 'Error message "' + errorMsgLiteral + - '" cannot be found. The current React version ' + - 'and the error map are probably out of sync. ' + - 'Please run `gulp react:extract-errors` before building React.' - ); + // The error cannot be found in the map. + node[SEEN_SYMBOL] = true; + if (process.env.NODE_ENV !== 'test') { + console.warn( + 'Error message "' + errorMsgLiteral + + '" cannot be found. The current React version ' + + 'and the error map are probably out of sync. ' + + 'Please run `gulp react:extract-errors` before building React.' + ); + } + return; } var devInvariant = t.callExpression(node.callee, [ diff --git a/scripts/jest/preprocessor.js b/scripts/jest/preprocessor.js index 727bd20cf23eb..3a60dfd051e6d 100644 --- a/scripts/jest/preprocessor.js +++ b/scripts/jest/preprocessor.js @@ -27,6 +27,7 @@ var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc'); // TODO: make sure this stays in sync with gulpfile var babelOptions = { plugins: [ + pathToBabelPluginDevWithCode, // this pass has to run before `rewrite-modules` [babelPluginModules, { map: Object.assign( {}, diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index 4abeb96f3bdeb..ee46261d4f0ef 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -8,7 +8,6 @@ * * @emails react-core */ - 'use strict'; describe('ReactDOMProduction', function() { @@ -97,9 +96,7 @@ describe('ReactDOMProduction', function() { var container = document.createElement('div'); ReactDOM.render(, container); }).toThrowError( - 'React: production error #109. Visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=109&args=%22Component%22 ' + - 'for more details.' + /React: production error #109\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=109&args=%22Component%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ // eslint-disable-line max-len ); }); }); From 889416b4b642db2a697df8594b9fbc12fcdd195a Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Thu, 2 Jun 2016 18:29:16 -0700 Subject: [PATCH 08/11] make invariant hygienic again --- .../dev-expression-with-codes-test.js | 35 ++++++++++++++--- scripts/error-codes/constants.js | 15 ------- .../error-codes/dev-expression-with-codes.js | 39 ++++++++++++------- 3 files changed, 53 insertions(+), 36 deletions(-) delete mode 100644 scripts/error-codes/constants.js diff --git a/scripts/error-codes/__tests__/dev-expression-with-codes-test.js b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js index 4e13660fd3bb1..fcc1a31513ee2 100644 --- a/scripts/error-codes/__tests__/dev-expression-with-codes-test.js +++ b/scripts/error-codes/__tests__/dev-expression-with-codes-test.js @@ -11,7 +11,6 @@ let babel = require('babel-core'); let devExpressionWithCodes = require('../dev-expression-with-codes'); -let prodInvariantName = require('../constants').prodInvariantName; function transform(input) { return babel.transform(input, { @@ -60,39 +59,63 @@ if (process.env.NODE_ENV !== 'production') { compare( "var invariant = require('invariant');", -`var invariant = require('invariant'); +`var _prodInvariant = require('reactProdInvariant'); -var ${prodInvariantName} = require('reactProdInvariant');` +var invariant = require('invariant');` ); }); it('should replace simple invariant calls', () => { compare( "invariant(condition, 'Do not override existing functions.');", + "var _prodInvariant = require('reactProdInvariant');\n\n" + + "!condition ? " + + "process.env.NODE_ENV !== 'production' ? " + + "invariant(false, 'Do not override existing functions.') : " + + `_prodInvariant('16') : void 0;` + ); + }); + + it("should only add `reactProdInvariant` once", () => { + var expectedInvariantTransformResult = ( "!condition ? " + "process.env.NODE_ENV !== 'production' ? " + "invariant(false, 'Do not override existing functions.') : " + - `${prodInvariantName}('16') : void 0;` + `_prodInvariant('16') : void 0;` + ); + + compare( +`var invariant = require('invariant'); +invariant(condition, 'Do not override existing functions.'); +invariant(condition, 'Do not override existing functions.');`, + +`var _prodInvariant = require('reactProdInvariant'); + +var invariant = require('invariant'); +${expectedInvariantTransformResult} +${expectedInvariantTransformResult}` ); }); it('should support invariant calls with args', () => { compare( "invariant(condition, 'Expected %s target to be an array; got %s', 'foo', 'bar');", + "var _prodInvariant = require('reactProdInvariant');\n\n" + "!condition ? " + "process.env.NODE_ENV !== 'production' ? " + "invariant(false, 'Expected %s target to be an array; got %s', 'foo', 'bar') : " + - `${prodInvariantName}('7', 'foo', 'bar') : void 0;` + `_prodInvariant('7', 'foo', 'bar') : void 0;` ); }); it('should support invariant calls with a concatenated template string and args', () => { compare( "invariant(condition, 'Expected a component class, ' + 'got %s.' + '%s', 'Foo', 'Bar');", + "var _prodInvariant = require('reactProdInvariant');\n\n" + "!condition ? " + "process.env.NODE_ENV !== 'production' ? " + "invariant(false, 'Expected a component class, got %s.%s', 'Foo', 'Bar') : " + - `${prodInvariantName}('18', 'Foo', 'Bar') : void 0;` + `_prodInvariant('18', 'Foo', 'Bar') : void 0;` ); }); diff --git a/scripts/error-codes/constants.js b/scripts/error-codes/constants.js deleted file mode 100644 index ed1087d91e15c..0000000000000 --- a/scripts/error-codes/constants.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -var prodInvariantName = '_prod$Invariant_'; - -module.exports = { - prodInvariantName, -}; diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index c33fdb63534c5..8f27ca09bda09 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -11,22 +11,27 @@ var evalToString = require('./evalToString'); var existingErrorMap = require('./codes.json'); var invertObject = require('./invertObject'); -var prodInvariantName = require('./constants').prodInvariantName; var errorMap = invertObject(existingErrorMap); -var prodInvariantModuleName = 'reactProdInvariant'; module.exports = function(babel) { var t = babel.types; var SEEN_SYMBOL = Symbol(); - var buildRequire = babel.template(`var IMPORT_NAME = require(SOURCE);`); - - var REQUIRE_PROD_INVARIANT = buildRequire({ - IMPORT_NAME: t.identifier(prodInvariantName), - SOURCE: t.stringLiteral(prodInvariantModuleName), - }); + function getProdInvariantId(path, localState) { + if (!localState.id) { + localState.id = path.scope.generateUidIdentifier('prodInvariant'); + path.scope.getProgramParent().push({ + id: localState.id, + init: t.callExpression( + t.identifier('require'), + [t.stringLiteral('reactProdInvariant')] + ), + }); + } + return localState.id; + } var DEV_EXPRESSION = t.binaryExpression( '!==', @@ -43,6 +48,10 @@ module.exports = function(babel) { ); return { + pre: function() { + this.id = null; + }, + visitor: { Identifier: { enter: function(path) { @@ -63,17 +72,16 @@ module.exports = function(babel) { if (node[SEEN_SYMBOL]) { return; } - // Insert require('reactProdInvariant') after all `require('invariant')`s. - // NOTE currently it only supports the format of - // `var invariant = require('invariant');` (VariableDeclaration) - // and NOT ES6 imports/assignments. + // Insert `var _hygienicVarName = require('reactProdInvariant');` + // before all `require('invariant')`s. + // NOTE it doesn't support ES6 imports yet. if ( path.get('callee').isIdentifier({name: 'require'}) && path.get('arguments')[0] && path.get('arguments')[0].isStringLiteral({value: 'invariant'}) ) { node[SEEN_SYMBOL] = true; - path.parentPath.parentPath.insertAfter(REQUIRE_PROD_INVARIANT); + getProdInvariantId(path, this); } else if (path.get('callee').isIdentifier({name: 'invariant'})) { // Turns this code: // @@ -93,7 +101,7 @@ module.exports = function(babel) { // - `XYZ` is an error code: an unique identifier (a number string) // that references a verbose error message. // The mapping is stored in `scripts/error-codes/codes.json`. - // - PROD_INVARIANT is the `reactProdInvariant` function that always throw with a error URL like + // - `PROD_INVARIANT` is the `reactProdInvariant` function that always throw with an error URL like // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args="foo"&args="bar" // // Specifically this does 3 things: @@ -129,7 +137,8 @@ module.exports = function(babel) { devInvariant[SEEN_SYMBOL] = true; - var prodInvariant = t.callExpression(t.identifier(prodInvariantName), [ + var localInvariantId = getProdInvariantId(path, this); + var prodInvariant = t.callExpression(localInvariantId, [ t.stringLiteral(prodErrorId), ].concat(node.arguments.slice(2))); From 84365a3e224364219816e2b337a372536d02a510 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Thu, 2 Jun 2016 23:04:41 -0700 Subject: [PATCH 09/11] changed URL format and removed stack info --- .../error-codes/dev-expression-with-codes.js | 2 +- .../dom/__tests__/ReactDOMProduction-test.js | 4 +++- .../__tests__/reactProdInvariant-test.js | 15 +++++++++----- src/shared/utils/reactProdInvariant.js | 20 ++++++------------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index 8f27ca09bda09..0165ff67093d7 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -102,7 +102,7 @@ module.exports = function(babel) { // that references a verbose error message. // The mapping is stored in `scripts/error-codes/codes.json`. // - `PROD_INVARIANT` is the `reactProdInvariant` function that always throw with an error URL like - // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args="foo"&args="bar" + // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args[]=foo&args[]=bar // // Specifically this does 3 things: // 1. Checks the condition first, preventing an extra function call. diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index ee46261d4f0ef..ce80989c2eeb8 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -96,7 +96,9 @@ describe('ReactDOMProduction', function() { var container = document.createElement('div'); ReactDOM.render(, container); }).toThrowError( - /React: production error #109\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=109&args=%22Component%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ // eslint-disable-line max-len + 'React: production error #109. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=109&args[]=Component' + + ' for more details.' ); }); }); diff --git a/src/shared/utils/__tests__/reactProdInvariant-test.js b/src/shared/utils/__tests__/reactProdInvariant-test.js index accefc0bd947b..2dd797bbcde9d 100644 --- a/src/shared/utils/__tests__/reactProdInvariant-test.js +++ b/src/shared/utils/__tests__/reactProdInvariant-test.js @@ -8,7 +8,6 @@ * * @emails react-core */ -/* eslint-disable max-len */ 'use strict'; var reactProdInvariant; @@ -23,19 +22,25 @@ describe('reactProdInvariant', function() { expect(function() { reactProdInvariant(124, 'foo', 'bar'); }).toThrowError( - /React: production error #124\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=124&args=%22foo%22&args=%22bar%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ + 'React: production error #124. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=124&args[]=foo&args[]=bar' + + ' for more details.' ); expect(function() { reactProdInvariant(20); }).toThrowError( - /React: production error #20\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=20&stack=%22Error.*reactProdInvariant.*%22 for more details\./ + 'React: production error #20. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=20' + + ' for more details.' ); expect(function() { - reactProdInvariant(77, 'foo', 'bar', 'and', 'what', 'else'); + reactProdInvariant(77, '
', '&?bar'); }).toThrowError( - /React: production error #77\. Visit http:\/\/facebook\.github\.io\/react\/docs\/error-codes\.html\?invariant=77&args=%22foo%22&args=%22bar%22&args=%22and%22&args=%22what%22&args=%22else%22&stack=%22Error.*reactProdInvariant.*%22 for more details\./ + 'React: production error #77. Visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar' + + ' for more details.' ); }); }); diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index 1136f03b21411..4bc893545063d 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -18,31 +18,23 @@ */ function reactProdInvariant(code, a, b, c, d, e, f) { var argCount = arguments.length - 1; - var error = new Error(''); - var format = ( + var message = ( 'React: production error #' + code + '. ' + 'Visit http://facebook.github.io/react/docs/' + 'error-codes.html?invariant=' + code ); - while (argCount > 0) { - format += '&args=%22%s%22'; - argCount--; + for (var argIdx = 0; argIdx < argCount; argIdx++) { + message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]); } - format += '&stack=%22' + (error.stack ? encodeURIComponent(error.stack) : '') + '%22'; - format += ' for more details.'; - - var args = [a, b, c, d, e, f]; - var argIndex = 0; - error.message = format.replace(/%s/g, function() { - return args[argIndex++]; - }); + message += ' for more details.'; + var error = new Error(message); error.name = 'Invariant Violation'; - error.framesToPop = 1; // we don't care about reactProdInvariant's own frame + throw error; } From d855ba36be98415db93af54539d902e991b8c79c Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Tue, 7 Jun 2016 13:32:33 -0700 Subject: [PATCH 10/11] improved message format and babel code --- .../error-codes/dev-expression-with-codes.js | 29 ++++++++++--------- .../dom/__tests__/ReactDOMProduction-test.js | 5 ++-- .../__tests__/reactProdInvariant-test.js | 15 ++++++---- src/shared/utils/reactProdInvariant.js | 14 +++++---- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index 0165ff67093d7..840b1f9fb58e1 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -17,20 +17,21 @@ var errorMap = invertObject(existingErrorMap); module.exports = function(babel) { var t = babel.types; - var SEEN_SYMBOL = Symbol(); + var SEEN_SYMBOL = Symbol('dev-expression-with-codes.seen'); - function getProdInvariantId(path, localState) { - if (!localState.id) { - localState.id = path.scope.generateUidIdentifier('prodInvariant'); + // Generate a hygienic identifier + function getProdInvariantIdentifier(path, localState) { + if (!localState.prodInvariantIdentifier) { + localState.prodInvariantIdentifier = path.scope.generateUidIdentifier('prodInvariant'); path.scope.getProgramParent().push({ - id: localState.id, + id: localState.prodInvariantIdentifier, init: t.callExpression( t.identifier('require'), [t.stringLiteral('reactProdInvariant')] ), }); } - return localState.id; + return localState.prodInvariantIdentifier; } var DEV_EXPRESSION = t.binaryExpression( @@ -49,7 +50,7 @@ module.exports = function(babel) { return { pre: function() { - this.id = null; + this.prodInvariantIdentifier = null; }, visitor: { @@ -59,7 +60,7 @@ module.exports = function(babel) { if (process.env.NODE_ENV === 'test') { return; } - // replace __DEV__ with process.env.NODE_ENV !== 'production' + // Replace __DEV__ with process.env.NODE_ENV !== 'production' if (path.isIdentifier({name: '__DEV__'})) { path.replaceWith(DEV_EXPRESSION); } @@ -72,7 +73,7 @@ module.exports = function(babel) { if (node[SEEN_SYMBOL]) { return; } - // Insert `var _hygienicVarName = require('reactProdInvariant');` + // Insert `var PROD_INVARIANT = require('reactProdInvariant');` // before all `require('invariant')`s. // NOTE it doesn't support ES6 imports yet. if ( @@ -81,7 +82,7 @@ module.exports = function(babel) { path.get('arguments')[0].isStringLiteral({value: 'invariant'}) ) { node[SEEN_SYMBOL] = true; - getProdInvariantId(path, this); + getProdInvariantIdentifier(path, this); } else if (path.get('callee').isIdentifier({name: 'invariant'})) { // Turns this code: // @@ -98,17 +99,17 @@ module.exports = function(babel) { // } // // where - // - `XYZ` is an error code: an unique identifier (a number string) + // - `XYZ` 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`. - // - `PROD_INVARIANT` is the `reactProdInvariant` function that always throw with an error URL like + // - `PROD_INVARIANT` is the `reactProdInvariant` function that always throws with an error URL like // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args[]=foo&args[]=bar // // Specifically this does 3 things: // 1. Checks the condition first, preventing an extra function call. // 2. Adds an environment check so that verbose error messages aren't // shipped to production. - // 3. Rewrite the call to `invariant` in production to `reactProdInvariant` + // 3. Rewrites the call to `invariant` in production to `reactProdInvariant` // - `reactProdInvariant` is always renamed to avoid shadowing // The generated code is longer than the original code but will dead // code removal in a minifier will strip that out. @@ -137,7 +138,7 @@ module.exports = function(babel) { devInvariant[SEEN_SYMBOL] = true; - var localInvariantId = getProdInvariantId(path, this); + var localInvariantId = getProdInvariantIdentifier(path, this); var prodInvariant = t.callExpression(localInvariantId, [ t.stringLiteral(prodErrorId), ].concat(node.arguments.slice(2))); diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index ce80989c2eeb8..1547f474eb089 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -96,9 +96,10 @@ describe('ReactDOMProduction', function() { var container = document.createElement('div'); ReactDOM.render(, container); }).toThrowError( - 'React: production error #109. Visit ' + + 'Minified React error #109; visit ' + 'http://facebook.github.io/react/docs/error-codes.html?invariant=109&args[]=Component' + - ' for more details.' + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.' ); }); }); diff --git a/src/shared/utils/__tests__/reactProdInvariant-test.js b/src/shared/utils/__tests__/reactProdInvariant-test.js index 2dd797bbcde9d..2677de5820ab9 100644 --- a/src/shared/utils/__tests__/reactProdInvariant-test.js +++ b/src/shared/utils/__tests__/reactProdInvariant-test.js @@ -22,25 +22,28 @@ describe('reactProdInvariant', function() { expect(function() { reactProdInvariant(124, 'foo', 'bar'); }).toThrowError( - 'React: production error #124. Visit ' + + 'Minified React error #124; visit ' + 'http://facebook.github.io/react/docs/error-codes.html?invariant=124&args[]=foo&args[]=bar' + - ' for more details.' + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.' ); expect(function() { reactProdInvariant(20); }).toThrowError( - 'React: production error #20. Visit ' + + 'Minified React error #20; visit ' + 'http://facebook.github.io/react/docs/error-codes.html?invariant=20' + - ' for more details.' + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.' ); expect(function() { reactProdInvariant(77, '
', '&?bar'); }).toThrowError( - 'React: production error #77. Visit ' + + 'Minified React error #77; visit ' + 'http://facebook.github.io/react/docs/error-codes.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar' + - ' for more details.' + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.' ); }); }); diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index 4bc893545063d..f0f26441779c2 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -14,22 +14,24 @@ * WARNING: DO NOT manually require this module. * This is a replacement for `invariant(...)` used by the error code system * and will _only_ be required by the corresponding babel pass. - * It always throw. + * It always throws. */ -function reactProdInvariant(code, a, b, c, d, e, f) { +function reactProdInvariant(code) { var argCount = arguments.length - 1; var message = ( - 'React: production error #' + code + '. ' + - 'Visit http://facebook.github.io/react/docs/' + - 'error-codes.html?invariant=' + code + 'Minified React error #' + code + '; visit ' + + 'http://facebook.github.io/react/docs/error-codes.html?invariant=' + code ); for (var argIdx = 0; argIdx < argCount; argIdx++) { message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]); } - message += ' for more details.'; + message += ( + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.' + ); var error = new Error(message); error.name = 'Invariant Violation'; From f69268faf127ab37c4dde5698f75f613bde103f7 Mon Sep 17 00:00:00 2001 From: Keyan Zhang Date: Tue, 7 Jun 2016 16:53:25 -0700 Subject: [PATCH 11/11] changed url to /error-decoder.html --- scripts/error-codes/dev-expression-with-codes.js | 2 +- src/renderers/dom/__tests__/ReactDOMProduction-test.js | 2 +- src/shared/utils/__tests__/reactProdInvariant-test.js | 6 +++--- src/shared/utils/reactProdInvariant.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/error-codes/dev-expression-with-codes.js b/scripts/error-codes/dev-expression-with-codes.js index 840b1f9fb58e1..9862a20f51a7b 100644 --- a/scripts/error-codes/dev-expression-with-codes.js +++ b/scripts/error-codes/dev-expression-with-codes.js @@ -103,7 +103,7 @@ module.exports = function(babel) { // that references a verbose error message. // The mapping is stored in `scripts/error-codes/codes.json`. // - `PROD_INVARIANT` is the `reactProdInvariant` function that always throws with an error URL like - // http://facebook.github.io/react/docs/error-codes.html?invariant=XYZ&args[]=foo&args[]=bar + // http://facebook.github.io/react/docs/error-decoder.html?invariant=XYZ&args[]=foo&args[]=bar // // Specifically this does 3 things: // 1. Checks the condition first, preventing an extra function call. diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index 1547f474eb089..2704f15767631 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -97,7 +97,7 @@ describe('ReactDOMProduction', function() { ReactDOM.render(, container); }).toThrowError( 'Minified React error #109; visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=109&args[]=Component' + + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=109&args[]=Component' + ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.' ); diff --git a/src/shared/utils/__tests__/reactProdInvariant-test.js b/src/shared/utils/__tests__/reactProdInvariant-test.js index 2677de5820ab9..9b5f950ae65ea 100644 --- a/src/shared/utils/__tests__/reactProdInvariant-test.js +++ b/src/shared/utils/__tests__/reactProdInvariant-test.js @@ -23,7 +23,7 @@ describe('reactProdInvariant', function() { reactProdInvariant(124, 'foo', 'bar'); }).toThrowError( 'Minified React error #124; visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=124&args[]=foo&args[]=bar' + + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=124&args[]=foo&args[]=bar' + ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.' ); @@ -32,7 +32,7 @@ describe('reactProdInvariant', function() { reactProdInvariant(20); }).toThrowError( 'Minified React error #20; visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=20' + + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=20' + ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.' ); @@ -41,7 +41,7 @@ describe('reactProdInvariant', function() { reactProdInvariant(77, '
', '&?bar'); }).toThrowError( 'Minified React error #77; visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar' + + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar' + ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.' ); diff --git a/src/shared/utils/reactProdInvariant.js b/src/shared/utils/reactProdInvariant.js index f0f26441779c2..ca235bbeb9d25 100644 --- a/src/shared/utils/reactProdInvariant.js +++ b/src/shared/utils/reactProdInvariant.js @@ -21,7 +21,7 @@ function reactProdInvariant(code) { var message = ( 'Minified React error #' + code + '; visit ' + - 'http://facebook.github.io/react/docs/error-codes.html?invariant=' + code + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code ); for (var argIdx = 0; argIdx < argCount; argIdx++) {