Load jsx-runtime after existing imports (#12546)

Co-authored-by: Brian Ng <bng412@gmail.com>
Co-authored-by: Babel Bot <babel-bot@users.noreply.github.com>
This commit is contained in:
Nicolò Ribaudo 2021-01-10 16:23:22 +01:00 committed by GitHub
parent 6c9a481e83
commit 4f83a09dd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 207 additions and 46 deletions

View File

@ -89,6 +89,13 @@ export type ImportOptions = {
* * false - No particular requirements for context of the access. (Default)
*/
ensureNoContext: boolean,
/**
* Define whether the import should be loaded before or after the existing imports.
* "after" is only allowed inside ECMAScript modules, since it's not possible to
* reliably pick the location _after_ require() calls but _before_ other code in CJS.
*/
importPosition: "before" | "after",
};
/**
@ -120,6 +127,7 @@ export default class ImportInjector {
importingInterop: "babel",
ensureLiveReference: false,
ensureNoContext: false,
importPosition: "before",
};
constructor(path, importedSource, opts) {
@ -200,9 +208,11 @@ export default class ImportInjector {
ensureLiveReference,
ensureNoContext,
nameHint,
importPosition,
// Not meant for public usage. Allows code that absolutely must control
// ordering to set a specific hoist value on the import nodes.
// This is ignored when "importPosition" is "after".
blockHoist,
} = opts;
@ -215,6 +225,10 @@ export default class ImportInjector {
const isModuleForNode = isMod && importingInterop === "node";
const isModuleForBabel = isMod && importingInterop === "babel";
if (importPosition === "after" && !isMod) {
throw new Error(`"importPosition": "after" is only supported in modules`);
}
const builder = new ImportBuilder(
importedSource,
this._programScope,
@ -397,7 +411,7 @@ export default class ImportInjector {
const { statements, resultName } = builder.done();
this._insertStatements(statements, blockHoist);
this._insertStatements(statements, importPosition, blockHoist);
if (
(isDefault || isNamed) &&
@ -409,20 +423,32 @@ export default class ImportInjector {
return resultName;
}
_insertStatements(statements, blockHoist = 3) {
_insertStatements(statements, importPosition = "before", blockHoist = 3) {
const body = this._programPath.get("body");
if (importPosition === "after") {
for (let i = body.length - 1; i >= 0; i--) {
if (body[i].isImportDeclaration()) {
body[i].insertAfter(statements);
return;
}
}
} else {
statements.forEach(node => {
node._blockHoist = blockHoist;
});
const targetPath = this._programPath.get("body").find(p => {
const targetPath = body.find(p => {
const val = p.node._blockHoist;
return Number.isFinite(val) && val < 4;
});
if (targetPath) {
targetPath.insertBefore(statements);
} else {
return;
}
}
this._programPath.unshiftContainer("body", statements);
}
}
}

View File

@ -2,18 +2,24 @@ import * as babel from "@babel/core";
import { ImportInjector } from "../";
function test(sourceType, opts, initializer, expectedCode) {
function test(sourceType, opts, initializer, inputCode, expectedCode) {
if (typeof opts === "function") {
expectedCode = initializer;
expectedCode = inputCode;
inputCode = initializer;
initializer = opts;
opts = null;
}
if (expectedCode === undefined) {
expectedCode = inputCode;
inputCode = "";
}
const result = babel.transform("", {
const result = babel.transform(inputCode, {
cwd: __dirname,
sourceType,
filename: "example" + (sourceType === "module" ? ".mjs" : ".js"),
babelrc: false,
configFile: false,
plugins: [
function ({ types: t }) {
return {
@ -1103,4 +1109,35 @@ describe("@babel/helper-module-imports", () => {
});
});
});
describe("importPosition: after", () => {
it("works in ES modules", () => {
testModule(
{ importPosition: "after" },
m => m.addNamed("read", "source"),
`
import f from "foo";
f();
import b from "bar";
b();
`,
`
import f from "foo";
f();
import b from "bar";
import { read as _read } from "source";
b();
_read;
`,
);
});
it("is disallowed in CJS modules", () => {
expect(() =>
testScript({ importPosition: "after" }, m =>
m.addNamed("read", "source"),
),
).toThrow(`"importPosition": "after" is only supported in modules`);
});
});
});

View File

@ -1,7 +1,7 @@
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
var _jsxFileName = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/input.js";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {
children: /*#__PURE__*/_jsxDEV("div", {

View File

@ -1,8 +1,8 @@
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
var _jsxFileName = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/self-inside-arrow/input.mjs",
_this = this;
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,

View File

@ -1,7 +1,7 @@
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
var _jsxFileName = "<CWD>\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\auto-import-dev-windows\\input.js";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {
children: /*#__PURE__*/_jsxDEV("div", {

View File

@ -1,8 +1,8 @@
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
var _jsxFileName = "<CWD>\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\self-inside-arrow-windows\\input.mjs",
_this = this;
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,

View File

@ -638,6 +638,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
reference = addNamed(path, importName, actualSource, {
importedInterop: "uncompiled",
importPosition: "after",
});
set(pass, `imports/${importName}`, reference);

View File

@ -0,0 +1,11 @@
// https://github.com/babel/babel/issues/12522
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);
// Imports are hoisted, so this is still ok
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';

View File

@ -0,0 +1,9 @@
// https://github.com/babel/babel/issues/12522
ReactDOM.render( /*#__PURE__*/_jsx("p", {
children: "Hello, World!"
}), document.getElementById('root')); // Imports are hoisted, so this is still ok
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';
import { jsx as _jsx } from "react/jsx-runtime";

View File

@ -0,0 +1,10 @@
// https://github.com/babel/babel/issues/12522
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);

View File

@ -0,0 +1,7 @@
{
"plugins": [
["transform-react-jsx", { "runtime": "automatic" }],
"transform-modules-commonjs"
],
"sourceType": "module"
}

View File

@ -0,0 +1,16 @@
"use strict";
require("react-app-polyfill/ie11");
require("react-app-polyfill/stable");
var _reactDom = _interopRequireDefault(require("react-dom"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// https://github.com/babel/babel/issues/12522
_reactDom.default.render( /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
children: "Hello, World!"
}), document.getElementById('root'));

View File

@ -0,0 +1,10 @@
// https://github.com/babel/babel/issues/12522
require('react-app-polyfill/ie11');
require('react-app-polyfill/stable');
const ReactDOM = require('react-dom');
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);

View File

@ -0,0 +1,4 @@
{
"plugins": [["transform-react-jsx", { "runtime": "automatic" }]],
"sourceType": "script"
}

View File

@ -0,0 +1,12 @@
var _reactJsxRuntime = require("react/jsx-runtime");
// https://github.com/babel/babel/issues/12522
require('react-app-polyfill/ie11');
require('react-app-polyfill/stable');
const ReactDOM = require('react-dom');
ReactDOM.render( /*#__PURE__*/_reactJsxRuntime.jsx("p", {
children: "Hello, World!"
}), document.getElementById('root'));

View File

@ -0,0 +1,10 @@
// https://github.com/babel/babel/issues/12522
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);

View File

@ -0,0 +1,8 @@
// https://github.com/babel/babel/issues/12522
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';
import { jsx as _jsx } from "react/jsx-runtime";
ReactDOM.render( /*#__PURE__*/_jsx("p", {
children: "Hello, World!"
}), document.getElementById('root'));

View File

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

View File

@ -1,7 +1,7 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { createElement as _createElement } from "react";
import { jsx as _jsx } from "react/jsx-runtime";
import * as react from "react";
import { jsx as _jsx } from "react/jsx-runtime";
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
var y = react.createElement("div", {
foo: 1
});

View File

@ -1,5 +1,5 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsxs("div", {
children: ["foo", "bar", "baz", /*#__PURE__*/_jsx("div", {

View File

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

View File

@ -1,4 +1,4 @@
import { jsx as _jsx } from "react/jsx-runtime";
import * as React from "react";
import { jsx as _jsx } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(React.Fragment, {}, "foo");

View File

@ -1,4 +1,4 @@
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, {});

View File

@ -1,5 +1,5 @@
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsx("div", {})

View File

@ -1,5 +1,5 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("span", {}), [/*#__PURE__*/_jsx("span", {}, '0'), /*#__PURE__*/_jsx("span", {}, '1')]]

View File

@ -1,6 +1,6 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment as _Fragment } 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";
/*#__PURE__*/
_jsx("div", {

View File

@ -1,5 +1,5 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
/*#__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 HelloMessage = React.createClass({
displayName: "HelloMessage",
render: function () {

View File

@ -1,5 +1,5 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("div", {

View File

@ -1,5 +1,5 @@
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("div", {}, "1"), /*#__PURE__*/_jsx("div", {