Improve transform-react-jsx typings (#13820)
* improve transform-react-jsx typings * rename test fixtures * remove unreachable sourceSelf check * update test fixtures * Update packages/babel-core/src/index.ts * Update packages/babel-plugin-transform-react-jsx/src/create-plugin.ts Co-authored-by: Henry Zhu <hi@henryzoo.com> * Update packages/babel-plugin-transform-react-jsx/src/create-plugin.ts Co-authored-by: Henry Zhu <hi@henryzoo.com>
This commit is contained in:
parent
41325645f8
commit
c2f747c9b9
@ -2,6 +2,7 @@ declare const PACKAGE_JSON: { name: string; version: string };
|
|||||||
export const version = PACKAGE_JSON.version;
|
export const version = PACKAGE_JSON.version;
|
||||||
|
|
||||||
export { default as File } from "./transformation/file/file";
|
export { default as File } from "./transformation/file/file";
|
||||||
|
export type { default as PluginPass } from "./transformation/plugin-pass";
|
||||||
export { default as buildExternalHelpers } from "./tools/build-external-helpers";
|
export { default as buildExternalHelpers } from "./tools/build-external-helpers";
|
||||||
export { resolvePlugin, resolvePreset } from "./config/files";
|
export { resolvePlugin, resolvePreset } from "./config/files";
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
var x = <div __self={self}></div>;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"throws": "Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self 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.",
|
||||||
|
"plugins": ["transform-react-jsx-development"]
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
var x = <div __source={source}></div>;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"throws": "Duplicate __source prop found. You are most likely using the deprecated transform-react-jsx-source 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.",
|
||||||
|
"plugins": ["transform-react-jsx-development"]
|
||||||
|
}
|
||||||
@ -1,8 +1,28 @@
|
|||||||
import jsx from "@babel/plugin-syntax-jsx";
|
import jsx from "@babel/plugin-syntax-jsx";
|
||||||
import { declare } from "@babel/helper-plugin-utils";
|
import { declare } from "@babel/helper-plugin-utils";
|
||||||
import { types as t } from "@babel/core";
|
import { types as t } from "@babel/core";
|
||||||
|
import type { PluginPass } from "@babel/core";
|
||||||
|
import type { NodePath, Visitor } from "@babel/traverse";
|
||||||
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
|
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
|
||||||
import annotateAsPure from "@babel/helper-annotate-as-pure";
|
import annotateAsPure from "@babel/helper-annotate-as-pure";
|
||||||
|
import type {
|
||||||
|
ArrowFunctionExpression,
|
||||||
|
CallExpression,
|
||||||
|
Class,
|
||||||
|
Expression,
|
||||||
|
FunctionParent,
|
||||||
|
Identifier,
|
||||||
|
JSXAttribute,
|
||||||
|
JSXElement,
|
||||||
|
JSXFragment,
|
||||||
|
JSXOpeningElement,
|
||||||
|
JSXSpreadAttribute,
|
||||||
|
MemberExpression,
|
||||||
|
ObjectExpression,
|
||||||
|
Program,
|
||||||
|
} from "@babel/types";
|
||||||
|
|
||||||
|
type Diff<T, U> = T extends U ? never : T;
|
||||||
|
|
||||||
const DEFAULT = {
|
const DEFAULT = {
|
||||||
importSource: "react",
|
importSource: "react",
|
||||||
@ -17,8 +37,10 @@ const JSX_RUNTIME_ANNOTATION_REGEX = /\*?\s*@jsxRuntime\s+([^\s]+)/;
|
|||||||
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
|
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
|
||||||
const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/;
|
const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/;
|
||||||
|
|
||||||
const get = (pass, name) => pass.get(`@babel/plugin-react-jsx/${name}`);
|
const get = (pass: PluginPass, name: string) =>
|
||||||
const set = (pass, name, v) => pass.set(`@babel/plugin-react-jsx/${name}`, v);
|
pass.get(`@babel/plugin-react-jsx/${name}`);
|
||||||
|
const set = (pass: PluginPass, name: string, v: any) =>
|
||||||
|
pass.set(`@babel/plugin-react-jsx/${name}`, v);
|
||||||
|
|
||||||
export default function createPlugin({ name, development }) {
|
export default function createPlugin({ name, development }) {
|
||||||
return declare((api, options) => {
|
return declare((api, options) => {
|
||||||
@ -90,19 +112,8 @@ export default function createPlugin({ name, development }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const injectMetaPropertiesVisitor = {
|
const injectMetaPropertiesVisitor: Visitor<PluginPass> = {
|
||||||
JSXOpeningElement(path, state) {
|
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 attributes = [];
|
const attributes = [];
|
||||||
if (isThisAllowed(path)) {
|
if (isThisAllowed(path)) {
|
||||||
attributes.push(
|
attributes.push(
|
||||||
@ -144,11 +155,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
Program: {
|
Program: {
|
||||||
enter(path, state) {
|
enter(path, state) {
|
||||||
const { file } = state;
|
const { file } = state;
|
||||||
let runtime = RUNTIME_DEFAULT;
|
let runtime: string = RUNTIME_DEFAULT;
|
||||||
|
|
||||||
let source = IMPORT_SOURCE_DEFAULT;
|
let source: string = IMPORT_SOURCE_DEFAULT;
|
||||||
let pragma = PRAGMA_DEFAULT;
|
let pragma: string = PRAGMA_DEFAULT;
|
||||||
let pragmaFrag = PRAGMA_FRAG_DEFAULT;
|
let pragmaFrag: string = PRAGMA_FRAG_DEFAULT;
|
||||||
|
|
||||||
let sourceSet = !!options.importSource;
|
let sourceSet = !!options.importSource;
|
||||||
let pragmaSet = !!options.pragma;
|
let pragmaSet = !!options.pragma;
|
||||||
@ -208,7 +219,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const define = (name, id) =>
|
const define = (name: string, id: string) =>
|
||||||
set(state, name, createImportLazily(state, path, id, source));
|
set(state, name, createImportLazily(state, path, id, source));
|
||||||
|
|
||||||
define("id/jsx", development ? "jsxDEV" : "jsx");
|
define("id/jsx", development ? "jsxDEV" : "jsx");
|
||||||
@ -280,20 +291,23 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
path.node.value = t.jsxExpressionContainer(path.node.value);
|
path.node.value = t.jsxExpressionContainer(path.node.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
} as Visitor<PluginPass>,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Finds the closest parent function that provides `this`. Specifically, this looks for
|
// Finds the closest parent function that provides `this`. Specifically, this looks for
|
||||||
// the first parent function that isn't an arrow function.
|
// the first parent function that isn't an arrow function.
|
||||||
//
|
//
|
||||||
// Derived from `Scope#getFunctionParent`
|
// Derived from `Scope#getFunctionParent`
|
||||||
function getThisFunctionParent(path) {
|
function getThisFunctionParent(
|
||||||
|
path: NodePath,
|
||||||
|
): NodePath<Diff<FunctionParent, ArrowFunctionExpression>> | null {
|
||||||
let scope = path.scope;
|
let scope = path.scope;
|
||||||
do {
|
do {
|
||||||
if (
|
if (
|
||||||
scope.path.isFunctionParent() &&
|
scope.path.isFunctionParent() &&
|
||||||
!scope.path.isArrowFunctionExpression()
|
!scope.path.isArrowFunctionExpression()
|
||||||
) {
|
) {
|
||||||
|
// @ts-expect-error ts can not infer scope.path is Diff<FunctionParent, ArrowFunctionExpression>
|
||||||
return scope.path;
|
return scope.path;
|
||||||
}
|
}
|
||||||
} while ((scope = scope.parent));
|
} while ((scope = scope.parent));
|
||||||
@ -301,12 +315,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the class has specified a superclass.
|
// Returns whether the class has specified a superclass.
|
||||||
function isDerivedClass(classPath) {
|
function isDerivedClass(classPath: NodePath<Class>) {
|
||||||
return classPath.node.superClass !== null;
|
return classPath.node.superClass !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether `this` is allowed at given path.
|
// Returns whether `this` is allowed at given path.
|
||||||
function isThisAllowed(path) {
|
function isThisAllowed(path: NodePath<JSXOpeningElement>) {
|
||||||
// This specifically skips arrow functions as they do not rewrite `this`.
|
// This specifically skips arrow functions as they do not rewrite `this`.
|
||||||
const parentMethodOrFunction = getThisFunctionParent(path);
|
const parentMethodOrFunction = getThisFunctionParent(path);
|
||||||
if (parentMethodOrFunction === null) {
|
if (parentMethodOrFunction === null) {
|
||||||
@ -323,10 +337,16 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Now we are in a constructor. If it is a derived class, we do not reference `this`.
|
// Now we are in a constructor. If it is a derived class, we do not reference `this`.
|
||||||
return !isDerivedClass(parentMethodOrFunction.parentPath.parentPath);
|
return !isDerivedClass(
|
||||||
|
parentMethodOrFunction.parentPath.parentPath as NodePath<Class>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function call(pass, name, args) {
|
function call(
|
||||||
|
pass: PluginPass,
|
||||||
|
name: string,
|
||||||
|
args: CallExpression["arguments"],
|
||||||
|
) {
|
||||||
const node = t.callExpression(get(pass, `id/${name}`)(), args);
|
const node = t.callExpression(get(pass, `id/${name}`)(), args);
|
||||||
if (PURE_ANNOTATION ?? get(pass, "defaultPure")) annotateAsPure(node);
|
if (PURE_ANNOTATION ?? get(pass, "defaultPure")) annotateAsPure(node);
|
||||||
return node;
|
return node;
|
||||||
@ -337,7 +357,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
// from <div key={key} {...props} />. This is an intermediary
|
// from <div key={key} {...props} />. This is an intermediary
|
||||||
// step while we deprecate key spread from props. Afterwards,
|
// step while we deprecate key spread from props. Afterwards,
|
||||||
// we will stop using createElement in the transform.
|
// we will stop using createElement in the transform.
|
||||||
function shouldUseCreateElement(path) {
|
function shouldUseCreateElement(path: NodePath<JSXElement>) {
|
||||||
const openingPath = path.get("openingElement");
|
const openingPath = path.get("openingElement");
|
||||||
const attributes = openingPath.node.attributes;
|
const attributes = openingPath.node.attributes;
|
||||||
|
|
||||||
@ -391,7 +411,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function accumulateAttribute(array, attribute) {
|
function accumulateAttribute(
|
||||||
|
array: ObjectExpression["properties"],
|
||||||
|
attribute: NodePath<JSXAttribute | JSXSpreadAttribute>,
|
||||||
|
) {
|
||||||
if (t.isJSXSpreadAttribute(attribute.node)) {
|
if (t.isJSXSpreadAttribute(attribute.node)) {
|
||||||
const arg = attribute.node.argument;
|
const arg = attribute.node.argument;
|
||||||
// Collect properties into props array if spreading object expression
|
// Collect properties into props array if spreading object expression
|
||||||
@ -426,27 +449,34 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (t.isJSXNamespacedName(attribute.node.name)) {
|
if (t.isJSXNamespacedName(attribute.node.name)) {
|
||||||
|
// @ts-expect-error mutating AST
|
||||||
attribute.node.name = t.stringLiteral(
|
attribute.node.name = t.stringLiteral(
|
||||||
attribute.node.name.namespace.name +
|
attribute.node.name.namespace.name +
|
||||||
":" +
|
":" +
|
||||||
attribute.node.name.name.name,
|
attribute.node.name.name.name,
|
||||||
);
|
);
|
||||||
} else if (t.isValidIdentifier(attribute.node.name.name, false)) {
|
} else if (t.isValidIdentifier(attribute.node.name.name, false)) {
|
||||||
|
// @ts-expect-error mutating AST
|
||||||
attribute.node.name.type = "Identifier";
|
attribute.node.name.type = "Identifier";
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error mutating AST
|
||||||
attribute.node.name = t.stringLiteral(attribute.node.name.name);
|
attribute.node.name = t.stringLiteral(attribute.node.name.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
array.push(
|
array.push(
|
||||||
t.inherits(
|
t.inherits(
|
||||||
t.objectProperty(attribute.node.name, value),
|
t.objectProperty(
|
||||||
|
// @ts-expect-error The attribute.node.name is an Identifier now
|
||||||
|
attribute.node.name,
|
||||||
|
value,
|
||||||
|
),
|
||||||
attribute.node,
|
attribute.node,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildChildrenProperty(children) {
|
function buildChildrenProperty(children: Expression[]) {
|
||||||
let childrenNode;
|
let childrenNode;
|
||||||
if (children.length === 1) {
|
if (children.length === 1) {
|
||||||
childrenNode = children[0];
|
childrenNode = children[0];
|
||||||
@ -462,7 +492,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
// Builds JSX into:
|
// Builds JSX into:
|
||||||
// Production: React.jsx(type, arguments, key)
|
// Production: React.jsx(type, arguments, key)
|
||||||
// Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self)
|
// Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self)
|
||||||
function buildJSXElementCall(path, file) {
|
function buildJSXElementCall(path: NodePath<JSXElement>, file: PluginPass) {
|
||||||
const openingPath = path.get("openingElement");
|
const openingPath = path.get("openingElement");
|
||||||
const args = [getTag(openingPath)];
|
const args = [getTag(openingPath)];
|
||||||
|
|
||||||
@ -507,7 +537,8 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
if (attribsArray.length || children.length) {
|
if (attribsArray.length || children.length) {
|
||||||
attribs = buildJSXOpeningElementAttributes(
|
attribs = buildJSXOpeningElementAttributes(
|
||||||
attribsArray,
|
attribsArray,
|
||||||
file,
|
//@ts-expect-error The children here contains JSXSpreadChild,
|
||||||
|
// which will be thrown later
|
||||||
children,
|
children,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -536,7 +567,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
|
|
||||||
// Builds props for React.jsx. This function adds children into the props
|
// Builds props for React.jsx. This function adds children into the props
|
||||||
// and ensures that props is always an object
|
// and ensures that props is always an object
|
||||||
function buildJSXOpeningElementAttributes(attribs, file, children) {
|
function buildJSXOpeningElementAttributes(
|
||||||
|
attribs: NodePath<JSXAttribute | JSXSpreadAttribute>[],
|
||||||
|
children: Expression[],
|
||||||
|
) {
|
||||||
const props = attribs.reduce(accumulateAttribute, []);
|
const props = attribs.reduce(accumulateAttribute, []);
|
||||||
|
|
||||||
// In React.jsx, children is no longer a separate argument, but passed in
|
// In React.jsx, children is no longer a separate argument, but passed in
|
||||||
@ -551,14 +585,25 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
// Builds JSX Fragment <></> into
|
// Builds JSX Fragment <></> into
|
||||||
// Production: React.jsx(type, arguments)
|
// Production: React.jsx(type, arguments)
|
||||||
// Development: React.jsxDEV(type, { children })
|
// Development: React.jsxDEV(type, { children })
|
||||||
function buildJSXFragmentCall(path, file) {
|
function buildJSXFragmentCall(
|
||||||
|
path: NodePath<JSXFragment>,
|
||||||
|
file: PluginPass,
|
||||||
|
) {
|
||||||
const args = [get(file, "id/fragment")()];
|
const args = [get(file, "id/fragment")()];
|
||||||
|
|
||||||
const children = t.react.buildChildren(path.node);
|
const children = t.react.buildChildren(path.node);
|
||||||
|
|
||||||
args.push(
|
args.push(
|
||||||
t.objectExpression(
|
t.objectExpression(
|
||||||
children.length > 0 ? [buildChildrenProperty(children)] : [],
|
children.length > 0
|
||||||
|
? [
|
||||||
|
buildChildrenProperty(
|
||||||
|
//@ts-expect-error The children here contains JSXSpreadChild,
|
||||||
|
// which will be thrown later
|
||||||
|
children,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -574,7 +619,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
|
|
||||||
// Builds JSX Fragment <></> into
|
// Builds JSX Fragment <></> into
|
||||||
// React.createElement(React.Fragment, null, ...children)
|
// React.createElement(React.Fragment, null, ...children)
|
||||||
function buildCreateElementFragmentCall(path, file) {
|
function buildCreateElementFragmentCall(
|
||||||
|
path: NodePath<JSXFragment>,
|
||||||
|
file: PluginPass,
|
||||||
|
) {
|
||||||
if (filter && !filter(path.node, file)) return;
|
if (filter && !filter(path.node, file)) return;
|
||||||
|
|
||||||
return call(file, "createElement", [
|
return call(file, "createElement", [
|
||||||
@ -587,7 +635,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
// Builds JSX into:
|
// Builds JSX into:
|
||||||
// Production: React.createElement(type, arguments, children)
|
// Production: React.createElement(type, arguments, children)
|
||||||
// Development: React.createElement(type, arguments, children, source, self)
|
// Development: React.createElement(type, arguments, children, source, self)
|
||||||
function buildCreateElementCall(path, file) {
|
function buildCreateElementCall(
|
||||||
|
path: NodePath<JSXElement>,
|
||||||
|
file: PluginPass,
|
||||||
|
) {
|
||||||
const openingPath = path.get("openingElement");
|
const openingPath = path.get("openingElement");
|
||||||
|
|
||||||
return call(file, "createElement", [
|
return call(file, "createElement", [
|
||||||
@ -601,7 +652,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTag(openingPath) {
|
function getTag(openingPath: NodePath<JSXOpeningElement>) {
|
||||||
const tagExpr = convertJSXIdentifier(
|
const tagExpr = convertJSXIdentifier(
|
||||||
openingPath.node.name,
|
openingPath.node.name,
|
||||||
openingPath.node,
|
openingPath.node,
|
||||||
@ -628,7 +679,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
* breaking on spreads, we then push a new object containing
|
* breaking on spreads, we then push a new object containing
|
||||||
* all prior attributes to an array for later processing.
|
* all prior attributes to an array for later processing.
|
||||||
*/
|
*/
|
||||||
function buildCreateElementOpeningElementAttributes(file, path, attribs) {
|
function buildCreateElementOpeningElementAttributes(
|
||||||
|
file: PluginPass,
|
||||||
|
path: NodePath<JSXElement>,
|
||||||
|
attribs: NodePath<JSXAttribute | JSXSpreadAttribute>[],
|
||||||
|
) {
|
||||||
const runtime = get(file, "runtime");
|
const runtime = get(file, "runtime");
|
||||||
if (!process.env.BABEL_8_BREAKING) {
|
if (!process.env.BABEL_8_BREAKING) {
|
||||||
if (runtime !== "automatic") {
|
if (runtime !== "automatic") {
|
||||||
@ -704,7 +759,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getSource(source, importName) {
|
function getSource(source: string, importName: string) {
|
||||||
switch (importName) {
|
switch (importName) {
|
||||||
case "Fragment":
|
case "Fragment":
|
||||||
return `${source}/${development ? "jsx-dev-runtime" : "jsx-runtime"}`;
|
return `${source}/${development ? "jsx-dev-runtime" : "jsx-runtime"}`;
|
||||||
@ -718,7 +773,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createImportLazily(pass, path, importName, source) {
|
function createImportLazily(
|
||||||
|
pass: PluginPass,
|
||||||
|
path: NodePath<Program>,
|
||||||
|
importName: string,
|
||||||
|
source: string,
|
||||||
|
): () => Identifier | MemberExpression {
|
||||||
return () => {
|
return () => {
|
||||||
const actualSource = getSource(source, importName);
|
const actualSource = getSource(source, importName);
|
||||||
if (isModule(path)) {
|
if (isModule(path)) {
|
||||||
@ -749,20 +809,25 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMemberExpression(id) {
|
function toMemberExpression(id: string): Identifier | MemberExpression {
|
||||||
return id
|
return (
|
||||||
.split(".")
|
id
|
||||||
.map(name => t.identifier(name))
|
.split(".")
|
||||||
.reduce((object, property) => t.memberExpression(object, property));
|
.map(name => t.identifier(name))
|
||||||
|
// @ts-expect-error - The Array#reduce does not have a signature
|
||||||
|
// where the type of initialial value differs from callback return type
|
||||||
|
.reduce((object, property) => t.memberExpression(object, property))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSource(path, state) {
|
function makeSource(path: NodePath, state: PluginPass) {
|
||||||
const location = path.node.loc;
|
const location = path.node.loc;
|
||||||
if (!location) {
|
if (!location) {
|
||||||
// the element was generated and doesn't have location information
|
// the element was generated and doesn't have location information
|
||||||
return path.scope.buildUndefinedNode();
|
return path.scope.buildUndefinedNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error todo: avoid mutating PluginPass
|
||||||
if (!state.fileNameIdentifier) {
|
if (!state.fileNameIdentifier) {
|
||||||
const { filename = "" } = state;
|
const { filename = "" } = state;
|
||||||
|
|
||||||
@ -774,17 +839,25 @@ function makeSource(path, state) {
|
|||||||
init: t.stringLiteral(filename),
|
init: t.stringLiteral(filename),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error todo: avoid mutating PluginPass
|
||||||
state.fileNameIdentifier = fileNameIdentifier;
|
state.fileNameIdentifier = fileNameIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeTrace(
|
return makeTrace(
|
||||||
t.cloneNode(state.fileNameIdentifier),
|
t.cloneNode(
|
||||||
|
// @ts-expect-error todo: avoid mutating PluginPass
|
||||||
|
state.fileNameIdentifier,
|
||||||
|
),
|
||||||
location.start.line,
|
location.start.line,
|
||||||
location.start.column,
|
location.start.column,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTrace(fileNameIdentifier, lineNumber, column0Based) {
|
function makeTrace(
|
||||||
|
fileNameIdentifier: Identifier,
|
||||||
|
lineNumber?: number,
|
||||||
|
column0Based?: number,
|
||||||
|
) {
|
||||||
const fileLineLiteral =
|
const fileLineLiteral =
|
||||||
lineNumber != null ? t.numericLiteral(lineNumber) : t.nullLiteral();
|
lineNumber != null ? t.numericLiteral(lineNumber) : t.nullLiteral();
|
||||||
|
|
||||||
@ -810,7 +883,7 @@ function makeTrace(fileNameIdentifier, lineNumber, column0Based) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sourceSelfError(path, name) {
|
function sourceSelfError(path: NodePath, name: string) {
|
||||||
const pluginName = `transform-react-jsx-${name.slice(2)}`;
|
const pluginName = `transform-react-jsx-${name.slice(2)}`;
|
||||||
|
|
||||||
return path.buildCodeFrameError(
|
return path.buildCodeFrameError(
|
||||||
|
|||||||
@ -7,22 +7,14 @@ import cleanJSXElementLiteralChild from "../../utils/react/cleanJSXElementLitera
|
|||||||
import type * as t from "../..";
|
import type * as t from "../..";
|
||||||
|
|
||||||
type ReturnedChild =
|
type ReturnedChild =
|
||||||
| t.JSXExpressionContainer
|
|
||||||
| t.JSXSpreadChild
|
| t.JSXSpreadChild
|
||||||
| t.JSXElement
|
| t.JSXElement
|
||||||
| t.JSXFragment
|
| t.JSXFragment
|
||||||
| t.Expression;
|
| t.Expression;
|
||||||
|
|
||||||
export default function buildChildren(node: {
|
export default function buildChildren(
|
||||||
children: ReadonlyArray<
|
node: t.JSXElement | t.JSXFragment,
|
||||||
| t.JSXText
|
): ReturnedChild[] {
|
||||||
| t.JSXExpressionContainer
|
|
||||||
| t.JSXSpreadChild
|
|
||||||
| t.JSXElement
|
|
||||||
| t.JSXFragment
|
|
||||||
| t.JSXEmptyExpression
|
|
||||||
>;
|
|
||||||
}): ReturnedChild[] {
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
|
|
||||||
for (let i = 0; i < node.children.length; i++) {
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user