Lazily inject imports to the JSX runtime (#12493)

This commit is contained in:
Nicolò Ribaudo 2020-12-15 12:55:17 +01:00 committed by GitHub
parent a0c1a9a9e4
commit 28d2cbcbf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 88 additions and 133 deletions

View File

@ -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 = {};
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);
imports.forEach(importName => {
if (!importMap[importName]) {
importMap[importName] = addNamed(
path,
importName,
getSource(state.source, importName),
{
importedInterop: "uncompiled",
ensureLiveReference: true,
},
).name;
reference = addNamed(path, importName, actualSource, {
importedInterop: "uncompiled",
});
pass.set(`@babel/plugin-react-jsx/imports/${importName}`, reference);
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;
} 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;
}
importMap[importName] = sourceMap[source];
}
});
return importMap;
}
return t.memberExpression(reference, t.identifier(importName));
}
};
}
function createIdentifierParser(id) {

View File

@ -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 ??

View File

@ -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 = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/input.js";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {

View File

@ -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 = "<CWD>\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\auto-import-dev-windows\\input.js";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {

View File

@ -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 ??

View File

@ -1,7 +1,7 @@
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
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";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {

View File

@ -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

View File

@ -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", {

View File

@ -1,7 +1,7 @@
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
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";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {

View File

@ -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", {})

View File

@ -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')]]

View File

@ -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", {

View File

@ -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", {

View File

@ -1 +1,3 @@
const foo = /*#__PURE__*/undefined.jsx("p", {});
var _reactJsxRuntime = require("react/jsx-runtime");
const foo = /*#__PURE__*/_reactJsxRuntime.jsx("p", {});