create-plugin.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = createPlugin;
  6. var _pluginSyntaxJsx = require("@babel/plugin-syntax-jsx");
  7. var _helperPluginUtils = require("@babel/helper-plugin-utils");
  8. var _core = require("@babel/core");
  9. var _helperModuleImports = require("@babel/helper-module-imports");
  10. var _helperAnnotateAsPure = require("@babel/helper-annotate-as-pure");
  11. const DEFAULT = {
  12. importSource: "react",
  13. runtime: "automatic",
  14. pragma: "React.createElement",
  15. pragmaFrag: "React.Fragment"
  16. };
  17. const JSX_SOURCE_ANNOTATION_REGEX = /^\s*\*?\s*@jsxImportSource\s+([^\s]+)\s*$/m;
  18. const JSX_RUNTIME_ANNOTATION_REGEX = /^\s*\*?\s*@jsxRuntime\s+([^\s]+)\s*$/m;
  19. const JSX_ANNOTATION_REGEX = /^\s*\*?\s*@jsx\s+([^\s]+)\s*$/m;
  20. const JSX_FRAG_ANNOTATION_REGEX = /^\s*\*?\s*@jsxFrag\s+([^\s]+)\s*$/m;
  21. const get = (pass, name) => pass.get(`@babel/plugin-react-jsx/${name}`);
  22. const set = (pass, name, v) => pass.set(`@babel/plugin-react-jsx/${name}`, v);
  23. function hasProto(node) {
  24. return node.properties.some(value => _core.types.isObjectProperty(value, {
  25. computed: false,
  26. shorthand: false
  27. }) && (_core.types.isIdentifier(value.key, {
  28. name: "__proto__"
  29. }) || _core.types.isStringLiteral(value.key, {
  30. value: "__proto__"
  31. })));
  32. }
  33. function createPlugin({
  34. name,
  35. development
  36. }) {
  37. return (0, _helperPluginUtils.declare)((_, options) => {
  38. const {
  39. pure: PURE_ANNOTATION,
  40. throwIfNamespace = true,
  41. filter,
  42. runtime: RUNTIME_DEFAULT = development ? "automatic" : "classic",
  43. importSource: IMPORT_SOURCE_DEFAULT = DEFAULT.importSource,
  44. pragma: PRAGMA_DEFAULT = DEFAULT.pragma,
  45. pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag
  46. } = options;
  47. {
  48. var {
  49. useSpread = false,
  50. useBuiltIns = false
  51. } = options;
  52. if (RUNTIME_DEFAULT === "classic") {
  53. if (typeof useSpread !== "boolean") {
  54. throw new Error("transform-react-jsx currently only accepts a boolean option for " + "useSpread (defaults to false)");
  55. }
  56. if (typeof useBuiltIns !== "boolean") {
  57. throw new Error("transform-react-jsx currently only accepts a boolean option for " + "useBuiltIns (defaults to false)");
  58. }
  59. if (useSpread && useBuiltIns) {
  60. throw new Error("transform-react-jsx currently only accepts useBuiltIns or useSpread " + "but not both");
  61. }
  62. }
  63. }
  64. const injectMetaPropertiesVisitor = {
  65. JSXOpeningElement(path, state) {
  66. const attributes = [];
  67. if (isThisAllowed(path.scope)) {
  68. attributes.push(_core.types.jsxAttribute(_core.types.jsxIdentifier("__self"), _core.types.jsxExpressionContainer(_core.types.thisExpression())));
  69. }
  70. attributes.push(_core.types.jsxAttribute(_core.types.jsxIdentifier("__source"), _core.types.jsxExpressionContainer(makeSource(path, state))));
  71. path.pushContainer("attributes", attributes);
  72. }
  73. };
  74. return {
  75. name,
  76. inherits: _pluginSyntaxJsx.default,
  77. visitor: {
  78. JSXNamespacedName(path) {
  79. if (throwIfNamespace) {
  80. throw path.buildCodeFrameError(`Namespace tags are not supported by default. React's JSX doesn't support namespace tags. \
  81. You can set \`throwIfNamespace: false\` to bypass this warning.`);
  82. }
  83. },
  84. JSXSpreadChild(path) {
  85. throw path.buildCodeFrameError("Spread children are not supported in React.");
  86. },
  87. Program: {
  88. enter(path, state) {
  89. const {
  90. file
  91. } = state;
  92. let runtime = RUNTIME_DEFAULT;
  93. let source = IMPORT_SOURCE_DEFAULT;
  94. let pragma = PRAGMA_DEFAULT;
  95. let pragmaFrag = PRAGMA_FRAG_DEFAULT;
  96. let sourceSet = !!options.importSource;
  97. let pragmaSet = !!options.pragma;
  98. let pragmaFragSet = !!options.pragmaFrag;
  99. if (file.ast.comments) {
  100. for (const comment of file.ast.comments) {
  101. const sourceMatches = JSX_SOURCE_ANNOTATION_REGEX.exec(comment.value);
  102. if (sourceMatches) {
  103. source = sourceMatches[1];
  104. sourceSet = true;
  105. }
  106. const runtimeMatches = JSX_RUNTIME_ANNOTATION_REGEX.exec(comment.value);
  107. if (runtimeMatches) {
  108. runtime = runtimeMatches[1];
  109. }
  110. const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
  111. if (jsxMatches) {
  112. pragma = jsxMatches[1];
  113. pragmaSet = true;
  114. }
  115. const jsxFragMatches = JSX_FRAG_ANNOTATION_REGEX.exec(comment.value);
  116. if (jsxFragMatches) {
  117. pragmaFrag = jsxFragMatches[1];
  118. pragmaFragSet = true;
  119. }
  120. }
  121. }
  122. set(state, "runtime", runtime);
  123. if (runtime === "classic") {
  124. if (sourceSet) {
  125. throw path.buildCodeFrameError(`importSource cannot be set when runtime is classic.`);
  126. }
  127. const createElement = toMemberExpression(pragma);
  128. const fragment = toMemberExpression(pragmaFrag);
  129. set(state, "id/createElement", () => _core.types.cloneNode(createElement));
  130. set(state, "id/fragment", () => _core.types.cloneNode(fragment));
  131. set(state, "defaultPure", pragma === DEFAULT.pragma);
  132. } else if (runtime === "automatic") {
  133. if (pragmaSet || pragmaFragSet) {
  134. throw path.buildCodeFrameError(`pragma and pragmaFrag cannot be set when runtime is automatic.`);
  135. }
  136. const define = (name, id) => set(state, name, createImportLazily(state, path, id, source));
  137. define("id/jsx", development ? "jsxDEV" : "jsx");
  138. define("id/jsxs", development ? "jsxDEV" : "jsxs");
  139. define("id/createElement", "createElement");
  140. define("id/fragment", "Fragment");
  141. set(state, "defaultPure", source === DEFAULT.importSource);
  142. } else {
  143. throw path.buildCodeFrameError(`Runtime must be either "classic" or "automatic".`);
  144. }
  145. if (development) {
  146. path.traverse(injectMetaPropertiesVisitor, state);
  147. }
  148. }
  149. },
  150. JSXFragment: {
  151. exit(path, file) {
  152. let callExpr;
  153. if (get(file, "runtime") === "classic") {
  154. callExpr = buildCreateElementFragmentCall(path, file);
  155. } else {
  156. callExpr = buildJSXFragmentCall(path, file);
  157. }
  158. path.replaceWith(_core.types.inherits(callExpr, path.node));
  159. }
  160. },
  161. JSXElement: {
  162. exit(path, file) {
  163. let callExpr;
  164. if (get(file, "runtime") === "classic" || shouldUseCreateElement(path)) {
  165. callExpr = buildCreateElementCall(path, file);
  166. } else {
  167. callExpr = buildJSXElementCall(path, file);
  168. }
  169. path.replaceWith(_core.types.inherits(callExpr, path.node));
  170. }
  171. },
  172. JSXAttribute(path) {
  173. if (_core.types.isJSXElement(path.node.value)) {
  174. path.node.value = _core.types.jsxExpressionContainer(path.node.value);
  175. }
  176. }
  177. }
  178. };
  179. function isDerivedClass(classPath) {
  180. return classPath.node.superClass !== null;
  181. }
  182. function isThisAllowed(scope) {
  183. do {
  184. const {
  185. path
  186. } = scope;
  187. if (path.isFunctionParent() && !path.isArrowFunctionExpression()) {
  188. if (!path.isMethod()) {
  189. return true;
  190. }
  191. if (path.node.kind !== "constructor") {
  192. return true;
  193. }
  194. return !isDerivedClass(path.parentPath.parentPath);
  195. }
  196. if (path.isTSModuleBlock()) {
  197. return false;
  198. }
  199. } while (scope = scope.parent);
  200. return true;
  201. }
  202. function call(pass, name, args) {
  203. const node = _core.types.callExpression(get(pass, `id/${name}`)(), args);
  204. if (PURE_ANNOTATION != null ? PURE_ANNOTATION : get(pass, "defaultPure")) (0, _helperAnnotateAsPure.default)(node);
  205. return node;
  206. }
  207. function shouldUseCreateElement(path) {
  208. const openingPath = path.get("openingElement");
  209. const attributes = openingPath.node.attributes;
  210. let seenPropsSpread = false;
  211. for (let i = 0; i < attributes.length; i++) {
  212. const attr = attributes[i];
  213. if (seenPropsSpread && _core.types.isJSXAttribute(attr) && attr.name.name === "key") {
  214. return true;
  215. } else if (_core.types.isJSXSpreadAttribute(attr)) {
  216. seenPropsSpread = true;
  217. }
  218. }
  219. return false;
  220. }
  221. function convertJSXIdentifier(node, parent) {
  222. if (_core.types.isJSXIdentifier(node)) {
  223. if (node.name === "this" && _core.types.isReferenced(node, parent)) {
  224. return _core.types.thisExpression();
  225. } else if (_core.types.isValidIdentifier(node.name, false)) {
  226. node.type = "Identifier";
  227. return node;
  228. } else {
  229. return _core.types.stringLiteral(node.name);
  230. }
  231. } else if (_core.types.isJSXMemberExpression(node)) {
  232. return _core.types.memberExpression(convertJSXIdentifier(node.object, node), convertJSXIdentifier(node.property, node));
  233. } else if (_core.types.isJSXNamespacedName(node)) {
  234. return _core.types.stringLiteral(`${node.namespace.name}:${node.name.name}`);
  235. }
  236. return node;
  237. }
  238. function convertAttributeValue(node) {
  239. if (_core.types.isJSXExpressionContainer(node)) {
  240. return node.expression;
  241. } else {
  242. return node;
  243. }
  244. }
  245. function accumulateAttribute(array, attribute) {
  246. if (_core.types.isJSXSpreadAttribute(attribute.node)) {
  247. const arg = attribute.node.argument;
  248. if (_core.types.isObjectExpression(arg) && !hasProto(arg)) {
  249. array.push(...arg.properties);
  250. } else {
  251. array.push(_core.types.spreadElement(arg));
  252. }
  253. return array;
  254. }
  255. const value = convertAttributeValue(attribute.node.name.name !== "key" ? attribute.node.value || _core.types.booleanLiteral(true) : attribute.node.value);
  256. if (attribute.node.name.name === "key" && value === null) {
  257. throw attribute.buildCodeFrameError('Please provide an explicit key value. Using "key" as a shorthand for "key={true}" is not allowed.');
  258. }
  259. if (_core.types.isStringLiteral(value) && !_core.types.isJSXExpressionContainer(attribute.node.value)) {
  260. var _value$extra;
  261. value.value = value.value.replace(/\n\s+/g, " ");
  262. (_value$extra = value.extra) == null || delete _value$extra.raw;
  263. }
  264. if (_core.types.isJSXNamespacedName(attribute.node.name)) {
  265. attribute.node.name = _core.types.stringLiteral(attribute.node.name.namespace.name + ":" + attribute.node.name.name.name);
  266. } else if (_core.types.isValidIdentifier(attribute.node.name.name, false)) {
  267. attribute.node.name.type = "Identifier";
  268. } else {
  269. attribute.node.name = _core.types.stringLiteral(attribute.node.name.name);
  270. }
  271. array.push(_core.types.inherits(_core.types.objectProperty(attribute.node.name, value), attribute.node));
  272. return array;
  273. }
  274. function buildChildrenProperty(children) {
  275. let childrenNode;
  276. if (children.length === 1) {
  277. childrenNode = children[0];
  278. } else if (children.length > 1) {
  279. childrenNode = _core.types.arrayExpression(children);
  280. } else {
  281. return undefined;
  282. }
  283. return _core.types.objectProperty(_core.types.identifier("children"), childrenNode);
  284. }
  285. function buildJSXElementCall(path, file) {
  286. const openingPath = path.get("openingElement");
  287. const args = [getTag(openingPath)];
  288. const attribsArray = [];
  289. const extracted = Object.create(null);
  290. for (const attr of openingPath.get("attributes")) {
  291. if (attr.isJSXAttribute() && _core.types.isJSXIdentifier(attr.node.name)) {
  292. const {
  293. name
  294. } = attr.node.name;
  295. switch (name) {
  296. case "__source":
  297. case "__self":
  298. if (extracted[name]) throw sourceSelfError(path, name);
  299. case "key":
  300. {
  301. const keyValue = convertAttributeValue(attr.node.value);
  302. if (keyValue === null) {
  303. throw attr.buildCodeFrameError('Please provide an explicit key value. Using "key" as a shorthand for "key={true}" is not allowed.');
  304. }
  305. extracted[name] = keyValue;
  306. break;
  307. }
  308. default:
  309. attribsArray.push(attr);
  310. }
  311. } else {
  312. attribsArray.push(attr);
  313. }
  314. }
  315. const children = _core.types.react.buildChildren(path.node);
  316. let attribs;
  317. if (attribsArray.length || children.length) {
  318. attribs = buildJSXOpeningElementAttributes(attribsArray, children);
  319. } else {
  320. attribs = _core.types.objectExpression([]);
  321. }
  322. args.push(attribs);
  323. if (development) {
  324. var _extracted$key;
  325. args.push((_extracted$key = extracted.key) != null ? _extracted$key : path.scope.buildUndefinedNode(), _core.types.booleanLiteral(children.length > 1));
  326. if (extracted.__source) {
  327. args.push(extracted.__source);
  328. if (extracted.__self) args.push(extracted.__self);
  329. } else if (extracted.__self) {
  330. args.push(path.scope.buildUndefinedNode(), extracted.__self);
  331. }
  332. } else if (extracted.key !== undefined) {
  333. args.push(extracted.key);
  334. }
  335. return call(file, children.length > 1 ? "jsxs" : "jsx", args);
  336. }
  337. function buildJSXOpeningElementAttributes(attribs, children) {
  338. const props = attribs.reduce(accumulateAttribute, []);
  339. if ((children == null ? void 0 : children.length) > 0) {
  340. props.push(buildChildrenProperty(children));
  341. }
  342. return _core.types.objectExpression(props);
  343. }
  344. function buildJSXFragmentCall(path, file) {
  345. const args = [get(file, "id/fragment")()];
  346. const children = _core.types.react.buildChildren(path.node);
  347. args.push(_core.types.objectExpression(children.length > 0 ? [buildChildrenProperty(children)] : []));
  348. if (development) {
  349. args.push(path.scope.buildUndefinedNode(), _core.types.booleanLiteral(children.length > 1));
  350. }
  351. return call(file, children.length > 1 ? "jsxs" : "jsx", args);
  352. }
  353. function buildCreateElementFragmentCall(path, file) {
  354. if (filter && !filter(path.node, file)) return;
  355. return call(file, "createElement", [get(file, "id/fragment")(), _core.types.nullLiteral(), ..._core.types.react.buildChildren(path.node)]);
  356. }
  357. function buildCreateElementCall(path, file) {
  358. const openingPath = path.get("openingElement");
  359. return call(file, "createElement", [getTag(openingPath), buildCreateElementOpeningElementAttributes(file, path, openingPath.get("attributes")), ..._core.types.react.buildChildren(path.node)]);
  360. }
  361. function getTag(openingPath) {
  362. const tagExpr = convertJSXIdentifier(openingPath.node.name, openingPath.node);
  363. let tagName;
  364. if (_core.types.isIdentifier(tagExpr)) {
  365. tagName = tagExpr.name;
  366. } else if (_core.types.isStringLiteral(tagExpr)) {
  367. tagName = tagExpr.value;
  368. }
  369. if (_core.types.react.isCompatTag(tagName)) {
  370. return _core.types.stringLiteral(tagName);
  371. } else {
  372. return tagExpr;
  373. }
  374. }
  375. function buildCreateElementOpeningElementAttributes(file, path, attribs) {
  376. const runtime = get(file, "runtime");
  377. {
  378. if (runtime !== "automatic") {
  379. const objs = [];
  380. const props = attribs.reduce(accumulateAttribute, []);
  381. if (!useSpread) {
  382. let start = 0;
  383. props.forEach((prop, i) => {
  384. if (_core.types.isSpreadElement(prop)) {
  385. if (i > start) {
  386. objs.push(_core.types.objectExpression(props.slice(start, i)));
  387. }
  388. objs.push(prop.argument);
  389. start = i + 1;
  390. }
  391. });
  392. if (props.length > start) {
  393. objs.push(_core.types.objectExpression(props.slice(start)));
  394. }
  395. } else if (props.length) {
  396. objs.push(_core.types.objectExpression(props));
  397. }
  398. if (!objs.length) {
  399. return _core.types.nullLiteral();
  400. }
  401. if (objs.length === 1) {
  402. if (!(_core.types.isSpreadElement(props[0]) && _core.types.isObjectExpression(props[0].argument))) {
  403. return objs[0];
  404. }
  405. }
  406. if (!_core.types.isObjectExpression(objs[0])) {
  407. objs.unshift(_core.types.objectExpression([]));
  408. }
  409. const helper = useBuiltIns ? _core.types.memberExpression(_core.types.identifier("Object"), _core.types.identifier("assign")) : file.addHelper("extends");
  410. return _core.types.callExpression(helper, objs);
  411. }
  412. }
  413. const props = [];
  414. const found = Object.create(null);
  415. for (const attr of attribs) {
  416. const {
  417. node
  418. } = attr;
  419. const name = _core.types.isJSXAttribute(node) && _core.types.isJSXIdentifier(node.name) && node.name.name;
  420. if (runtime === "automatic" && (name === "__source" || name === "__self")) {
  421. if (found[name]) throw sourceSelfError(path, name);
  422. found[name] = true;
  423. }
  424. accumulateAttribute(props, attr);
  425. }
  426. return props.length === 1 && _core.types.isSpreadElement(props[0]) && !_core.types.isObjectExpression(props[0].argument) ? props[0].argument : props.length > 0 ? _core.types.objectExpression(props) : _core.types.nullLiteral();
  427. }
  428. });
  429. function getSource(source, importName) {
  430. switch (importName) {
  431. case "Fragment":
  432. return `${source}/${development ? "jsx-dev-runtime" : "jsx-runtime"}`;
  433. case "jsxDEV":
  434. return `${source}/jsx-dev-runtime`;
  435. case "jsx":
  436. case "jsxs":
  437. return `${source}/jsx-runtime`;
  438. case "createElement":
  439. return source;
  440. }
  441. }
  442. function createImportLazily(pass, path, importName, source) {
  443. return () => {
  444. const actualSource = getSource(source, importName);
  445. if ((0, _helperModuleImports.isModule)(path)) {
  446. let reference = get(pass, `imports/${importName}`);
  447. if (reference) return _core.types.cloneNode(reference);
  448. reference = (0, _helperModuleImports.addNamed)(path, importName, actualSource, {
  449. importedInterop: "uncompiled",
  450. importPosition: "after"
  451. });
  452. set(pass, `imports/${importName}`, reference);
  453. return reference;
  454. } else {
  455. let reference = get(pass, `requires/${actualSource}`);
  456. if (reference) {
  457. reference = _core.types.cloneNode(reference);
  458. } else {
  459. reference = (0, _helperModuleImports.addNamespace)(path, actualSource, {
  460. importedInterop: "uncompiled"
  461. });
  462. set(pass, `requires/${actualSource}`, reference);
  463. }
  464. return _core.types.memberExpression(reference, _core.types.identifier(importName));
  465. }
  466. };
  467. }
  468. }
  469. function toMemberExpression(id) {
  470. return id.split(".").map(name => _core.types.identifier(name)).reduce((object, property) => _core.types.memberExpression(object, property));
  471. }
  472. function makeSource(path, state) {
  473. const location = path.node.loc;
  474. if (!location) {
  475. return path.scope.buildUndefinedNode();
  476. }
  477. if (!state.fileNameIdentifier) {
  478. const {
  479. filename = ""
  480. } = state;
  481. const fileNameIdentifier = path.scope.generateUidIdentifier("_jsxFileName");
  482. path.scope.getProgramParent().push({
  483. id: fileNameIdentifier,
  484. init: _core.types.stringLiteral(filename)
  485. });
  486. state.fileNameIdentifier = fileNameIdentifier;
  487. }
  488. return makeTrace(_core.types.cloneNode(state.fileNameIdentifier), location.start.line, location.start.column);
  489. }
  490. function makeTrace(fileNameIdentifier, lineNumber, column0Based) {
  491. const fileLineLiteral = lineNumber != null ? _core.types.numericLiteral(lineNumber) : _core.types.nullLiteral();
  492. const fileColumnLiteral = column0Based != null ? _core.types.numericLiteral(column0Based + 1) : _core.types.nullLiteral();
  493. return _core.template.expression.ast`{
  494. fileName: ${fileNameIdentifier},
  495. lineNumber: ${fileLineLiteral},
  496. columnNumber: ${fileColumnLiteral},
  497. }`;
  498. }
  499. function sourceSelfError(path, name) {
  500. const pluginName = `transform-react-jsx-${name.slice(2)}`;
  501. 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.`);
  502. }
  503. //# sourceMappingURL=create-plugin.js.map