Skip to content

Commit

Permalink
Load jsx-runtime after existing imports (#12546)
Browse files Browse the repository at this point in the history
Co-authored-by: Brian Ng <bng412@gmail.com>
Co-authored-by: Babel Bot <babel-bot@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 10, 2021
1 parent 6c9a481 commit 4f83a09
Show file tree
Hide file tree
Showing 30 changed files with 207 additions and 46 deletions.
52 changes: 39 additions & 13 deletions packages/babel-helper-module-imports/src/import-injector.js
Expand Up @@ -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",
};

/**
Expand Down Expand Up @@ -120,6 +127,7 @@ export default class ImportInjector {
importingInterop: "babel",
ensureLiveReference: false,
ensureNoContext: false,
importPosition: "before",
};

constructor(path, importedSource, opts) {
Expand Down Expand Up @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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) &&
Expand All @@ -409,20 +423,32 @@ export default class ImportInjector {
return resultName;
}

_insertStatements(statements, blockHoist = 3) {
statements.forEach(node => {
node._blockHoist = blockHoist;
});

const targetPath = this._programPath.get("body").find(p => {
const val = p.node._blockHoist;
return Number.isFinite(val) && val < 4;
});
_insertStatements(statements, importPosition = "before", blockHoist = 3) {
const body = this._programPath.get("body");

if (targetPath) {
targetPath.insertBefore(statements);
if (importPosition === "after") {
for (let i = body.length - 1; i >= 0; i--) {
if (body[i].isImportDeclaration()) {
body[i].insertAfter(statements);
return;
}
}
} else {
this._programPath.unshiftContainer("body", statements);
statements.forEach(node => {
node._blockHoist = blockHoist;
});

const targetPath = body.find(p => {
const val = p.node._blockHoist;
return Number.isFinite(val) && val < 4;
});

if (targetPath) {
targetPath.insertBefore(statements);
return;
}
}

this._programPath.unshiftContainer("body", statements);
}
}
43 changes: 40 additions & 3 deletions packages/babel-helper-module-imports/test/index.js
Expand Up @@ -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 {
Expand Down Expand Up @@ -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`);
});
});
});
@@ -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", {
Expand Down
@@ -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,
Expand Down
@@ -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", {
Expand Down
@@ -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,
Expand Down
Expand Up @@ -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);

Expand Down
@@ -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';
@@ -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";
@@ -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')
);
@@ -0,0 +1,7 @@
{
"plugins": [
["transform-react-jsx", { "runtime": "automatic" }],
"transform-modules-commonjs"
],
"sourceType": "module"
}
@@ -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'));
@@ -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')
);
@@ -0,0 +1,4 @@
{
"plugins": [["transform-react-jsx", { "runtime": "automatic" }]],
"sourceType": "script"
}
@@ -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'));
@@ -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')
);
@@ -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'));
@@ -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", {
Expand Down
@@ -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
});
Expand Down
@@ -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", {
Expand Down
@@ -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", {
Expand Down
@@ -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");
@@ -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, {});
@@ -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", {})
Expand Down
@@ -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')]]
Expand Down
@@ -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", {
Expand Down

0 comments on commit 4f83a09

Please sign in to comment.