diff --git a/packages/babel-helper-builder-react-jsx-experimental/package.json b/packages/babel-helper-builder-react-jsx-experimental/package.json deleted file mode 100644 index 45972e95ad..0000000000 --- a/packages/babel-helper-builder-react-jsx-experimental/package.json +++ /dev/null @@ -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" - } -} diff --git a/packages/babel-helper-builder-react-jsx-experimental/src/index.js b/packages/babel-helper-builder-react-jsx-experimental/src/index.js deleted file mode 100644 index 7254acc254..0000000000 --- a/packages/babel-helper-builder-react-jsx-experimental/src/index.js +++ /dev/null @@ -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)) { - 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
to distinguish it - // from
. 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.`, - ); - } -} diff --git a/packages/babel-plugin-transform-react-jsx-development/package.json b/packages/babel-plugin-transform-react-jsx-development/package.json index 657ab0d1a4..1062f2054a 100644 --- a/packages/babel-plugin-transform-react-jsx-development/package.json +++ b/packages/babel-plugin-transform-react-jsx-development/package.json @@ -16,9 +16,7 @@ "babel-plugin" ], "dependencies": { - "@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11", - "@babel/helper-plugin-utils": "workspace:^7.10.4", - "@babel/plugin-syntax-jsx": "workspace:^7.12.1" + "@babel/plugin-transform-react-jsx": "workspace:^7.12.11" }, "peerDependencies": { "@babel/core": "^7.0.0-0" 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 f9942948d5..5273b9f25d 100644 --- a/packages/babel-plugin-transform-react-jsx-development/src/index.js +++ b/packages/babel-plugin-transform-react-jsx-development/src/index.js @@ -1,59 +1,3 @@ -import jsx from "@babel/plugin-syntax-jsx"; -import { helper } from "@babel/helper-builder-react-jsx-experimental"; -import { declare } from "@babel/helper-plugin-utils"; -import { types as t } from "@babel/core"; +/* eslint-disable @babel/development/plugin-name */ -export default declare((api, options) => { - 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, - }; -}); +export { default } from "@babel/plugin-transform-react-jsx/lib/development.js"; diff --git a/packages/babel-plugin-transform-react-jsx/package.json b/packages/babel-plugin-transform-react-jsx/package.json index 5a70454444..efd5eb0f15 100644 --- a/packages/babel-plugin-transform-react-jsx/package.json +++ b/packages/babel-plugin-transform-react-jsx/package.json @@ -16,10 +16,11 @@ "babel-plugin" ], "dependencies": { - "@babel/helper-builder-react-jsx": "workspace:^7.10.4", - "@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11", + "@babel/helper-annotate-as-pure": "workspace:^7.12.10", + "@babel/helper-module-imports": "workspace:^7.12.5", "@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": { "@babel/core": "^7.0.0-0" diff --git a/packages/babel-plugin-transform-react-jsx/src/create-plugin.js b/packages/babel-plugin-transform-react-jsx/src/create-plugin.js new file mode 100644 index 0000000000..77134bb4d4 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/src/create-plugin.js @@ -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)) { + 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
to distinguish it + // from
. 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.`, + ); +} diff --git a/packages/babel-plugin-transform-react-jsx/src/development.js b/packages/babel-plugin-transform-react-jsx/src/development.js new file mode 100644 index 0000000000..4de15f885d --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/src/development.js @@ -0,0 +1,6 @@ +import createPlugin from "./create-plugin.js"; + +export default createPlugin({ + name: "transform-react-jsx/development", + development: true, +}); diff --git a/packages/babel-plugin-transform-react-jsx/src/index.js b/packages/babel-plugin-transform-react-jsx/src/index.js index 0143eed733..8b83306d99 100644 --- a/packages/babel-plugin-transform-react-jsx/src/index.js +++ b/packages/babel-plugin-transform-react-jsx/src/index.js @@ -1,61 +1,8 @@ -import jsx from "@babel/plugin-syntax-jsx"; -import { helper } from "@babel/helper-builder-react-jsx-experimental"; -import { declare } from "@babel/helper-plugin-utils"; -import { types as t } from "@babel/core"; +/* eslint-disable @babel/development/plugin-name */ -export default declare((api, options) => { - const { runtime = "classic" } = options; - const PURE_ANNOTATION = options.pure; +import createPlugin from "./create-plugin.js"; - 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: false, - runtime, - }); - - return { - name: "transform-react-jsx", - inherits: jsx, - visitor, - }; +export default createPlugin({ + name: "transform-react-jsx", + development: false, }); diff --git a/scripts/rollup-plugin-babel-source.js b/scripts/rollup-plugin-babel-source.js index 1c131c804e..7f4b794c37 100644 --- a/scripts/rollup-plugin-babel-source.js +++ b/scripts/rollup-plugin-babel-source.js @@ -54,15 +54,14 @@ module.exports = function () { ); } - const matches = importee.match(/^@babel\/([^/]+)$/); + const matches = importee.match( + /^@babel\/(?[^/]+)(?:\/lib\/(?.*?))?$/ + ); if (!matches) return null; + const { pkg, internal } = matches.groups; // resolve babel package names to their src index file - const packageFolder = path.join( - dirname, - "packages", - `babel-${matches[1]}` - ); + const packageFolder = path.join(dirname, "packages", `babel-${pkg}`); let packageJsonSource; try { @@ -76,10 +75,11 @@ module.exports = function () { const packageJson = JSON.parse(packageJsonSource); - const filename = - typeof packageJson["browser"] === "string" - ? packageJson["browser"] - : packageJson["main"]; + const filename = internal + ? `src/${internal}` + : typeof packageJson["browser"] === "string" + ? packageJson["browser"] + : packageJson["main"]; const asJS = path.normalize( path.join( diff --git a/yarn.lock b/yarn.lock index a04a256a61..22e240fe51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -301,16 +301,6 @@ __metadata: languageName: unknown 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": version: 0.0.0-use.local 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" dependencies: "@babel/core": "workspace:*" - "@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11" "@babel/helper-plugin-test-runner": "workspace:*" - "@babel/helper-plugin-utils": "workspace:^7.10.4" - "@babel/plugin-syntax-jsx": "workspace:^7.12.1" + "@babel/plugin-transform-react-jsx": "workspace:^7.12.11" peerDependencies: "@babel/core": ^7.0.0-0 languageName: unknown @@ -2581,16 +2569,17 @@ __metadata: languageName: unknown 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 resolution: "@babel/plugin-transform-react-jsx@workspace:packages/babel-plugin-transform-react-jsx" dependencies: "@babel/core": "workspace:*" - "@babel/helper-builder-react-jsx": "workspace:^7.10.4" - "@babel/helper-builder-react-jsx-experimental": "workspace:^7.12.11" + "@babel/helper-annotate-as-pure": "workspace:^7.12.10" + "@babel/helper-module-imports": "workspace:^7.12.5" "@babel/helper-plugin-test-runner": "workspace:*" "@babel/helper-plugin-utils": "workspace:^7.10.4" "@babel/plugin-syntax-jsx": "workspace:^7.12.1" + "@babel/types": "workspace:^7.12.11" peerDependencies: "@babel/core": ^7.0.0-0 languageName: unknown