refactor: Move react-jsx-development implementation into react-jsx (#12524)

* Move react-jsx-development implementation into react-jsx

* Move helper-builder-jsx-experimental into transform-react-jsx

* Move JSX validation

* Move Program visitor

* introduce get/set utils

* pre -> getTag

* Dedupe code

* post -> getState

* Simplify logic

* Move final pieces

* Other simplifications

* Update lockfile

* Fix standalone bundling
This commit is contained in:
Nicolò Ribaudo 2020-12-22 11:53:14 +01:00 committed by GitHub
parent e08f68bf61
commit 17d62c3743
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 729 additions and 1007 deletions

View File

@ -1,20 +0,0 @@
{
"name": "@babel/helper-builder-react-jsx-experimental",
"version": "7.12.11",
"description": "Helper function to build react jsx",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-builder-react-jsx-experimental"
},
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "lib/index.js",
"dependencies": {
"@babel/helper-annotate-as-pure": "workspace:^7.12.10",
"@babel/helper-module-imports": "workspace:^7.12.5",
"@babel/types": "workspace:^7.12.11"
}
}

View File

@ -1,839 +0,0 @@
import * as t from "@babel/types";
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
import annotateAsPure from "@babel/helper-annotate-as-pure";
const DEFAULT = {
importSource: "react",
runtime: "automatic",
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
};
export function helper(babel, options) {
// TODO (Babel 8): Remove `useBuiltIns` & `useSpread`
const { useSpread = false, useBuiltIns = false } = options;
if (options.runtime === "classic") {
if (typeof useSpread !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useSpread (defaults to false)",
);
}
if (typeof useBuiltIns !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useBuiltIns (defaults to false)",
);
}
if (useSpread && useBuiltIns) {
throw new Error(
"transform-react-jsx currently only accepts useBuiltIns or useSpread " +
"but not both",
);
}
}
const FILE_NAME_VAR = "_jsxFileName";
const JSX_SOURCE_ANNOTATION_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/;
const JSX_RUNTIME_ANNOTATION_REGEX = /\*?\s*@jsxRuntime\s+([^\s]+)/;
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/;
const {
importSource: IMPORT_SOURCE_DEFAULT = DEFAULT.importSource,
runtime: RUNTIME_DEFAULT = DEFAULT.runtime,
pragma: PRAGMA_DEFAULT = DEFAULT.pragma,
pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag,
} = options;
const injectMetaPropertiesVisitor = {
JSXOpeningElement(path, state) {
for (const attr of path.get("attributes")) {
if (!attr.isJSXElement()) continue;
const { name } = attr.node.name;
if (name === "__source" || name === "__self") {
throw path.buildCodeFrameError(
`__source and __self should not be defined in props and are reserved for internal usage.`,
);
}
}
const self = t.jsxAttribute(
t.jsxIdentifier("__self"),
t.jsxExpressionContainer(t.thisExpression()),
);
const source = t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, state)),
);
path.pushContainer("attributes", [self, source]);
},
};
return {
JSXNamespacedName(path, state) {
const throwIfNamespace =
state.opts.throwIfNamespace === undefined
? true
: !!state.opts.throwIfNamespace;
if (throwIfNamespace) {
throw path.buildCodeFrameError(
`Namespace tags are not supported by default. React's JSX doesn't support namespace tags. \
You can set \`throwIfNamespace: false\` to bypass this warning.`,
);
}
},
JSXSpreadChild(path) {
throw path.buildCodeFrameError(
"Spread children are not supported in React.",
);
},
JSXElement: {
exit(path, file) {
let callExpr;
if (
file.get("@babel/plugin-react-jsx/runtime") === "classic" ||
shouldUseCreateElement(path)
) {
callExpr = buildCreateElementCall(path, file);
} else {
callExpr = buildJSXElementCall(path, file);
}
path.replaceWith(t.inherits(callExpr, path.node));
},
},
JSXFragment: {
exit(path, file) {
let callExpr;
if (file.get("@babel/plugin-react-jsx/runtime") === "classic") {
callExpr = buildCreateElementFragmentCall(path, file);
} else {
callExpr = buildJSXFragmentCall(path, file);
}
path.replaceWith(t.inherits(callExpr, path.node));
},
},
JSXAttribute(path) {
if (t.isJSXElement(path.node.value)) {
path.node.value = t.jsxExpressionContainer(path.node.value);
}
},
Program: {
enter(path, state) {
const { file } = state;
let runtime = RUNTIME_DEFAULT;
// For jsx mode
let source = IMPORT_SOURCE_DEFAULT;
let sourceSet = !!options.importSource;
// For createElement mode
let pragma = PRAGMA_DEFAULT;
let pragmaFrag = PRAGMA_FRAG_DEFAULT;
let pragmaSet = !!options.pragma;
let pragmaFragSet = !!options.pragmaFrag;
if (file.ast.comments) {
for (const comment of (file.ast.comments: Array<Object>)) {
const sourceMatches = JSX_SOURCE_ANNOTATION_REGEX.exec(
comment.value,
);
if (sourceMatches) {
source = sourceMatches[1];
sourceSet = true;
}
const runtimeMatches = JSX_RUNTIME_ANNOTATION_REGEX.exec(
comment.value,
);
if (runtimeMatches) {
runtime = runtimeMatches[1];
}
const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
if (jsxMatches) {
pragma = jsxMatches[1];
pragmaSet = true;
}
const jsxFragMatches = JSX_FRAG_ANNOTATION_REGEX.exec(
comment.value,
);
if (jsxFragMatches) {
pragmaFrag = jsxFragMatches[1];
pragmaFragSet = true;
}
}
}
state.set("@babel/plugin-react-jsx/runtime", runtime);
if (runtime === "classic") {
if (sourceSet) {
throw path.buildCodeFrameError(
`importSource cannot be set when runtime is classic.`,
);
}
state.set(
"@babel/plugin-react-jsx/createElementIdentifier",
createIdentifierParser(pragma),
);
state.set(
"@babel/plugin-react-jsx/jsxFragIdentifier",
createIdentifierParser(pragmaFrag),
);
state.set("@babel/plugin-react-jsx/usedFragment", false);
state.set(
"@babel/plugin-react-jsx/pragmaSet",
pragma !== DEFAULT.pragma,
);
state.set(
"@babel/plugin-react-jsx/pragmaFragSet",
pragmaFrag !== DEFAULT.pragmaFrag,
);
} else if (runtime === "automatic") {
if (pragmaSet || pragmaFragSet) {
throw path.buildCodeFrameError(
`pragma and pragmaFrag cannot be set when runtime is automatic.`,
);
}
state.set(
"@babel/plugin-react-jsx/jsxIdentifier",
createImportLazily(
state,
path,
options.development ? "jsxDEV" : "jsx",
source,
),
);
state.set(
"@babel/plugin-react-jsx/jsxStaticIdentifier",
createImportLazily(
state,
path,
options.development ? "jsxDEV" : "jsxs",
source,
),
);
state.set(
"@babel/plugin-react-jsx/createElementIdentifier",
createImportLazily(state, path, "createElement", source),
);
state.set(
"@babel/plugin-react-jsx/jsxFragIdentifier",
createImportLazily(state, path, "Fragment", source),
);
state.set(
"@babel/plugin-react-jsx/importSourceSet",
source !== DEFAULT.importSource,
);
} else {
throw path.buildCodeFrameError(
`Runtime must be either "classic" or "automatic".`,
);
}
if (options.development) {
path.traverse(injectMetaPropertiesVisitor, state);
}
},
// TODO (Babel 8): Decide if this should be removed or brought back.
// see: https://github.com/babel/babel/pull/12253#discussion_r513086528
//
// exit(path, state) {
// if (
// state.get("@babel/plugin-react-jsx/runtime") === "classic" &&
// state.get("@babel/plugin-react-jsx/pragmaSet") &&
// state.get("@babel/plugin-react-jsx/usedFragment") &&
// !state.get("@babel/plugin-react-jsx/pragmaFragSet")
// ) {
// throw new Error(
// "transform-react-jsx: pragma has been set but " +
// "pragmaFrag has not been set",
// );
// }
// },
},
};
// We want to use React.createElement, even in the case of
// jsx, for <div {...props} key={key} /> to distinguish it
// from <div key={key} {...props} />. This is an intermediary
// step while we deprecate key spread from props. Afterwards,
// we will stop using createElement in the transform.
function shouldUseCreateElement(path) {
const openingPath = path.get("openingElement");
const attributes = openingPath.node.attributes;
let seenPropsSpread = false;
for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i];
if (
seenPropsSpread &&
t.isJSXAttribute(attr) &&
attr.name.name === "key"
) {
return true;
} else if (t.isJSXSpreadAttribute(attr)) {
seenPropsSpread = true;
}
}
return false;
}
function getSource(source, importName) {
switch (importName) {
case "Fragment":
return `${source}/${
options.development ? "jsx-dev-runtime" : "jsx-runtime"
}`;
case "jsxDEV":
return `${source}/jsx-dev-runtime`;
case "jsx":
case "jsxs":
return `${source}/jsx-runtime`;
case "createElement":
return source;
}
}
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);
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 t.memberExpression(reference, t.identifier(importName));
}
};
}
function createIdentifierParser(id) {
return () => {
return id
.split(".")
.map(name => t.identifier(name))
.reduce((object, property) => t.memberExpression(object, property));
};
}
function makeTrace(fileNameIdentifier, lineNumber, column0Based) {
const fileLineLiteral =
lineNumber != null ? t.numericLiteral(lineNumber) : t.nullLiteral();
const fileColumnLiteral =
column0Based != null
? t.numericLiteral(column0Based + 1)
: t.nullLiteral();
const fileNameProperty = t.objectProperty(
t.identifier("fileName"),
fileNameIdentifier,
);
const lineNumberProperty = t.objectProperty(
t.identifier("lineNumber"),
fileLineLiteral,
);
const columnNumberProperty = t.objectProperty(
t.identifier("columnNumber"),
fileColumnLiteral,
);
return t.objectExpression([
fileNameProperty,
lineNumberProperty,
columnNumberProperty,
]);
}
function makeSource(path, state) {
const location = path.node.loc;
if (!location) {
// the element was generated and doesn't have location information
return path.scope.buildUndefinedNode();
}
if (!state.fileNameIdentifier) {
const { filename = "" } = state;
const fileNameIdentifier = path.scope.generateUidIdentifier(
FILE_NAME_VAR,
);
const scope = path.hub.getScope();
if (scope) {
scope.push({
id: fileNameIdentifier,
init: t.stringLiteral(filename),
});
}
state.fileNameIdentifier = fileNameIdentifier;
}
return makeTrace(
t.cloneNode(state.fileNameIdentifier),
location.start.line,
location.start.column,
);
}
function convertJSXIdentifier(node, parent) {
if (t.isJSXIdentifier(node)) {
if (node.name === "this" && t.isReferenced(node, parent)) {
return t.thisExpression();
} else if (t.isValidIdentifier(node.name, false)) {
node.type = "Identifier";
} else {
return t.stringLiteral(node.name);
}
} else if (t.isJSXMemberExpression(node)) {
return t.memberExpression(
convertJSXIdentifier(node.object, node),
convertJSXIdentifier(node.property, node),
);
} else if (t.isJSXNamespacedName(node)) {
/**
* If the flag "throwIfNamespace" is false
* print XMLNamespace like string literal
*/
return t.stringLiteral(`${node.namespace.name}:${node.name.name}`);
}
return node;
}
function convertAttributeValue(node) {
if (t.isJSXExpressionContainer(node)) {
return node.expression;
} else {
return node;
}
}
function convertAttribute(node) {
const value = convertAttributeValue(node.value || t.booleanLiteral(true));
if (t.isJSXSpreadAttribute(node)) {
return t.spreadElement(node.argument);
}
if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
value.value = value.value.replace(/\n\s+/g, " ");
// "raw" JSXText should not be used from a StringLiteral because it needs to be escaped.
if (value.extra && value.extra.raw) {
delete value.extra.raw;
}
}
if (t.isJSXNamespacedName(node.name)) {
node.name = t.stringLiteral(
node.name.namespace.name + ":" + node.name.name.name,
);
} else if (t.isValidIdentifier(node.name.name, false)) {
node.name.type = "Identifier";
} else {
node.name = t.stringLiteral(node.name.name);
}
return t.inherits(t.objectProperty(node.name, value), node);
}
// Builds JSX into:
// Production: React.jsx(type, arguments, key)
// Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self)
function buildJSXElementCall(path, file) {
const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
const tagExpr = convertJSXIdentifier(
openingPath.node.name,
openingPath.node,
);
const args = [];
let tagName;
if (t.isIdentifier(tagExpr)) {
tagName = tagExpr.name;
} else if (t.isLiteral(tagExpr)) {
tagName = tagExpr.value;
}
const state = {
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};
if (options.pre) {
options.pre(state, file);
}
let attribs = [];
const extracted = Object.create(null);
// for React.jsx, key, __source (dev), and __self (dev) is passed in as
// a separate argument rather than in the args object. We go through the
// props and filter out these three keywords so we can pass them in
// as separate arguments later
for (const attr of openingPath.get("attributes")) {
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name)) {
const { name } = attr.node.name;
switch (name) {
case "__source":
case "__self":
if (extracted[name]) throw sourceSelfError(path, name);
/* falls through */
case "key":
extracted[name] = convertAttributeValue(attr.node.value);
break;
default:
attribs.push(attr.node);
}
} else {
attribs.push(attr.node);
}
}
if (attribs.length || path.node.children.length) {
attribs = buildJSXOpeningElementAttributes(
attribs,
file,
path.node.children,
);
} else {
// attributes should never be null
attribs = t.objectExpression([]);
}
args.push(attribs);
if (!options.development) {
if (extracted.key !== undefined) {
args.push(extracted.key);
}
} else {
// isStaticChildren, __source, and __self are only used in development
// automatically include __source and __self in this plugin
// so we can eliminate the need for separate Babel plugins in Babel 8
args.push(
extracted.key ?? path.scope.buildUndefinedNode(),
t.booleanLiteral(path.node.children.length > 1),
extracted.__source ?? path.scope.buildUndefinedNode(),
extracted.__self ?? t.thisExpression(),
);
}
if (options.post) {
options.post(state, file);
}
const call =
state.call ||
t.callExpression(
path.node.children.length > 1 ? state.jsxStaticCallee : state.jsxCallee,
args,
);
if (state.pure) annotateAsPure(call);
return call;
}
// Builds props for React.jsx. This function adds children into the props
// and ensures that props is always an object
function buildJSXOpeningElementAttributes(attribs, file, children) {
const props = attribs.map(convertAttribute);
// In React.jsx, children is no longer a separate argument, but passed in
// through the argument object
if (children && children.length > 0) {
if (children.length === 1) {
props.push(t.objectProperty(t.identifier("children"), children[0]));
} else {
props.push(
t.objectProperty(
t.identifier("children"),
t.arrayExpression(children),
),
);
}
}
return t.objectExpression(props);
}
// Builds JSX Fragment <></> into
// Production: React.jsx(type, arguments)
// Development: React.jsxDEV(type, { children})
function buildJSXFragmentCall(path, file) {
const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
const args = [];
const tagName = null;
const tagExpr = file.get("@babel/plugin-react-jsx/jsxFragIdentifier")();
const state = {
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};
if (options.pre) {
options.pre(state, file);
}
let childrenNode;
if (path.node.children.length > 0) {
if (path.node.children.length === 1) {
childrenNode = path.node.children[0];
} else {
childrenNode = t.arrayExpression(path.node.children);
}
}
args.push(
t.objectExpression(
childrenNode !== undefined
? [t.objectProperty(t.identifier("children"), childrenNode)]
: [],
),
);
if (options.development) {
args.push(
path.scope.buildUndefinedNode(),
t.booleanLiteral(path.node.children.length > 1),
);
}
if (options.post) {
options.post(state, file);
}
const call =
state.call ||
t.callExpression(
path.node.children.length > 1 ? state.jsxStaticCallee : state.jsxCallee,
args,
);
if (state.pure) annotateAsPure(call);
return call;
}
function buildCreateElementFragmentCall(path, file) {
if (options.filter && !options.filter(path.node, file)) {
return;
}
const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
const args = [];
const tagName = null;
const tagExpr = file.get("@babel/plugin-react-jsx/jsxFragIdentifier")();
const state = {
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};
if (options.pre) {
options.pre(state, file);
}
// no attributes are allowed with <> syntax
args.push(t.nullLiteral(), ...path.node.children);
if (options.post) {
options.post(state, file);
}
file.set("@babel/plugin-react-jsx/usedFragment", true);
const call =
state.call || t.callExpression(state.createElementCallee, args);
if (state.pure) annotateAsPure(call);
return call;
}
// Builds JSX into:
// Production: React.createElement(type, arguments, children)
// Development: React.createElement(type, arguments, children, source, self)
function buildCreateElementCall(path, file) {
const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
const tagExpr = convertJSXIdentifier(
openingPath.node.name,
openingPath.node,
);
const args = [];
let tagName;
if (t.isIdentifier(tagExpr)) {
tagName = tagExpr.name;
} else if (t.isLiteral(tagExpr)) {
tagName = tagExpr.value;
}
const state = {
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};
if (options.pre) {
options.pre(state, file);
}
const attribs = buildCreateElementOpeningElementAttributes(
file,
path,
openingPath.node.attributes,
);
args.push(attribs, ...path.node.children);
if (options.post) {
options.post(state, file);
}
const call =
state.call || t.callExpression(state.createElementCallee, args);
if (state.pure) annotateAsPure(call);
return call;
}
/**
* The logic for this is quite terse. It's because we need to
* support spread elements. We loop over all attributes,
* breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing.
*/
function buildCreateElementOpeningElementAttributes(file, path, attribs) {
// TODO (Babel 8): Only leave this branch of the code and remove the rest
if (
RUNTIME_DEFAULT === "automatic" ||
file.get("@babel/plugin-react-jsx/runtime") === "automatic"
) {
const props = [];
const found = Object.create(null);
for (const attr of attribs) {
const name =
t.isJSXAttribute(attr) &&
t.isJSXIdentifier(attr.name) &&
attr.name.name;
if (name === "__source" || name === "__self") {
if (found[name]) throw sourceSelfError(path, name);
found[name] = true;
}
props.push(convertAttribute(attr));
}
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}
let props = [];
const objs = [];
for (const attr of attribs) {
if (useSpread || !t.isJSXSpreadAttribute(attr)) {
props.push(convertAttribute(attr));
} else {
if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}
objs.push(attr.argument);
}
}
if (!props.length && !objs.length) {
return t.nullLiteral();
}
if (useSpread) {
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}
if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}
if (objs.length === 1) {
return objs[0];
}
// looks like we have multiple objects
if (!t.isObjectExpression(objs[0])) {
objs.unshift(t.objectExpression([]));
}
const helper = useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
: file.addHelper("extends");
// spread it
return t.callExpression(helper, objs);
}
function sourceSelfError(path, name) {
const pluginName = `transform-react-jsx-${name.slice(2)}`;
return path.buildCodeFrameError(
`Duplicate ${name} prop found. You are most likely using the deprecated ${pluginName} Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.`,
);
}
}

View File

@ -16,9 +16,7 @@
"babel-plugin" "babel-plugin"
], ],
"dependencies": { "dependencies": {
"@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11", "@babel/plugin-transform-react-jsx": "workspace:^7.12.11"
"@babel/helper-plugin-utils": "workspace:^7.10.4",
"@babel/plugin-syntax-jsx": "workspace:^7.12.1"
}, },
"peerDependencies": { "peerDependencies": {
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"

View File

@ -1,59 +1,3 @@
import jsx from "@babel/plugin-syntax-jsx"; /* eslint-disable @babel/development/plugin-name */
import { helper } from "@babel/helper-builder-react-jsx-experimental";
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
export default declare((api, options) => { export { default } from "@babel/plugin-transform-react-jsx/lib/development.js";
const PURE_ANNOTATION = options.pure;
const visitor = helper(api, {
pre(state) {
const tagName = state.tagName;
const args = state.args;
if (t.react.isCompatTag(tagName)) {
args.push(t.stringLiteral(tagName));
} else {
args.push(state.tagExpr);
}
},
post(state, pass) {
if (pass.get("@babel/plugin-react-jsx/runtime") === "classic") {
state.createElementCallee = pass.get(
"@babel/plugin-react-jsx/createElementIdentifier",
)();
state.pure =
PURE_ANNOTATION ?? !pass.get("@babel/plugin-react-jsx/pragmaSet");
} else {
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 ??
!pass.get("@babel/plugin-react-jsx/importSourceSet");
}
},
...options,
development: true,
});
return {
name: "transform-react-jsx",
inherits: jsx,
visitor,
};
});

View File

@ -16,10 +16,11 @@
"babel-plugin" "babel-plugin"
], ],
"dependencies": { "dependencies": {
"@babel/helper-builder-react-jsx": "workspace:^7.10.4", "@babel/helper-annotate-as-pure": "workspace:^7.12.10",
"@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11", "@babel/helper-module-imports": "workspace:^7.12.5",
"@babel/helper-plugin-utils": "workspace:^7.10.4", "@babel/helper-plugin-utils": "workspace:^7.10.4",
"@babel/plugin-syntax-jsx": "workspace:^7.12.1" "@babel/plugin-syntax-jsx": "workspace:^7.12.1",
"@babel/types": "workspace:^7.12.11"
}, },
"peerDependencies": { "peerDependencies": {
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"

View File

@ -0,0 +1,696 @@
import jsx from "@babel/plugin-syntax-jsx";
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
import annotateAsPure from "@babel/helper-annotate-as-pure";
const DEFAULT = {
importSource: "react",
runtime: "automatic",
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
};
const JSX_SOURCE_ANNOTATION_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/;
const JSX_RUNTIME_ANNOTATION_REGEX = /\*?\s*@jsxRuntime\s+([^\s]+)/;
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/;
const get = (pass, name) => pass.get(`@babel/plugin-react-jsx/${name}`);
const set = (pass, name, v) => pass.set(`@babel/plugin-react-jsx/${name}`, v);
export default function createPlugin({ name, development }) {
return declare((api, options) => {
const {
pure: PURE_ANNOTATION,
throwIfNamespace = true,
// TODO (Babel 8): It should throw if this option is used with the automatic runtime
filter,
// TODO (Babel 8): Remove `useBuiltIns` & `useSpread`
useSpread = false,
useBuiltIns = false,
runtime: RUNTIME_DEFAULT = development ? "automatic" : "classic",
importSource: IMPORT_SOURCE_DEFAULT = DEFAULT.importSource,
pragma: PRAGMA_DEFAULT = DEFAULT.pragma,
pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag,
} = options;
// TOOD(Babel 8): If the runtime is 'automatic', we should throw when useSpread/useBuiltIns are set
if (RUNTIME_DEFAULT === "classic") {
if (typeof useSpread !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useSpread (defaults to false)",
);
}
if (typeof useBuiltIns !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useBuiltIns (defaults to false)",
);
}
if (useSpread && useBuiltIns) {
throw new Error(
"transform-react-jsx currently only accepts useBuiltIns or useSpread " +
"but not both",
);
}
}
const injectMetaPropertiesVisitor = {
JSXOpeningElement(path, state) {
for (const attr of path.get("attributes")) {
if (!attr.isJSXElement()) continue;
const { name } = attr.node.name;
if (name === "__source" || name === "__self") {
throw path.buildCodeFrameError(
`__source and __self should not be defined in props and are reserved for internal usage.`,
);
}
}
const self = t.jsxAttribute(
t.jsxIdentifier("__self"),
t.jsxExpressionContainer(t.thisExpression()),
);
const source = t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, state)),
);
path.pushContainer("attributes", [self, source]);
},
};
return {
name,
inherits: jsx,
visitor: {
JSXNamespacedName(path) {
if (throwIfNamespace) {
throw path.buildCodeFrameError(
`Namespace tags are not supported by default. React's JSX doesn't support namespace tags. \
You can set \`throwIfNamespace: false\` to bypass this warning.`,
);
}
},
JSXSpreadChild(path) {
throw path.buildCodeFrameError(
"Spread children are not supported in React.",
);
},
Program: {
enter(path, state) {
const { file } = state;
let runtime = RUNTIME_DEFAULT;
let source = IMPORT_SOURCE_DEFAULT;
let pragma = PRAGMA_DEFAULT;
let pragmaFrag = PRAGMA_FRAG_DEFAULT;
let sourceSet = !!options.importSource;
let pragmaSet = !!options.pragma;
let pragmaFragSet = !!options.pragmaFrag;
if (file.ast.comments) {
for (const comment of (file.ast.comments: Array<Object>)) {
const sourceMatches = JSX_SOURCE_ANNOTATION_REGEX.exec(
comment.value,
);
if (sourceMatches) {
source = sourceMatches[1];
sourceSet = true;
}
const runtimeMatches = JSX_RUNTIME_ANNOTATION_REGEX.exec(
comment.value,
);
if (runtimeMatches) {
runtime = runtimeMatches[1];
}
const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
if (jsxMatches) {
pragma = jsxMatches[1];
pragmaSet = true;
}
const jsxFragMatches = JSX_FRAG_ANNOTATION_REGEX.exec(
comment.value,
);
if (jsxFragMatches) {
pragmaFrag = jsxFragMatches[1];
pragmaFragSet = true;
}
}
}
set(state, "runtime", runtime);
if (runtime === "classic") {
if (sourceSet) {
throw path.buildCodeFrameError(
`importSource cannot be set when runtime is classic.`,
);
}
const createElement = toMemberExpression(pragma);
const fragment = toMemberExpression(pragmaFrag);
set(state, "id/createElement", () => t.cloneNode(createElement));
set(state, "id/fragment", () => t.cloneNode(fragment));
set(state, "defaultPure", pragma === DEFAULT.pragma);
} else if (runtime === "automatic") {
if (pragmaSet || pragmaFragSet) {
throw path.buildCodeFrameError(
`pragma and pragmaFrag cannot be set when runtime is automatic.`,
);
}
const define = (name, id) =>
set(state, name, createImportLazily(state, path, id, source));
define("id/jsx", development ? "jsxDEV" : "jsx");
define("id/jsxs", development ? "jsxDEV" : "jsxs");
define("id/createElement", "createElement");
define("id/fragment", "Fragment");
set(state, "defaultPure", source === DEFAULT.importSource);
} else {
throw path.buildCodeFrameError(
`Runtime must be either "classic" or "automatic".`,
);
}
if (development) {
path.traverse(injectMetaPropertiesVisitor, state);
}
},
// TODO (Babel 8): Decide if this should be removed or brought back.
// see: https://github.com/babel/babel/pull/12253#discussion_r513086528
//
// exit(path, state) {
// if (
// get(state, "runtime") === "classic" &&
// get(state, "pragmaSet") &&
// get(state, "usedFragment") &&
// !get(state, "pragmaFragSet")
// ) {
// throw new Error(
// "transform-react-jsx: pragma has been set but " +
// "pragmaFrag has not been set",
// );
// }
// },
},
JSXElement: {
exit(path, file) {
let callExpr;
if (
get(file, "runtime") === "classic" ||
shouldUseCreateElement(path)
) {
callExpr = buildCreateElementCall(path, file);
} else {
callExpr = buildJSXElementCall(path, file);
}
path.replaceWith(t.inherits(callExpr, path.node));
},
},
JSXFragment: {
exit(path, file) {
let callExpr;
if (get(file, "runtime") === "classic") {
callExpr = buildCreateElementFragmentCall(path, file);
} else {
callExpr = buildJSXFragmentCall(path, file);
}
path.replaceWith(t.inherits(callExpr, path.node));
},
},
JSXAttribute(path) {
if (t.isJSXElement(path.node.value)) {
path.node.value = t.jsxExpressionContainer(path.node.value);
}
},
},
};
function call(pass, name, args) {
const node = t.callExpression(get(pass, `id/${name}`)(), args);
if (PURE_ANNOTATION ?? get(pass, "defaultPure")) annotateAsPure(node);
return node;
}
// We want to use React.createElement, even in the case of
// jsx, for <div {...props} key={key} /> to distinguish it
// from <div key={key} {...props} />. This is an intermediary
// step while we deprecate key spread from props. Afterwards,
// we will stop using createElement in the transform.
function shouldUseCreateElement(path) {
const openingPath = path.get("openingElement");
const attributes = openingPath.node.attributes;
let seenPropsSpread = false;
for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i];
if (
seenPropsSpread &&
t.isJSXAttribute(attr) &&
attr.name.name === "key"
) {
return true;
} else if (t.isJSXSpreadAttribute(attr)) {
seenPropsSpread = true;
}
}
return false;
}
function convertJSXIdentifier(node, parent) {
if (t.isJSXIdentifier(node)) {
if (node.name === "this" && t.isReferenced(node, parent)) {
return t.thisExpression();
} else if (t.isValidIdentifier(node.name, false)) {
node.type = "Identifier";
} else {
return t.stringLiteral(node.name);
}
} else if (t.isJSXMemberExpression(node)) {
return t.memberExpression(
convertJSXIdentifier(node.object, node),
convertJSXIdentifier(node.property, node),
);
} else if (t.isJSXNamespacedName(node)) {
/**
* If the flag "throwIfNamespace" is false
* print XMLNamespace like string literal
*/
return t.stringLiteral(`${node.namespace.name}:${node.name.name}`);
}
return node;
}
function convertAttributeValue(node) {
if (t.isJSXExpressionContainer(node)) {
return node.expression;
} else {
return node;
}
}
function convertAttribute(node) {
const value = convertAttributeValue(node.value || t.booleanLiteral(true));
if (t.isJSXSpreadAttribute(node)) {
return t.spreadElement(node.argument);
}
if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
value.value = value.value.replace(/\n\s+/g, " ");
// "raw" JSXText should not be used from a StringLiteral because it needs to be escaped.
delete value.extra?.raw;
}
if (t.isJSXNamespacedName(node.name)) {
node.name = t.stringLiteral(
node.name.namespace.name + ":" + node.name.name.name,
);
} else if (t.isValidIdentifier(node.name.name, false)) {
node.name.type = "Identifier";
} else {
node.name = t.stringLiteral(node.name.name);
}
return t.inherits(t.objectProperty(node.name, value), node);
}
function buildChildrenProperty(children) {
let childrenNode;
if (children.length === 1) {
childrenNode = children[0];
} else if (children.length > 1) {
childrenNode = t.arrayExpression(children);
} else {
return undefined;
}
return t.objectProperty(t.identifier("children"), childrenNode);
}
// Builds JSX into:
// Production: React.jsx(type, arguments, key)
// Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self)
function buildJSXElementCall(path, file) {
const openingPath = path.get("openingElement");
const args = [getTag(openingPath)];
let attribs = [];
const extracted = Object.create(null);
// for React.jsx, key, __source (dev), and __self (dev) is passed in as
// a separate argument rather than in the args object. We go through the
// props and filter out these three keywords so we can pass them in
// as separate arguments later
for (const attr of openingPath.get("attributes")) {
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name)) {
const { name } = attr.node.name;
switch (name) {
case "__source":
case "__self":
if (extracted[name]) throw sourceSelfError(path, name);
/* falls through */
case "key":
extracted[name] = convertAttributeValue(attr.node.value);
break;
default:
attribs.push(attr.node);
}
} else {
attribs.push(attr.node);
}
}
const children = t.react.buildChildren(path.node);
if (attribs.length || children.length) {
attribs = buildJSXOpeningElementAttributes(attribs, file, children);
} else {
// attributes should never be null
attribs = t.objectExpression([]);
}
args.push(attribs);
if (development) {
// isStaticChildren, __source, and __self are only used in development
// automatically include __source and __self in this plugin
// so we can eliminate the need for separate Babel plugins in Babel 8
args.push(
extracted.key ?? path.scope.buildUndefinedNode(),
t.booleanLiteral(children.length > 1),
extracted.__source ?? path.scope.buildUndefinedNode(),
extracted.__self ?? t.thisExpression(),
);
} else if (extracted.key !== undefined) {
args.push(extracted.key);
}
return call(file, children.length > 1 ? "jsxs" : "jsx", args);
}
// Builds props for React.jsx. This function adds children into the props
// and ensures that props is always an object
function buildJSXOpeningElementAttributes(attribs, file, children) {
const props = attribs.map(convertAttribute);
// In React.jsx, children is no longer a separate argument, but passed in
// through the argument object
if (children?.length > 0) {
props.push(buildChildrenProperty(children));
}
return t.objectExpression(props);
}
// Builds JSX Fragment <></> into
// Production: React.jsx(type, arguments)
// Development: React.jsxDEV(type, { children })
function buildJSXFragmentCall(path, file) {
const args = [get(file, "id/fragment")()];
const children = t.react.buildChildren(path.node);
args.push(
t.objectExpression(
children.length > 0 ? [buildChildrenProperty(children)] : [],
),
);
if (development) {
args.push(
path.scope.buildUndefinedNode(),
t.booleanLiteral(children.length > 1),
);
}
return call(file, children.length > 1 ? "jsxs" : "jsx", args);
}
// Builds JSX Fragment <></> into
// React.createElement(React.Fragment, null, ...children)
function buildCreateElementFragmentCall(path, file) {
if (filter && !filter(path.node, file)) return;
return call(file, "createElement", [
get(file, "id/fragment")(),
t.nullLiteral(),
...t.react.buildChildren(path.node),
]);
}
// Builds JSX into:
// Production: React.createElement(type, arguments, children)
// Development: React.createElement(type, arguments, children, source, self)
function buildCreateElementCall(path, file) {
const openingPath = path.get("openingElement");
return call(file, "createElement", [
getTag(openingPath),
buildCreateElementOpeningElementAttributes(
file,
path,
openingPath.node.attributes,
),
...t.react.buildChildren(path.node),
]);
}
function getTag(openingPath) {
const tagExpr = convertJSXIdentifier(
openingPath.node.name,
openingPath.node,
);
let tagName;
if (t.isIdentifier(tagExpr)) {
tagName = tagExpr.name;
} else if (t.isLiteral(tagExpr)) {
tagName = tagExpr.value;
}
if (t.react.isCompatTag(tagName)) {
return t.stringLiteral(tagName);
} else {
return tagExpr;
}
}
/**
* The logic for this is quite terse. It's because we need to
* support spread elements. We loop over all attributes,
* breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing.
*/
function buildCreateElementOpeningElementAttributes(file, path, attribs) {
// TODO (Babel 8): Only leave this branch of the code and remove the rest
if (
RUNTIME_DEFAULT === "automatic" ||
get(file, "runtime") === "automatic"
) {
const props = [];
const found = Object.create(null);
for (const attr of attribs) {
const name =
t.isJSXAttribute(attr) &&
t.isJSXIdentifier(attr.name) &&
attr.name.name;
if (name === "__source" || name === "__self") {
if (found[name]) throw sourceSelfError(path, name);
found[name] = true;
}
props.push(convertAttribute(attr));
}
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}
let props = [];
const objs = [];
for (const attr of attribs) {
if (useSpread || !t.isJSXSpreadAttribute(attr)) {
props.push(convertAttribute(attr));
} else {
if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}
objs.push(attr.argument);
}
}
if (!props.length && !objs.length) {
return t.nullLiteral();
}
if (useSpread) {
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}
if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}
if (objs.length === 1) {
return objs[0];
}
// looks like we have multiple objects
if (!t.isObjectExpression(objs[0])) {
objs.unshift(t.objectExpression([]));
}
const helper = useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
: file.addHelper("extends");
// spread it
return t.callExpression(helper, objs);
}
});
function getSource(source, importName) {
switch (importName) {
case "Fragment":
return `${source}/${development ? "jsx-dev-runtime" : "jsx-runtime"}`;
case "jsxDEV":
return `${source}/jsx-dev-runtime`;
case "jsx":
case "jsxs":
return `${source}/jsx-runtime`;
case "createElement":
return source;
}
}
function createImportLazily(pass, path, importName, source) {
return () => {
const actualSource = getSource(source, importName);
if (isModule(path)) {
let reference = get(pass, `imports/${importName}`);
if (reference) return t.cloneNode(reference);
reference = addNamed(path, importName, actualSource, {
importedInterop: "uncompiled",
});
set(pass, `imports/${importName}`, reference);
return reference;
} else {
let reference = get(pass, `requires/${actualSource}`);
if (reference) {
reference = t.cloneNode(reference);
} else {
reference = addNamespace(path, actualSource, {
importedInterop: "uncompiled",
});
set(pass, `requires/${actualSource}`, reference);
}
return t.memberExpression(reference, t.identifier(importName));
}
};
}
}
function toMemberExpression(id) {
return id
.split(".")
.map(name => t.identifier(name))
.reduce((object, property) => t.memberExpression(object, property));
}
function makeSource(path, state) {
const location = path.node.loc;
if (!location) {
// the element was generated and doesn't have location information
return path.scope.buildUndefinedNode();
}
if (!state.fileNameIdentifier) {
const { filename = "" } = state;
const fileNameIdentifier = path.scope.generateUidIdentifier("_jsxFileName");
const scope = path.hub.getScope();
if (scope) {
scope.push({
id: fileNameIdentifier,
init: t.stringLiteral(filename),
});
}
state.fileNameIdentifier = fileNameIdentifier;
}
return makeTrace(
t.cloneNode(state.fileNameIdentifier),
location.start.line,
location.start.column,
);
}
function makeTrace(fileNameIdentifier, lineNumber, column0Based) {
const fileLineLiteral =
lineNumber != null ? t.numericLiteral(lineNumber) : t.nullLiteral();
const fileColumnLiteral =
column0Based != null ? t.numericLiteral(column0Based + 1) : t.nullLiteral();
const fileNameProperty = t.objectProperty(
t.identifier("fileName"),
fileNameIdentifier,
);
const lineNumberProperty = t.objectProperty(
t.identifier("lineNumber"),
fileLineLiteral,
);
const columnNumberProperty = t.objectProperty(
t.identifier("columnNumber"),
fileColumnLiteral,
);
return t.objectExpression([
fileNameProperty,
lineNumberProperty,
columnNumberProperty,
]);
}
function sourceSelfError(path, name) {
const pluginName = `transform-react-jsx-${name.slice(2)}`;
return path.buildCodeFrameError(
`Duplicate ${name} prop found. You are most likely using the deprecated ${pluginName} Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.`,
);
}

View File

@ -0,0 +1,6 @@
import createPlugin from "./create-plugin.js";
export default createPlugin({
name: "transform-react-jsx/development",
development: true,
});

View File

@ -1,61 +1,8 @@
import jsx from "@babel/plugin-syntax-jsx"; /* eslint-disable @babel/development/plugin-name */
import { helper } from "@babel/helper-builder-react-jsx-experimental";
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
export default declare((api, options) => { import createPlugin from "./create-plugin.js";
const { runtime = "classic" } = options;
const PURE_ANNOTATION = options.pure;
const visitor = helper(api, { export default createPlugin({
pre(state) { name: "transform-react-jsx",
const tagName = state.tagName; development: false,
const args = state.args;
if (t.react.isCompatTag(tagName)) {
args.push(t.stringLiteral(tagName));
} else {
args.push(state.tagExpr);
}
},
post(state, pass) {
if (pass.get("@babel/plugin-react-jsx/runtime") === "classic") {
state.createElementCallee = pass.get(
"@babel/plugin-react-jsx/createElementIdentifier",
)();
state.pure =
PURE_ANNOTATION ?? !pass.get("@babel/plugin-react-jsx/pragmaSet");
} else {
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 ??
!pass.get("@babel/plugin-react-jsx/importSourceSet");
}
},
...options,
development: false,
runtime,
});
return {
name: "transform-react-jsx",
inherits: jsx,
visitor,
};
}); });

View File

@ -54,15 +54,14 @@ module.exports = function () {
); );
} }
const matches = importee.match(/^@babel\/([^/]+)$/); const matches = importee.match(
/^@babel\/(?<pkg>[^/]+)(?:\/lib\/(?<internal>.*?))?$/
);
if (!matches) return null; if (!matches) return null;
const { pkg, internal } = matches.groups;
// resolve babel package names to their src index file // resolve babel package names to their src index file
const packageFolder = path.join( const packageFolder = path.join(dirname, "packages", `babel-${pkg}`);
dirname,
"packages",
`babel-${matches[1]}`
);
let packageJsonSource; let packageJsonSource;
try { try {
@ -76,10 +75,11 @@ module.exports = function () {
const packageJson = JSON.parse(packageJsonSource); const packageJson = JSON.parse(packageJsonSource);
const filename = const filename = internal
typeof packageJson["browser"] === "string" ? `src/${internal}`
? packageJson["browser"] : typeof packageJson["browser"] === "string"
: packageJson["main"]; ? packageJson["browser"]
: packageJson["main"];
const asJS = path.normalize( const asJS = path.normalize(
path.join( path.join(

View File

@ -301,16 +301,6 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@babel/helper-builder-react-jsx-experimental@workspace:^7.12.11, @babel/helper-builder-react-jsx-experimental@workspace:packages/babel-helper-builder-react-jsx-experimental":
version: 0.0.0-use.local
resolution: "@babel/helper-builder-react-jsx-experimental@workspace:packages/babel-helper-builder-react-jsx-experimental"
dependencies:
"@babel/helper-annotate-as-pure": "workspace:^7.12.10"
"@babel/helper-module-imports": "workspace:^7.12.5"
"@babel/types": "workspace:^7.12.11"
languageName: unknown
linkType: soft
"@babel/helper-builder-react-jsx@workspace:^7.10.4, @babel/helper-builder-react-jsx@workspace:packages/babel-helper-builder-react-jsx": "@babel/helper-builder-react-jsx@workspace:^7.10.4, @babel/helper-builder-react-jsx@workspace:packages/babel-helper-builder-react-jsx":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@babel/helper-builder-react-jsx@workspace:packages/babel-helper-builder-react-jsx" resolution: "@babel/helper-builder-react-jsx@workspace:packages/babel-helper-builder-react-jsx"
@ -2546,10 +2536,8 @@ __metadata:
resolution: "@babel/plugin-transform-react-jsx-development@workspace:packages/babel-plugin-transform-react-jsx-development" resolution: "@babel/plugin-transform-react-jsx-development@workspace:packages/babel-plugin-transform-react-jsx-development"
dependencies: dependencies:
"@babel/core": "workspace:*" "@babel/core": "workspace:*"
"@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11"
"@babel/helper-plugin-test-runner": "workspace:*" "@babel/helper-plugin-test-runner": "workspace:*"
"@babel/helper-plugin-utils": "workspace:^7.10.4" "@babel/plugin-transform-react-jsx": "workspace:^7.12.11"
"@babel/plugin-syntax-jsx": "workspace:^7.12.1"
peerDependencies: peerDependencies:
"@babel/core": ^7.0.0-0 "@babel/core": ^7.0.0-0
languageName: unknown languageName: unknown
@ -2581,16 +2569,17 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@babel/plugin-transform-react-jsx@workspace:*, @babel/plugin-transform-react-jsx@workspace:^7.12.10, @babel/plugin-transform-react-jsx@workspace:packages/babel-plugin-transform-react-jsx": "@babel/plugin-transform-react-jsx@workspace:*, @babel/plugin-transform-react-jsx@workspace:^7.12.10, @babel/plugin-transform-react-jsx@workspace:^7.12.11, @babel/plugin-transform-react-jsx@workspace:packages/babel-plugin-transform-react-jsx":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@babel/plugin-transform-react-jsx@workspace:packages/babel-plugin-transform-react-jsx" resolution: "@babel/plugin-transform-react-jsx@workspace:packages/babel-plugin-transform-react-jsx"
dependencies: dependencies:
"@babel/core": "workspace:*" "@babel/core": "workspace:*"
"@babel/helper-builder-react-jsx": "workspace:^7.10.4" "@babel/helper-annotate-as-pure": "workspace:^7.12.10"
"@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11" "@babel/helper-module-imports": "workspace:^7.12.5"
"@babel/helper-plugin-test-runner": "workspace:*" "@babel/helper-plugin-test-runner": "workspace:*"
"@babel/helper-plugin-utils": "workspace:^7.10.4" "@babel/helper-plugin-utils": "workspace:^7.10.4"
"@babel/plugin-syntax-jsx": "workspace:^7.12.1" "@babel/plugin-syntax-jsx": "workspace:^7.12.1"
"@babel/types": "workspace:^7.12.11"
peerDependencies: peerDependencies:
"@babel/core": ^7.0.0-0 "@babel/core": ^7.0.0-0
languageName: unknown languageName: unknown