800 lines
22 KiB
JavaScript
800 lines
22 KiB
JavaScript
// MIT License
|
|
|
|
// Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
// © 2019 GitHub, Inc.
|
|
'use strict';
|
|
|
|
const esutils = require('esutils');
|
|
const {
|
|
isModule,
|
|
addNamespace,
|
|
addNamed,
|
|
addDefault,
|
|
} = require('@babel/helper-module-imports');
|
|
|
|
// These are all the valid auto import types (under the config autoImport)
|
|
// that a user can specific
|
|
const IMPORT_TYPES = {
|
|
none: 'none', // default option. Will not import anything
|
|
require: 'require', // var _react = require("react");
|
|
namespace: 'namespace', // import * as _react from "react";
|
|
defaultExport: 'defaultExport', // import _default from "react";
|
|
namedExports: 'namedExports', // import { jsx } from "react";
|
|
};
|
|
|
|
const JSX_AUTO_IMPORT_ANNOTATION_REGEX = /\*?\s*@jsxAutoImport\s+([^\s]+)/;
|
|
const JSX_IMPORT_SOURCE_ANNOTATION_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/;
|
|
|
|
// 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 remove createElement entirely
|
|
function shouldUseCreateElement(path, types) {
|
|
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 &&
|
|
types.isJSXAttribute(attr) &&
|
|
attr.name.name === 'key'
|
|
) {
|
|
return true;
|
|
} else if (types.isJSXSpreadAttribute(attr)) {
|
|
seenPropsSpread = true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function helper(babel, opts) {
|
|
const {types: t} = babel;
|
|
|
|
const visitor = {};
|
|
|
|
visitor.JSXNamespacedName = function(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 turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
|
);
|
|
}
|
|
};
|
|
|
|
visitor.JSXSpreadChild = function(path) {
|
|
throw path.buildCodeFrameError(
|
|
'Spread children are not supported in React.',
|
|
);
|
|
};
|
|
|
|
visitor.JSXElement = {
|
|
exit(path, file) {
|
|
let callExpr;
|
|
if (shouldUseCreateElement(path, t)) {
|
|
callExpr = buildCreateElementCall(path, file);
|
|
} else {
|
|
callExpr = buildJSXElementCall(path, file);
|
|
}
|
|
|
|
if (callExpr) {
|
|
path.replaceWith(t.inherits(callExpr, path.node));
|
|
}
|
|
},
|
|
};
|
|
|
|
visitor.JSXFragment = {
|
|
exit(path, file) {
|
|
if (opts.compat) {
|
|
throw path.buildCodeFrameError(
|
|
'Fragment tags are only supported in React 16 and up.',
|
|
);
|
|
}
|
|
let callExpr = buildJSXFragmentCall(path, file);
|
|
|
|
if (callExpr) {
|
|
path.replaceWith(t.inherits(callExpr, path.node));
|
|
}
|
|
},
|
|
};
|
|
|
|
return visitor;
|
|
|
|
function convertJSXIdentifier(node, parent) {
|
|
if (t.isJSXIdentifier(node)) {
|
|
if (node.name === 'this' && t.isReferenced(node, parent)) {
|
|
return t.thisExpression();
|
|
} else if (esutils.keyword.isIdentifierNameES6(node.name)) {
|
|
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 there is flag "throwIfNamespace"
|
|
* 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, duplicateChildren) {
|
|
let value = convertAttributeValue(node.value || t.booleanLiteral(true));
|
|
|
|
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 (duplicateChildren && duplicateChildren.length > 0) {
|
|
value = t.sequenceExpression([...duplicateChildren, value]);
|
|
}
|
|
|
|
if (t.isJSXNamespacedName(node.name)) {
|
|
node.name = t.stringLiteral(
|
|
node.name.namespace.name + ':' + node.name.name.name,
|
|
);
|
|
} else if (esutils.keyword.isIdentifierNameES6(node.name.name)) {
|
|
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) {
|
|
if (opts.filter && !opts.filter(path.node, file)) {
|
|
return;
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
if (opts.pre) {
|
|
opts.pre(state, file);
|
|
}
|
|
|
|
let attribs = [];
|
|
let key;
|
|
let source;
|
|
let self;
|
|
|
|
// 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 (let i = 0; i < openingPath.node.attributes.length; i++) {
|
|
const attr = openingPath.node.attributes[i];
|
|
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
|
|
if (attr.name.name === 'key') {
|
|
key = convertAttribute(attr).value;
|
|
} else if (attr.name.name === '__source') {
|
|
source = convertAttribute(attr).value;
|
|
} else if (attr.name.name === '__self') {
|
|
self = convertAttribute(attr).value;
|
|
} else {
|
|
attribs.push(attr);
|
|
}
|
|
} else {
|
|
attribs.push(attr);
|
|
}
|
|
}
|
|
|
|
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 (!file.opts.development) {
|
|
if (key !== undefined) {
|
|
args.push(key);
|
|
}
|
|
} else {
|
|
// isStaticChildren, __source, and __self are only used in development
|
|
args.push(
|
|
key === undefined ? t.identifier('undefined') : key,
|
|
t.booleanLiteral(path.node.children.length > 1),
|
|
source === undefined ? t.identifier('undefined') : source,
|
|
self === undefined ? t.identifier('undefined') : self,
|
|
);
|
|
}
|
|
|
|
if (opts.post) {
|
|
opts.post(state, file);
|
|
}
|
|
|
|
return (
|
|
state.call ||
|
|
t.callExpression(
|
|
path.node.children.length > 1 ? state.staticCallee : state.callee,
|
|
args,
|
|
)
|
|
);
|
|
}
|
|
|
|
function isChildrenProp(prop) {
|
|
return (
|
|
t.isJSXAttribute(prop) &&
|
|
t.isJSXIdentifier(prop.name) &&
|
|
prop.name.name === 'children'
|
|
);
|
|
}
|
|
|
|
// 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) {
|
|
let _props = [];
|
|
const objs = [];
|
|
|
|
// In order to avoid having duplicate "children" keys, we avoid
|
|
// pushing the "children" prop if we have actual children. However,
|
|
// the children prop may have side effects, so to be certain
|
|
// these side effects are evaluated, we add them to the following prop
|
|
// as a sequence expression to preserve order. So:
|
|
// <div children={x++} foo={y}>{child}</div> becomes
|
|
// React.jsx('div', {foo: (x++, y), children: child});
|
|
// duplicateChildren contains the extra children prop values
|
|
let duplicateChildren = [];
|
|
|
|
const hasChildren = children && children.length > 0;
|
|
|
|
const useBuiltIns = file.opts.useBuiltIns || false;
|
|
if (typeof useBuiltIns !== 'boolean') {
|
|
throw new Error(
|
|
'transform-react-jsx currently only accepts a boolean option for ' +
|
|
'useBuiltIns (defaults to false)',
|
|
);
|
|
}
|
|
|
|
while (attribs.length) {
|
|
const prop = attribs.shift();
|
|
if (hasChildren && isChildrenProp(prop)) {
|
|
duplicateChildren.push(convertAttributeValue(prop.value));
|
|
} else if (t.isJSXSpreadAttribute(prop)) {
|
|
_props = pushProps(_props, objs);
|
|
if (duplicateChildren.length > 0) {
|
|
objs.push(
|
|
t.sequenceExpression([...duplicateChildren, prop.argument]),
|
|
);
|
|
duplicateChildren = [];
|
|
} else {
|
|
objs.push(prop.argument);
|
|
}
|
|
} else {
|
|
_props.push(convertAttribute(prop, duplicateChildren));
|
|
if (duplicateChildren.length > 0) {
|
|
duplicateChildren = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
// In React.JSX, children is no longer a separate argument, but passed in
|
|
// through the argument object
|
|
if (hasChildren) {
|
|
if (children.length === 1) {
|
|
_props.push(
|
|
t.objectProperty(
|
|
t.identifier('children'),
|
|
duplicateChildren.length > 0
|
|
? t.sequenceExpression([...duplicateChildren, children[0]])
|
|
: children[0],
|
|
),
|
|
);
|
|
} else {
|
|
_props.push(
|
|
t.objectProperty(
|
|
t.identifier('children'),
|
|
duplicateChildren.length > 0
|
|
? t.sequenceExpression([
|
|
...duplicateChildren,
|
|
t.arrayExpression(children),
|
|
])
|
|
: t.arrayExpression(children),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
pushProps(_props, objs);
|
|
|
|
if (objs.length === 1) {
|
|
// only one object
|
|
if (!t.isObjectExpression(objs[0])) {
|
|
// if the prop object isn't an object, use Object.assign or _extends
|
|
// to ensure that the prop will always be an object (as opposed to a variable
|
|
// that could be null at some point)
|
|
const expressionHelper = useBuiltIns
|
|
? t.memberExpression(t.identifier('Object'), t.identifier('assign'))
|
|
: file.addHelper('extends');
|
|
|
|
attribs = t.callExpression(expressionHelper, [
|
|
t.objectExpression([]),
|
|
objs[0],
|
|
]);
|
|
} else {
|
|
attribs = objs[0];
|
|
}
|
|
} else {
|
|
// looks like we have multiple objects
|
|
if (!t.isObjectExpression(objs[0])) {
|
|
objs.unshift(t.objectExpression([]));
|
|
}
|
|
|
|
const expressionHelper = useBuiltIns
|
|
? t.memberExpression(t.identifier('Object'), t.identifier('assign'))
|
|
: file.addHelper('extends');
|
|
|
|
// spread it
|
|
attribs = t.callExpression(expressionHelper, objs);
|
|
}
|
|
|
|
return attribs;
|
|
}
|
|
|
|
// Builds JSX Fragment <></> into
|
|
// Production: React.jsx(type, arguments)
|
|
// Development: React.jsxDEV(type, { children})
|
|
function buildJSXFragmentCall(path, file) {
|
|
if (opts.filter && !opts.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('jsxFragIdentifier')();
|
|
|
|
const state = {
|
|
tagExpr: tagExpr,
|
|
tagName: tagName,
|
|
args: args,
|
|
};
|
|
|
|
if (opts.pre) {
|
|
opts.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 (file.opts.development) {
|
|
args.push(
|
|
t.identifier('undefined'),
|
|
t.booleanLiteral(path.node.children.length > 1),
|
|
);
|
|
}
|
|
|
|
if (opts.post) {
|
|
opts.post(state, file);
|
|
}
|
|
|
|
return (
|
|
state.call ||
|
|
t.callExpression(
|
|
path.node.children.length > 1 ? state.staticCallee : state.callee,
|
|
args,
|
|
)
|
|
);
|
|
}
|
|
|
|
// Builds JSX into:
|
|
// Production: React.createElement(type, arguments, children)
|
|
// Development: React.createElement(type, arguments, children, source, self)
|
|
function buildCreateElementCall(path, file) {
|
|
if (opts.filter && !opts.filter(path.node, file)) {
|
|
return;
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
if (opts.pre) {
|
|
opts.pre(state, file);
|
|
}
|
|
|
|
let attribs = openingPath.node.attributes;
|
|
if (attribs.length) {
|
|
attribs = buildCreateElementOpeningElementAttributes(attribs, file);
|
|
} else {
|
|
attribs = t.nullLiteral();
|
|
}
|
|
|
|
args.push(attribs, ...path.node.children);
|
|
|
|
if (opts.post) {
|
|
opts.post(state, file);
|
|
}
|
|
|
|
return state.call || t.callExpression(state.oldCallee, args);
|
|
}
|
|
|
|
function pushProps(_props, objs) {
|
|
if (!_props.length) {
|
|
return _props;
|
|
}
|
|
|
|
objs.push(t.objectExpression(_props));
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* 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(attribs, file) {
|
|
let _props = [];
|
|
const objs = [];
|
|
|
|
const useBuiltIns = file.opts.useBuiltIns || false;
|
|
if (typeof useBuiltIns !== 'boolean') {
|
|
throw new Error(
|
|
'transform-react-jsx currently only accepts a boolean option for ' +
|
|
'useBuiltIns (defaults to false)',
|
|
);
|
|
}
|
|
|
|
while (attribs.length) {
|
|
const prop = attribs.shift();
|
|
if (t.isJSXSpreadAttribute(prop)) {
|
|
_props = pushProps(_props, objs);
|
|
objs.push(prop.argument);
|
|
} else {
|
|
const attr = convertAttribute(prop);
|
|
_props.push(attr);
|
|
}
|
|
}
|
|
|
|
pushProps(_props, objs);
|
|
|
|
if (objs.length === 1) {
|
|
// only one object
|
|
attribs = objs[0];
|
|
} else {
|
|
// looks like we have multiple objects
|
|
if (!t.isObjectExpression(objs[0])) {
|
|
objs.unshift(t.objectExpression([]));
|
|
}
|
|
|
|
const expressionHelper = useBuiltIns
|
|
? t.memberExpression(t.identifier('Object'), t.identifier('assign'))
|
|
: file.addHelper('extends');
|
|
|
|
// spread it
|
|
attribs = t.callExpression(expressionHelper, objs);
|
|
}
|
|
|
|
return attribs;
|
|
}
|
|
}
|
|
|
|
module.exports = function(babel) {
|
|
const {types: t} = babel;
|
|
|
|
const createIdentifierParser = id => () => {
|
|
return id
|
|
.split('.')
|
|
.map(name => t.identifier(name))
|
|
.reduce((object, property) => t.memberExpression(object, property));
|
|
};
|
|
|
|
const visitor = helper(babel, {
|
|
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) {
|
|
state.callee = pass.get('jsxIdentifier')();
|
|
state.staticCallee = pass.get('jsxStaticIdentifier')();
|
|
state.oldCallee = pass.get('oldJSXIdentifier')();
|
|
},
|
|
});
|
|
|
|
const createIdentifierName = (path, autoImport, name, importName) => {
|
|
if (autoImport === IMPORT_TYPES.none) {
|
|
return `React.${name}`;
|
|
} else if (autoImport === IMPORT_TYPES.namedExports) {
|
|
if (importName) {
|
|
const identifierName = `${importName[name]}`;
|
|
return identifierName;
|
|
}
|
|
} else {
|
|
return `${importName}.${name}`;
|
|
}
|
|
};
|
|
|
|
function getImportNames(parentPath, state) {
|
|
const imports = {};
|
|
parentPath.traverse({
|
|
JSXElement(path) {
|
|
if (shouldUseCreateElement(path, t)) {
|
|
imports.createElement = true;
|
|
} else if (path.node.children.length > 1) {
|
|
const importName = state.development ? 'jsxDEV' : 'jsxs';
|
|
imports[importName] = true;
|
|
} else {
|
|
const importName = state.development ? 'jsxDEV' : 'jsx';
|
|
imports[importName] = true;
|
|
}
|
|
},
|
|
|
|
JSXFragment(path) {
|
|
imports.Fragment = true;
|
|
},
|
|
});
|
|
return imports;
|
|
}
|
|
|
|
function hasJSX(parentPath) {
|
|
let fileHasJSX = false;
|
|
parentPath.traverse({
|
|
JSXElement(path) {
|
|
fileHasJSX = true;
|
|
path.stop();
|
|
},
|
|
|
|
JSXFragment(path) {
|
|
fileHasJSX = true;
|
|
path.stop();
|
|
},
|
|
});
|
|
|
|
return fileHasJSX;
|
|
}
|
|
|
|
function addAutoImports(path, state) {
|
|
if (state.autoImport === IMPORT_TYPES.none) {
|
|
return;
|
|
}
|
|
|
|
if (IMPORT_TYPES[state.autoImport] === undefined) {
|
|
throw path.buildCodeFrameError(
|
|
'autoImport must be one of the following: ' +
|
|
Object.keys(IMPORT_TYPES).join(', '),
|
|
);
|
|
}
|
|
if (state.autoImport === IMPORT_TYPES.require && isModule(path)) {
|
|
throw path.buildCodeFrameError(
|
|
'Babel `sourceType` must be set to `script` for autoImport ' +
|
|
'to use `require` syntax. See Babel `sourceType` for details.',
|
|
);
|
|
}
|
|
if (state.autoImport !== IMPORT_TYPES.require && !isModule(path)) {
|
|
throw path.buildCodeFrameError(
|
|
'Babel `sourceType` must be set to `module` for autoImport to use `' +
|
|
state.autoImport +
|
|
'` syntax. See Babel `sourceType` for details.',
|
|
);
|
|
}
|
|
|
|
// import {jsx} from "react";
|
|
// import {createElement} from "react";
|
|
if (state.autoImport === IMPORT_TYPES.namedExports) {
|
|
const imports = getImportNames(path, state);
|
|
const importMap = {};
|
|
|
|
Object.keys(imports).forEach(importName => {
|
|
importMap[importName] = addNamed(path, importName, state.source).name;
|
|
});
|
|
|
|
return importMap;
|
|
}
|
|
|
|
// add import to file and get the import name
|
|
let name;
|
|
if (state.autoImport === IMPORT_TYPES.require) {
|
|
// var _react = require("react");
|
|
name = addNamespace(path, state.source, {
|
|
importedInterop: 'uncompiled',
|
|
}).name;
|
|
} else if (state.autoImport === IMPORT_TYPES.namespace) {
|
|
// import * as _react from "react";
|
|
name = addNamespace(path, state.source).name;
|
|
} else if (state.autoImport === IMPORT_TYPES.defaultExport) {
|
|
// import _default from "react";
|
|
name = addDefault(path, state.source).name;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
visitor.Program = {
|
|
enter(path, state) {
|
|
if (hasJSX(path)) {
|
|
let autoImport = state.opts.autoImport || IMPORT_TYPES.none;
|
|
let source = state.opts.importSource || 'react';
|
|
const {file} = state;
|
|
|
|
if (file.ast.comments) {
|
|
for (let i = 0; i < file.ast.comments.length; i++) {
|
|
const comment = file.ast.comments[i];
|
|
const jsxAutoImportMatches = JSX_AUTO_IMPORT_ANNOTATION_REGEX.exec(
|
|
comment.value,
|
|
);
|
|
if (jsxAutoImportMatches) {
|
|
autoImport = jsxAutoImportMatches[1];
|
|
}
|
|
const jsxImportSourceMatches = JSX_IMPORT_SOURCE_ANNOTATION_REGEX.exec(
|
|
comment.value,
|
|
);
|
|
if (jsxImportSourceMatches) {
|
|
source = jsxImportSourceMatches[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
const importName = addAutoImports(path, {
|
|
...state.opts,
|
|
autoImport,
|
|
source,
|
|
});
|
|
|
|
state.set(
|
|
'oldJSXIdentifier',
|
|
createIdentifierParser(
|
|
createIdentifierName(path, autoImport, 'createElement', importName),
|
|
),
|
|
);
|
|
|
|
state.set(
|
|
'jsxIdentifier',
|
|
createIdentifierParser(
|
|
createIdentifierName(
|
|
path,
|
|
autoImport,
|
|
state.opts.development ? 'jsxDEV' : 'jsx',
|
|
importName,
|
|
),
|
|
),
|
|
);
|
|
|
|
state.set(
|
|
'jsxStaticIdentifier',
|
|
createIdentifierParser(
|
|
createIdentifierName(
|
|
path,
|
|
autoImport,
|
|
state.opts.development ? 'jsxDEV' : 'jsxs',
|
|
importName,
|
|
),
|
|
),
|
|
);
|
|
|
|
state.set(
|
|
'jsxFragIdentifier',
|
|
createIdentifierParser(
|
|
createIdentifierName(path, autoImport, 'Fragment', importName),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
visitor.JSXAttribute = function(path) {
|
|
if (t.isJSXElement(path.node.value)) {
|
|
path.node.value = t.jsxExpressionContainer(path.node.value);
|
|
}
|
|
};
|
|
|
|
return {
|
|
name: 'transform-react-jsx',
|
|
visitor,
|
|
};
|
|
};
|