diff --git a/packages/babel-helper-builder-react-jsx-experimental/src/index.js b/packages/babel-helper-builder-react-jsx-experimental/src/index.js index f29098b73f2d..7254acc254ca 100644 --- a/packages/babel-helper-builder-react-jsx-experimental/src/index.js +++ b/packages/babel-helper-builder-react-jsx-experimental/src/index.js @@ -43,11 +43,6 @@ export function helper(babel, options) { const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/; const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/; - // This is the number of possible import names - // development: jsxDEV, Fragment, createElement - // production: jsx, jsxs, Fragment, createElement - const IMPORT_NAME_SIZE = options.development ? 3 : 4; - const { importSource: IMPORT_SOURCE_DEFAULT = DEFAULT.importSource, runtime: RUNTIME_DEFAULT = DEFAULT.runtime, @@ -214,44 +209,33 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, ); } - const importName = addAutoImports(path, { - ...state.opts, - source, - }); - state.set( "@babel/plugin-react-jsx/jsxIdentifier", - createIdentifierParser( - createIdentifierName( - path, - options.development ? "jsxDEV" : "jsx", - importName, - ), + createImportLazily( + state, + path, + options.development ? "jsxDEV" : "jsx", + source, ), ); state.set( "@babel/plugin-react-jsx/jsxStaticIdentifier", - createIdentifierParser( - createIdentifierName( - path, - options.development ? "jsxDEV" : "jsxs", - importName, - ), + createImportLazily( + state, + path, + options.development ? "jsxDEV" : "jsxs", + source, ), ); state.set( "@babel/plugin-react-jsx/createElementIdentifier", - createIdentifierParser( - createIdentifierName(path, "createElement", importName), - ), + createImportLazily(state, path, "createElement", source), ); state.set( "@babel/plugin-react-jsx/jsxFragIdentifier", - createIdentifierParser( - createIdentifierName(path, "Fragment", importName), - ), + createImportLazily(state, path, "Fragment", source), ); state.set( @@ -313,44 +297,6 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return false; } - function createIdentifierName(path, name, importName) { - if (isModule(path)) { - const identifierName = `${importName[name]}`; - return identifierName; - } else { - return `${importName[name]}.${name}`; - } - } - - function getImportNames(parentPath) { - const imports = new Set(); - - parentPath.traverse({ - "JSXElement|JSXFragment"(path) { - if (path.type === "JSXFragment") imports.add("Fragment"); - const openingPath = path.get("openingElement"); - - const validChildren = t.react.buildChildren(openingPath.parent); - let importName; - if (path.type === "JSXElement" && shouldUseCreateElement(path)) { - importName = "createElement"; - } else if (options.development) { - importName = "jsxDEV"; - } else if (validChildren.length > 1) { - importName = "jsxs"; - } else { - importName = "jsx"; - } - imports.add(importName); - - if (imports.size === IMPORT_NAME_SIZE) { - path.stop(); - } - }, - }); - return imports; - } - function getSource(source, importName) { switch (importName) { case "Fragment": @@ -367,47 +313,40 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } } - function addAutoImports(path, state) { - const imports = getImportNames(path, state); - if (isModule(path)) { - // import {jsx} from "react"; - // import {createElement} from "react"; - const importMap = {}; - - imports.forEach(importName => { - if (!importMap[importName]) { - importMap[importName] = addNamed( - path, - importName, - getSource(state.source, importName), - { - importedInterop: "uncompiled", - ensureLiveReference: true, - }, - ).name; - } - }); + function createImportLazily(pass, path, importName, source) { + return () => { + const actualSource = getSource(source, importName); + if (isModule(path)) { + let reference = pass.get( + `@babel/plugin-react-jsx/imports/${importName}`, + ); + if (reference) return t.cloneNode(reference); - return importMap; - } else { - const importMap = {}; - const sourceMap = {}; - imports.forEach(importName => { - const source = getSource(state.source, importName); - if (!importMap[importName]) { - if (!sourceMap[source]) { - // var _react = require("react") - sourceMap[source] = addNamespace(path, source, { - importedInterop: "uncompiled", - ensureLiveReference: true, - }).name; - } + reference = addNamed(path, importName, actualSource, { + importedInterop: "uncompiled", + }); + pass.set(`@babel/plugin-react-jsx/imports/${importName}`, reference); - importMap[importName] = sourceMap[source]; + return reference; + } else { + let reference = pass.get( + `@babel/plugin-react-jsx/requires/${actualSource}`, + ); + if (reference) { + reference = t.cloneNode(reference); + } else { + reference = addNamespace(path, actualSource, { + importedInterop: "uncompiled", + }); + pass.set( + `@babel/plugin-react-jsx/requires/${actualSource}`, + reference, + ); } - }); - return importMap; - } + + return t.memberExpression(reference, t.identifier(importName)); + } + }; } function createIdentifierParser(id) { diff --git a/packages/babel-plugin-transform-react-jsx-development/src/index.js b/packages/babel-plugin-transform-react-jsx-development/src/index.js index 4fb52ad62946..f9942948d5bb 100644 --- a/packages/babel-plugin-transform-react-jsx-development/src/index.js +++ b/packages/babel-plugin-transform-react-jsx-development/src/index.js @@ -26,13 +26,20 @@ export default declare((api, options) => { state.pure = PURE_ANNOTATION ?? !pass.get("@babel/plugin-react-jsx/pragmaSet"); } else { - state.jsxCallee = pass.get("@babel/plugin-react-jsx/jsxIdentifier")(); - state.jsxStaticCallee = pass.get( - "@babel/plugin-react-jsx/jsxStaticIdentifier", - )(); - state.createElementCallee = pass.get( - "@babel/plugin-react-jsx/createElementIdentifier", - )(); + const getter = get => ({ enumerable: true, configurable: true, get }); + + // TODO(Babel 8): helper-builder-react-jsx expects those properties to be AST nodes, but we want to + // generate them lazily so that we only inject imports when needed. + // These should actually be functions. + Object.defineProperties(state, { + jsxCallee: getter(pass.get("@babel/plugin-react-jsx/jsxIdentifier")), + jsxStaticCallee: getter( + pass.get("@babel/plugin-react-jsx/jsxStaticIdentifier"), + ), + createElementCallee: getter( + pass.get("@babel/plugin-react-jsx/createElementIdentifier"), + ), + }); state.pure = PURE_ANNOTATION ?? diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/output.mjs b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/output.mjs index 823d0e7e88fb..6d22b1515d23 100644 --- a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/output.mjs +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/output.mjs @@ -1,6 +1,6 @@ +import { Fragment as _Fragment } from "react/jsx-dev-runtime"; import { createElement as _createElement } from "react"; import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime"; -import { Fragment as _Fragment } from "react/jsx-dev-runtime"; var _jsxFileName = "/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/input.js"; var x = /*#__PURE__*/_jsxDEV(_Fragment, { diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/auto-import-dev-windows/output.mjs b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/auto-import-dev-windows/output.mjs index 287dcca783d9..29953eb85bd7 100644 --- a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/auto-import-dev-windows/output.mjs +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/auto-import-dev-windows/output.mjs @@ -1,6 +1,6 @@ +import { Fragment as _Fragment } from "react/jsx-dev-runtime"; import { createElement as _createElement } from "react"; import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime"; -import { Fragment as _Fragment } from "react/jsx-dev-runtime"; var _jsxFileName = "\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\auto-import-dev-windows\\input.js"; var x = /*#__PURE__*/_jsxDEV(_Fragment, { diff --git a/packages/babel-plugin-transform-react-jsx/src/index.js b/packages/babel-plugin-transform-react-jsx/src/index.js index ff86fd75ba9d..0143eed73396 100644 --- a/packages/babel-plugin-transform-react-jsx/src/index.js +++ b/packages/babel-plugin-transform-react-jsx/src/index.js @@ -27,13 +27,20 @@ export default declare((api, options) => { state.pure = PURE_ANNOTATION ?? !pass.get("@babel/plugin-react-jsx/pragmaSet"); } else { - state.jsxCallee = pass.get("@babel/plugin-react-jsx/jsxIdentifier")(); - state.jsxStaticCallee = pass.get( - "@babel/plugin-react-jsx/jsxStaticIdentifier", - )(); - state.createElementCallee = pass.get( - "@babel/plugin-react-jsx/createElementIdentifier", - )(); + const getter = get => ({ enumerable: true, configurable: true, get }); + + // TODO(Babel 8): helper-builder-react-jsx expects those properties to be AST nodes, but we want to + // generate them lazily so that we only inject imports when needed. + // These should actually be functions. + Object.defineProperties(state, { + jsxCallee: getter(pass.get("@babel/plugin-react-jsx/jsxIdentifier")), + jsxStaticCallee: getter( + pass.get("@babel/plugin-react-jsx/jsxStaticIdentifier"), + ), + createElementCallee: getter( + pass.get("@babel/plugin-react-jsx/createElementIdentifier"), + ), + }); state.pure = PURE_ANNOTATION ?? diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/auto-import-react-source-type-module/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/auto-import-react-source-type-module/output.mjs index 00a9fb933326..3e6a79e53a23 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/auto-import-react-source-type-module/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/auto-import-react-source-type-module/output.mjs @@ -1,7 +1,7 @@ -import { createElement as _createElement } from "react"; +import { Fragment as _Fragment } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { createElement as _createElement } from "react"; import { jsx as _jsx } from "react/jsx-runtime"; -import { Fragment as _Fragment } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsx(_Fragment, { children: /*#__PURE__*/_jsxs("div", { diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/react-defined/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/react-defined/output.mjs index 18af1b0b5e21..bcbd701ce128 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/react-defined/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/autoImport/react-defined/output.mjs @@ -1,6 +1,6 @@ +import { jsxs as _jsxs } from "react/jsx-runtime"; import { createElement as _createElement } from "react"; import { jsx as _jsx } from "react/jsx-runtime"; -import { jsxs as _jsxs } from "react/jsx-runtime"; import * as react from "react"; var y = react.createElement("div", { foo: 1 diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/concatenates-adjacent-string-literals/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/concatenates-adjacent-string-literals/output.mjs index 303129243f5e..9e87d3113349 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/concatenates-adjacent-string-literals/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/concatenates-adjacent-string-literals/output.mjs @@ -1,5 +1,5 @@ -import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { jsx as _jsx } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsxs("div", { children: ["foo", "bar", "baz", /*#__PURE__*/_jsx("div", { diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/does-not-add-source-self-automatic/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/does-not-add-source-self-automatic/output.mjs index 00a9fb933326..3e6a79e53a23 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/does-not-add-source-self-automatic/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/does-not-add-source-self-automatic/output.mjs @@ -1,7 +1,7 @@ -import { createElement as _createElement } from "react"; +import { Fragment as _Fragment } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { createElement as _createElement } from "react"; import { jsx as _jsx } from "react/jsx-runtime"; -import { Fragment as _Fragment } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsx(_Fragment, { children: /*#__PURE__*/_jsxs("div", { diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-fragments/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-fragments/output.mjs index b34f1fa72cf7..3c5c87e94a88 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-fragments/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-fragments/output.mjs @@ -1,5 +1,5 @@ -import { jsx as _jsx } from "react/jsx-runtime"; import { Fragment as _Fragment } from "react/jsx-runtime"; +import { jsx as _jsx } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsx(_Fragment, { children: /*#__PURE__*/_jsx("div", {}) diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-static-children/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-static-children/output.mjs index b1451ada1901..2a1f5868d6d1 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-static-children/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-static-children/output.mjs @@ -1,5 +1,5 @@ -import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { jsx as _jsx } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsxs("div", { children: [/*#__PURE__*/_jsx("span", {}), [/*#__PURE__*/_jsx("span", {}, '0'), /*#__PURE__*/_jsx("span", {}, '1')]] diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-have-correct-comma-in-nested-children/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-have-correct-comma-in-nested-children/output.mjs index ffb137dfab63..616150d3cc8b 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-have-correct-comma-in-nested-children/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-have-correct-comma-in-nested-children/output.mjs @@ -1,5 +1,5 @@ -import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { jsx as _jsx } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsxs("div", { children: [/*#__PURE__*/_jsx("div", { diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-properly-handle-keys/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-properly-handle-keys/output.mjs index 3b72b3dc762e..38c7fc736826 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-properly-handle-keys/output.mjs +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/should-properly-handle-keys/output.mjs @@ -1,5 +1,5 @@ -import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; +import { jsx as _jsx } from "react/jsx-runtime"; var x = /*#__PURE__*/_jsxs("div", { children: [/*#__PURE__*/_jsx("div", {}, "1"), /*#__PURE__*/_jsx("div", { diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/regression/issue-12478-automatic/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/regression/issue-12478-automatic/output.js index 7a12700836b4..2b1080f18f94 100644 --- a/packages/babel-plugin-transform-react-jsx/test/fixtures/regression/issue-12478-automatic/output.js +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/regression/issue-12478-automatic/output.js @@ -1 +1,3 @@ -const foo = /*#__PURE__*/undefined.jsx("p", {}); +var _reactJsxRuntime = require("react/jsx-runtime"); + +const foo = /*#__PURE__*/_reactJsxRuntime.jsx("p", {});