CJSImportTransformer.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  2. var _tokenizer = require('../parser/tokenizer');
  3. var _keywords = require('../parser/tokenizer/keywords');
  4. var _types = require('../parser/tokenizer/types');
  5. var _elideImportEquals = require('../util/elideImportEquals'); var _elideImportEquals2 = _interopRequireDefault(_elideImportEquals);
  6. var _getDeclarationInfo = require('../util/getDeclarationInfo'); var _getDeclarationInfo2 = _interopRequireDefault(_getDeclarationInfo);
  7. var _getImportExportSpecifierInfo = require('../util/getImportExportSpecifierInfo'); var _getImportExportSpecifierInfo2 = _interopRequireDefault(_getImportExportSpecifierInfo);
  8. var _isExportFrom = require('../util/isExportFrom'); var _isExportFrom2 = _interopRequireDefault(_isExportFrom);
  9. var _removeMaybeImportAttributes = require('../util/removeMaybeImportAttributes');
  10. var _shouldElideDefaultExport = require('../util/shouldElideDefaultExport'); var _shouldElideDefaultExport2 = _interopRequireDefault(_shouldElideDefaultExport);
  11. var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
  12. /**
  13. * Class for editing import statements when we are transforming to commonjs.
  14. */
  15. class CJSImportTransformer extends _Transformer2.default {
  16. __init() {this.hadExport = false}
  17. __init2() {this.hadNamedExport = false}
  18. __init3() {this.hadDefaultExport = false}
  19. constructor(
  20. rootTransformer,
  21. tokens,
  22. importProcessor,
  23. nameManager,
  24. helperManager,
  25. reactHotLoaderTransformer,
  26. enableLegacyBabel5ModuleInterop,
  27. enableLegacyTypeScriptModuleInterop,
  28. isTypeScriptTransformEnabled,
  29. isFlowTransformEnabled,
  30. preserveDynamicImport,
  31. keepUnusedImports,
  32. ) {
  33. super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.importProcessor = importProcessor;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.enableLegacyBabel5ModuleInterop = enableLegacyBabel5ModuleInterop;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.isFlowTransformEnabled = isFlowTransformEnabled;this.preserveDynamicImport = preserveDynamicImport;this.keepUnusedImports = keepUnusedImports;CJSImportTransformer.prototype.__init.call(this);CJSImportTransformer.prototype.__init2.call(this);CJSImportTransformer.prototype.__init3.call(this);;
  34. this.declarationInfo = isTypeScriptTransformEnabled
  35. ? _getDeclarationInfo2.default.call(void 0, tokens)
  36. : _getDeclarationInfo.EMPTY_DECLARATION_INFO;
  37. }
  38. getPrefixCode() {
  39. let prefix = "";
  40. if (this.hadExport) {
  41. prefix += 'Object.defineProperty(exports, "__esModule", {value: true});';
  42. }
  43. return prefix;
  44. }
  45. getSuffixCode() {
  46. if (this.enableLegacyBabel5ModuleInterop && this.hadDefaultExport && !this.hadNamedExport) {
  47. return "\nmodule.exports = exports.default;\n";
  48. }
  49. return "";
  50. }
  51. process() {
  52. // TypeScript `import foo = require('foo');` should always just be translated to plain require.
  53. if (this.tokens.matches3(_types.TokenType._import, _types.TokenType.name, _types.TokenType.eq)) {
  54. return this.processImportEquals();
  55. }
  56. if (this.tokens.matches1(_types.TokenType._import)) {
  57. this.processImport();
  58. return true;
  59. }
  60. if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.eq)) {
  61. this.tokens.replaceToken("module.exports");
  62. return true;
  63. }
  64. if (this.tokens.matches1(_types.TokenType._export) && !this.tokens.currentToken().isType) {
  65. this.hadExport = true;
  66. return this.processExport();
  67. }
  68. if (this.tokens.matches2(_types.TokenType.name, _types.TokenType.postIncDec)) {
  69. // Fall through to normal identifier matching if this doesn't apply.
  70. if (this.processPostIncDec()) {
  71. return true;
  72. }
  73. }
  74. if (this.tokens.matches1(_types.TokenType.name) || this.tokens.matches1(_types.TokenType.jsxName)) {
  75. return this.processIdentifier();
  76. }
  77. if (this.tokens.matches1(_types.TokenType.eq)) {
  78. return this.processAssignment();
  79. }
  80. if (this.tokens.matches1(_types.TokenType.assign)) {
  81. return this.processComplexAssignment();
  82. }
  83. if (this.tokens.matches1(_types.TokenType.preIncDec)) {
  84. return this.processPreIncDec();
  85. }
  86. return false;
  87. }
  88. processImportEquals() {
  89. const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
  90. if (this.importProcessor.shouldAutomaticallyElideImportedName(importName)) {
  91. // If this name is only used as a type, elide the whole import.
  92. _elideImportEquals2.default.call(void 0, this.tokens);
  93. } else {
  94. // Otherwise, switch `import` to `const`.
  95. this.tokens.replaceToken("const");
  96. }
  97. return true;
  98. }
  99. /**
  100. * Transform this:
  101. * import foo, {bar} from 'baz';
  102. * into
  103. * var _baz = require('baz'); var _baz2 = _interopRequireDefault(_baz);
  104. *
  105. * The import code was already generated in the import preprocessing step, so
  106. * we just need to look it up.
  107. */
  108. processImport() {
  109. if (this.tokens.matches2(_types.TokenType._import, _types.TokenType.parenL)) {
  110. if (this.preserveDynamicImport) {
  111. // Bail out, only making progress for this one token.
  112. this.tokens.copyToken();
  113. return;
  114. }
  115. const requireWrapper = this.enableLegacyTypeScriptModuleInterop
  116. ? ""
  117. : `${this.helperManager.getHelperName("interopRequireWildcard")}(`;
  118. this.tokens.replaceToken(`Promise.resolve().then(() => ${requireWrapper}require`);
  119. const contextId = this.tokens.currentToken().contextId;
  120. if (contextId == null) {
  121. throw new Error("Expected context ID on dynamic import invocation.");
  122. }
  123. this.tokens.copyToken();
  124. while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.parenR, contextId)) {
  125. this.rootTransformer.processToken();
  126. }
  127. this.tokens.replaceToken(requireWrapper ? ")))" : "))");
  128. return;
  129. }
  130. const shouldElideImport = this.removeImportAndDetectIfShouldElide();
  131. if (shouldElideImport) {
  132. this.tokens.removeToken();
  133. } else {
  134. const path = this.tokens.stringValue();
  135. this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
  136. this.tokens.appendCode(this.importProcessor.claimImportCode(path));
  137. }
  138. _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
  139. if (this.tokens.matches1(_types.TokenType.semi)) {
  140. this.tokens.removeToken();
  141. }
  142. }
  143. /**
  144. * Erase this import (since any CJS output would be completely different), and
  145. * return true if this import is should be elided due to being a type-only
  146. * import. Such imports will not be emitted at all to avoid side effects.
  147. *
  148. * Import elision only happens with the TypeScript or Flow transforms enabled.
  149. *
  150. * TODO: This function has some awkward overlap with
  151. * CJSImportProcessor.pruneTypeOnlyImports , and the two should be unified.
  152. * That function handles TypeScript implicit import name elision, and removes
  153. * an import if all typical imported names (without `type`) are removed due
  154. * to being type-only imports. This function handles Flow import removal and
  155. * properly distinguishes `import 'foo'` from `import {} from 'foo'` for TS
  156. * purposes.
  157. *
  158. * The position should end at the import string.
  159. */
  160. removeImportAndDetectIfShouldElide() {
  161. this.tokens.removeInitialToken();
  162. if (
  163. this.tokens.matchesContextual(_keywords.ContextualKeyword._type) &&
  164. !this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.comma) &&
  165. !this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._from)
  166. ) {
  167. // This is an "import type" statement, so exit early.
  168. this.removeRemainingImport();
  169. return true;
  170. }
  171. if (this.tokens.matches1(_types.TokenType.name) || this.tokens.matches1(_types.TokenType.star)) {
  172. // We have a default import or namespace import, so there must be some
  173. // non-type import.
  174. this.removeRemainingImport();
  175. return false;
  176. }
  177. if (this.tokens.matches1(_types.TokenType.string)) {
  178. // This is a bare import, so we should proceed with the import.
  179. return false;
  180. }
  181. let foundNonTypeImport = false;
  182. let foundAnyNamedImport = false;
  183. while (!this.tokens.matches1(_types.TokenType.string)) {
  184. // Check if any named imports are of the form "foo" or "foo as bar", with
  185. // no leading "type".
  186. if (
  187. (!foundNonTypeImport && this.tokens.matches1(_types.TokenType.braceL)) ||
  188. this.tokens.matches1(_types.TokenType.comma)
  189. ) {
  190. this.tokens.removeToken();
  191. if (!this.tokens.matches1(_types.TokenType.braceR)) {
  192. foundAnyNamedImport = true;
  193. }
  194. if (
  195. this.tokens.matches2(_types.TokenType.name, _types.TokenType.comma) ||
  196. this.tokens.matches2(_types.TokenType.name, _types.TokenType.braceR) ||
  197. this.tokens.matches4(_types.TokenType.name, _types.TokenType.name, _types.TokenType.name, _types.TokenType.comma) ||
  198. this.tokens.matches4(_types.TokenType.name, _types.TokenType.name, _types.TokenType.name, _types.TokenType.braceR)
  199. ) {
  200. foundNonTypeImport = true;
  201. }
  202. }
  203. this.tokens.removeToken();
  204. }
  205. if (this.keepUnusedImports) {
  206. return false;
  207. }
  208. if (this.isTypeScriptTransformEnabled) {
  209. return !foundNonTypeImport;
  210. } else if (this.isFlowTransformEnabled) {
  211. // In Flow, unlike TS, `import {} from 'foo';` preserves the import.
  212. return foundAnyNamedImport && !foundNonTypeImport;
  213. } else {
  214. return false;
  215. }
  216. }
  217. removeRemainingImport() {
  218. while (!this.tokens.matches1(_types.TokenType.string)) {
  219. this.tokens.removeToken();
  220. }
  221. }
  222. processIdentifier() {
  223. const token = this.tokens.currentToken();
  224. if (token.shadowsGlobal) {
  225. return false;
  226. }
  227. if (token.identifierRole === _tokenizer.IdentifierRole.ObjectShorthand) {
  228. return this.processObjectShorthand();
  229. }
  230. if (token.identifierRole !== _tokenizer.IdentifierRole.Access) {
  231. return false;
  232. }
  233. const replacement = this.importProcessor.getIdentifierReplacement(
  234. this.tokens.identifierNameForToken(token),
  235. );
  236. if (!replacement) {
  237. return false;
  238. }
  239. // Tolerate any number of closing parens while looking for an opening paren
  240. // that indicates a function call.
  241. let possibleOpenParenIndex = this.tokens.currentIndex() + 1;
  242. while (
  243. possibleOpenParenIndex < this.tokens.tokens.length &&
  244. this.tokens.tokens[possibleOpenParenIndex].type === _types.TokenType.parenR
  245. ) {
  246. possibleOpenParenIndex++;
  247. }
  248. // Avoid treating imported functions as methods of their `exports` object
  249. // by using `(0, f)` when the identifier is in a paren expression. Else
  250. // use `Function.prototype.call` when the identifier is a guaranteed
  251. // function call. When using `call`, pass undefined as the context.
  252. if (this.tokens.tokens[possibleOpenParenIndex].type === _types.TokenType.parenL) {
  253. if (
  254. this.tokens.tokenAtRelativeIndex(1).type === _types.TokenType.parenL &&
  255. this.tokens.tokenAtRelativeIndex(-1).type !== _types.TokenType._new
  256. ) {
  257. this.tokens.replaceToken(`${replacement}.call(void 0, `);
  258. // Remove the old paren.
  259. this.tokens.removeToken();
  260. // Balance out the new paren.
  261. this.rootTransformer.processBalancedCode();
  262. this.tokens.copyExpectedToken(_types.TokenType.parenR);
  263. } else {
  264. // See here: http://2ality.com/2015/12/references.html
  265. this.tokens.replaceToken(`(0, ${replacement})`);
  266. }
  267. } else {
  268. this.tokens.replaceToken(replacement);
  269. }
  270. return true;
  271. }
  272. processObjectShorthand() {
  273. const identifier = this.tokens.identifierName();
  274. const replacement = this.importProcessor.getIdentifierReplacement(identifier);
  275. if (!replacement) {
  276. return false;
  277. }
  278. this.tokens.replaceToken(`${identifier}: ${replacement}`);
  279. return true;
  280. }
  281. processExport() {
  282. if (
  283. this.tokens.matches2(_types.TokenType._export, _types.TokenType._enum) ||
  284. this.tokens.matches3(_types.TokenType._export, _types.TokenType._const, _types.TokenType._enum)
  285. ) {
  286. this.hadNamedExport = true;
  287. // Let the TypeScript transform handle it.
  288. return false;
  289. }
  290. if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._default)) {
  291. if (this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType._enum)) {
  292. this.hadDefaultExport = true;
  293. // Flow export default enums need some special handling, so handle them
  294. // in that tranform rather than this one.
  295. return false;
  296. }
  297. this.processExportDefault();
  298. return true;
  299. } else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.braceL)) {
  300. this.processExportBindings();
  301. return true;
  302. } else if (
  303. this.tokens.matches2(_types.TokenType._export, _types.TokenType.name) &&
  304. this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._type)
  305. ) {
  306. // export type {a};
  307. // export type {a as b};
  308. // export type {a} from './b';
  309. // export type * from './b';
  310. // export type * as ns from './b';
  311. this.tokens.removeInitialToken();
  312. this.tokens.removeToken();
  313. if (this.tokens.matches1(_types.TokenType.braceL)) {
  314. while (!this.tokens.matches1(_types.TokenType.braceR)) {
  315. this.tokens.removeToken();
  316. }
  317. this.tokens.removeToken();
  318. } else {
  319. // *
  320. this.tokens.removeToken();
  321. if (this.tokens.matches1(_types.TokenType._as)) {
  322. // as
  323. this.tokens.removeToken();
  324. // ns
  325. this.tokens.removeToken();
  326. }
  327. }
  328. // Remove type re-export `... } from './T'`
  329. if (
  330. this.tokens.matchesContextual(_keywords.ContextualKeyword._from) &&
  331. this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.string)
  332. ) {
  333. this.tokens.removeToken();
  334. this.tokens.removeToken();
  335. _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
  336. }
  337. return true;
  338. }
  339. this.hadNamedExport = true;
  340. if (
  341. this.tokens.matches2(_types.TokenType._export, _types.TokenType._var) ||
  342. this.tokens.matches2(_types.TokenType._export, _types.TokenType._let) ||
  343. this.tokens.matches2(_types.TokenType._export, _types.TokenType._const)
  344. ) {
  345. this.processExportVar();
  346. return true;
  347. } else if (
  348. this.tokens.matches2(_types.TokenType._export, _types.TokenType._function) ||
  349. // export async function
  350. this.tokens.matches3(_types.TokenType._export, _types.TokenType.name, _types.TokenType._function)
  351. ) {
  352. this.processExportFunction();
  353. return true;
  354. } else if (
  355. this.tokens.matches2(_types.TokenType._export, _types.TokenType._class) ||
  356. this.tokens.matches3(_types.TokenType._export, _types.TokenType._abstract, _types.TokenType._class) ||
  357. this.tokens.matches2(_types.TokenType._export, _types.TokenType.at)
  358. ) {
  359. this.processExportClass();
  360. return true;
  361. } else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.star)) {
  362. this.processExportStar();
  363. return true;
  364. } else {
  365. throw new Error("Unrecognized export syntax.");
  366. }
  367. }
  368. processAssignment() {
  369. const index = this.tokens.currentIndex();
  370. const identifierToken = this.tokens.tokens[index - 1];
  371. // If the LHS is a type identifier, this must be a declaration like `let a: b = c;`,
  372. // with `b` as the identifier, so nothing needs to be done in that case.
  373. if (identifierToken.isType || identifierToken.type !== _types.TokenType.name) {
  374. return false;
  375. }
  376. if (identifierToken.shadowsGlobal) {
  377. return false;
  378. }
  379. if (index >= 2 && this.tokens.matches1AtIndex(index - 2, _types.TokenType.dot)) {
  380. return false;
  381. }
  382. if (index >= 2 && [_types.TokenType._var, _types.TokenType._let, _types.TokenType._const].includes(this.tokens.tokens[index - 2].type)) {
  383. // Declarations don't need an extra assignment. This doesn't avoid the
  384. // assignment for comma-separated declarations, but it's still correct
  385. // since the assignment is just redundant.
  386. return false;
  387. }
  388. const assignmentSnippet = this.importProcessor.resolveExportBinding(
  389. this.tokens.identifierNameForToken(identifierToken),
  390. );
  391. if (!assignmentSnippet) {
  392. return false;
  393. }
  394. this.tokens.copyToken();
  395. this.tokens.appendCode(` ${assignmentSnippet} =`);
  396. return true;
  397. }
  398. /**
  399. * Process something like `a += 3`, where `a` might be an exported value.
  400. */
  401. processComplexAssignment() {
  402. const index = this.tokens.currentIndex();
  403. const identifierToken = this.tokens.tokens[index - 1];
  404. if (identifierToken.type !== _types.TokenType.name) {
  405. return false;
  406. }
  407. if (identifierToken.shadowsGlobal) {
  408. return false;
  409. }
  410. if (index >= 2 && this.tokens.matches1AtIndex(index - 2, _types.TokenType.dot)) {
  411. return false;
  412. }
  413. const assignmentSnippet = this.importProcessor.resolveExportBinding(
  414. this.tokens.identifierNameForToken(identifierToken),
  415. );
  416. if (!assignmentSnippet) {
  417. return false;
  418. }
  419. this.tokens.appendCode(` = ${assignmentSnippet}`);
  420. this.tokens.copyToken();
  421. return true;
  422. }
  423. /**
  424. * Process something like `++a`, where `a` might be an exported value.
  425. */
  426. processPreIncDec() {
  427. const index = this.tokens.currentIndex();
  428. const identifierToken = this.tokens.tokens[index + 1];
  429. if (identifierToken.type !== _types.TokenType.name) {
  430. return false;
  431. }
  432. if (identifierToken.shadowsGlobal) {
  433. return false;
  434. }
  435. // Ignore things like ++a.b and ++a[b] and ++a().b.
  436. if (
  437. index + 2 < this.tokens.tokens.length &&
  438. (this.tokens.matches1AtIndex(index + 2, _types.TokenType.dot) ||
  439. this.tokens.matches1AtIndex(index + 2, _types.TokenType.bracketL) ||
  440. this.tokens.matches1AtIndex(index + 2, _types.TokenType.parenL))
  441. ) {
  442. return false;
  443. }
  444. const identifierName = this.tokens.identifierNameForToken(identifierToken);
  445. const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
  446. if (!assignmentSnippet) {
  447. return false;
  448. }
  449. this.tokens.appendCode(`${assignmentSnippet} = `);
  450. this.tokens.copyToken();
  451. return true;
  452. }
  453. /**
  454. * Process something like `a++`, where `a` might be an exported value.
  455. * This starts at the `a`, not at the `++`.
  456. */
  457. processPostIncDec() {
  458. const index = this.tokens.currentIndex();
  459. const identifierToken = this.tokens.tokens[index];
  460. const operatorToken = this.tokens.tokens[index + 1];
  461. if (identifierToken.type !== _types.TokenType.name) {
  462. return false;
  463. }
  464. if (identifierToken.shadowsGlobal) {
  465. return false;
  466. }
  467. if (index >= 1 && this.tokens.matches1AtIndex(index - 1, _types.TokenType.dot)) {
  468. return false;
  469. }
  470. const identifierName = this.tokens.identifierNameForToken(identifierToken);
  471. const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
  472. if (!assignmentSnippet) {
  473. return false;
  474. }
  475. const operatorCode = this.tokens.rawCodeForToken(operatorToken);
  476. // We might also replace the identifier with something like exports.x, so
  477. // do that replacement here as well.
  478. const base = this.importProcessor.getIdentifierReplacement(identifierName) || identifierName;
  479. if (operatorCode === "++") {
  480. this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} + 1, ${base} - 1)`);
  481. } else if (operatorCode === "--") {
  482. this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} - 1, ${base} + 1)`);
  483. } else {
  484. throw new Error(`Unexpected operator: ${operatorCode}`);
  485. }
  486. this.tokens.removeToken();
  487. return true;
  488. }
  489. processExportDefault() {
  490. let exportedRuntimeValue = true;
  491. if (
  492. this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._function, _types.TokenType.name) ||
  493. // export default async function
  494. (this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType.name, _types.TokenType._function, _types.TokenType.name) &&
  495. this.tokens.matchesContextualAtIndex(
  496. this.tokens.currentIndex() + 2,
  497. _keywords.ContextualKeyword._async,
  498. ))
  499. ) {
  500. this.tokens.removeInitialToken();
  501. this.tokens.removeToken();
  502. // Named function export case: change it to a top-level function
  503. // declaration followed by exports statement.
  504. const name = this.processNamedFunction();
  505. this.tokens.appendCode(` exports.default = ${name};`);
  506. } else if (
  507. this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._class, _types.TokenType.name) ||
  508. this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType._abstract, _types.TokenType._class, _types.TokenType.name) ||
  509. this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType.at)
  510. ) {
  511. this.tokens.removeInitialToken();
  512. this.tokens.removeToken();
  513. this.copyDecorators();
  514. if (this.tokens.matches1(_types.TokenType._abstract)) {
  515. this.tokens.removeToken();
  516. }
  517. const name = this.rootTransformer.processNamedClass();
  518. this.tokens.appendCode(` exports.default = ${name};`);
  519. // After this point, this is a plain "export default E" statement.
  520. } else if (
  521. _shouldElideDefaultExport2.default.call(void 0,
  522. this.isTypeScriptTransformEnabled,
  523. this.keepUnusedImports,
  524. this.tokens,
  525. this.declarationInfo,
  526. )
  527. ) {
  528. // If the exported value is just an identifier and should be elided by TypeScript
  529. // rules, then remove it entirely. It will always have the form `export default e`,
  530. // where `e` is an identifier.
  531. exportedRuntimeValue = false;
  532. this.tokens.removeInitialToken();
  533. this.tokens.removeToken();
  534. this.tokens.removeToken();
  535. } else if (this.reactHotLoaderTransformer) {
  536. // We need to assign E to a variable. Change "export default E" to
  537. // "let _default; exports.default = _default = E"
  538. const defaultVarName = this.nameManager.claimFreeName("_default");
  539. this.tokens.replaceToken(`let ${defaultVarName}; exports.`);
  540. this.tokens.copyToken();
  541. this.tokens.appendCode(` = ${defaultVarName} =`);
  542. this.reactHotLoaderTransformer.setExtractedDefaultExportName(defaultVarName);
  543. } else {
  544. // Change "export default E" to "exports.default = E"
  545. this.tokens.replaceToken("exports.");
  546. this.tokens.copyToken();
  547. this.tokens.appendCode(" =");
  548. }
  549. if (exportedRuntimeValue) {
  550. this.hadDefaultExport = true;
  551. }
  552. }
  553. copyDecorators() {
  554. while (this.tokens.matches1(_types.TokenType.at)) {
  555. this.tokens.copyToken();
  556. if (this.tokens.matches1(_types.TokenType.parenL)) {
  557. this.tokens.copyExpectedToken(_types.TokenType.parenL);
  558. this.rootTransformer.processBalancedCode();
  559. this.tokens.copyExpectedToken(_types.TokenType.parenR);
  560. } else {
  561. this.tokens.copyExpectedToken(_types.TokenType.name);
  562. while (this.tokens.matches1(_types.TokenType.dot)) {
  563. this.tokens.copyExpectedToken(_types.TokenType.dot);
  564. this.tokens.copyExpectedToken(_types.TokenType.name);
  565. }
  566. if (this.tokens.matches1(_types.TokenType.parenL)) {
  567. this.tokens.copyExpectedToken(_types.TokenType.parenL);
  568. this.rootTransformer.processBalancedCode();
  569. this.tokens.copyExpectedToken(_types.TokenType.parenR);
  570. }
  571. }
  572. }
  573. }
  574. /**
  575. * Transform a declaration like `export var`, `export let`, or `export const`.
  576. */
  577. processExportVar() {
  578. if (this.isSimpleExportVar()) {
  579. this.processSimpleExportVar();
  580. } else {
  581. this.processComplexExportVar();
  582. }
  583. }
  584. /**
  585. * Determine if the export is of the form:
  586. * export var/let/const [varName] = [expr];
  587. * In other words, determine if function name inference might apply.
  588. */
  589. isSimpleExportVar() {
  590. let tokenIndex = this.tokens.currentIndex();
  591. // export
  592. tokenIndex++;
  593. // var/let/const
  594. tokenIndex++;
  595. if (!this.tokens.matches1AtIndex(tokenIndex, _types.TokenType.name)) {
  596. return false;
  597. }
  598. tokenIndex++;
  599. while (tokenIndex < this.tokens.tokens.length && this.tokens.tokens[tokenIndex].isType) {
  600. tokenIndex++;
  601. }
  602. if (!this.tokens.matches1AtIndex(tokenIndex, _types.TokenType.eq)) {
  603. return false;
  604. }
  605. return true;
  606. }
  607. /**
  608. * Transform an `export var` declaration initializing a single variable.
  609. *
  610. * For example, this:
  611. * export const f = () => {};
  612. * becomes this:
  613. * const f = () => {}; exports.f = f;
  614. *
  615. * The variable is unused (e.g. exports.f has the true value of the export).
  616. * We need to produce an assignment of this form so that the function will
  617. * have an inferred name of "f", which wouldn't happen in the more general
  618. * case below.
  619. */
  620. processSimpleExportVar() {
  621. // export
  622. this.tokens.removeInitialToken();
  623. // var/let/const
  624. this.tokens.copyToken();
  625. const varName = this.tokens.identifierName();
  626. // x: number -> x
  627. while (!this.tokens.matches1(_types.TokenType.eq)) {
  628. this.rootTransformer.processToken();
  629. }
  630. const endIndex = this.tokens.currentToken().rhsEndIndex;
  631. if (endIndex == null) {
  632. throw new Error("Expected = token with an end index.");
  633. }
  634. while (this.tokens.currentIndex() < endIndex) {
  635. this.rootTransformer.processToken();
  636. }
  637. this.tokens.appendCode(`; exports.${varName} = ${varName}`);
  638. }
  639. /**
  640. * Transform normal declaration exports, including handling destructuring.
  641. * For example, this:
  642. * export const {x: [a = 2, b], c} = d;
  643. * becomes this:
  644. * ({x: [exports.a = 2, exports.b], c: exports.c} = d;)
  645. */
  646. processComplexExportVar() {
  647. this.tokens.removeInitialToken();
  648. this.tokens.removeToken();
  649. const needsParens = this.tokens.matches1(_types.TokenType.braceL);
  650. if (needsParens) {
  651. this.tokens.appendCode("(");
  652. }
  653. let depth = 0;
  654. while (true) {
  655. if (
  656. this.tokens.matches1(_types.TokenType.braceL) ||
  657. this.tokens.matches1(_types.TokenType.dollarBraceL) ||
  658. this.tokens.matches1(_types.TokenType.bracketL)
  659. ) {
  660. depth++;
  661. this.tokens.copyToken();
  662. } else if (this.tokens.matches1(_types.TokenType.braceR) || this.tokens.matches1(_types.TokenType.bracketR)) {
  663. depth--;
  664. this.tokens.copyToken();
  665. } else if (
  666. depth === 0 &&
  667. !this.tokens.matches1(_types.TokenType.name) &&
  668. !this.tokens.currentToken().isType
  669. ) {
  670. break;
  671. } else if (this.tokens.matches1(_types.TokenType.eq)) {
  672. // Default values might have assignments in the RHS that we want to ignore, so skip past
  673. // them.
  674. const endIndex = this.tokens.currentToken().rhsEndIndex;
  675. if (endIndex == null) {
  676. throw new Error("Expected = token with an end index.");
  677. }
  678. while (this.tokens.currentIndex() < endIndex) {
  679. this.rootTransformer.processToken();
  680. }
  681. } else {
  682. const token = this.tokens.currentToken();
  683. if (_tokenizer.isDeclaration.call(void 0, token)) {
  684. const name = this.tokens.identifierName();
  685. let replacement = this.importProcessor.getIdentifierReplacement(name);
  686. if (replacement === null) {
  687. throw new Error(`Expected a replacement for ${name} in \`export var\` syntax.`);
  688. }
  689. if (_tokenizer.isObjectShorthandDeclaration.call(void 0, token)) {
  690. replacement = `${name}: ${replacement}`;
  691. }
  692. this.tokens.replaceToken(replacement);
  693. } else {
  694. this.rootTransformer.processToken();
  695. }
  696. }
  697. }
  698. if (needsParens) {
  699. // Seek to the end of the RHS.
  700. const endIndex = this.tokens.currentToken().rhsEndIndex;
  701. if (endIndex == null) {
  702. throw new Error("Expected = token with an end index.");
  703. }
  704. while (this.tokens.currentIndex() < endIndex) {
  705. this.rootTransformer.processToken();
  706. }
  707. this.tokens.appendCode(")");
  708. }
  709. }
  710. /**
  711. * Transform this:
  712. * export function foo() {}
  713. * into this:
  714. * function foo() {} exports.foo = foo;
  715. */
  716. processExportFunction() {
  717. this.tokens.replaceToken("");
  718. const name = this.processNamedFunction();
  719. this.tokens.appendCode(` exports.${name} = ${name};`);
  720. }
  721. /**
  722. * Skip past a function with a name and return that name.
  723. */
  724. processNamedFunction() {
  725. if (this.tokens.matches1(_types.TokenType._function)) {
  726. this.tokens.copyToken();
  727. } else if (this.tokens.matches2(_types.TokenType.name, _types.TokenType._function)) {
  728. if (!this.tokens.matchesContextual(_keywords.ContextualKeyword._async)) {
  729. throw new Error("Expected async keyword in function export.");
  730. }
  731. this.tokens.copyToken();
  732. this.tokens.copyToken();
  733. }
  734. if (this.tokens.matches1(_types.TokenType.star)) {
  735. this.tokens.copyToken();
  736. }
  737. if (!this.tokens.matches1(_types.TokenType.name)) {
  738. throw new Error("Expected identifier for exported function name.");
  739. }
  740. const name = this.tokens.identifierName();
  741. this.tokens.copyToken();
  742. if (this.tokens.currentToken().isType) {
  743. this.tokens.removeInitialToken();
  744. while (this.tokens.currentToken().isType) {
  745. this.tokens.removeToken();
  746. }
  747. }
  748. this.tokens.copyExpectedToken(_types.TokenType.parenL);
  749. this.rootTransformer.processBalancedCode();
  750. this.tokens.copyExpectedToken(_types.TokenType.parenR);
  751. this.rootTransformer.processPossibleTypeRange();
  752. this.tokens.copyExpectedToken(_types.TokenType.braceL);
  753. this.rootTransformer.processBalancedCode();
  754. this.tokens.copyExpectedToken(_types.TokenType.braceR);
  755. return name;
  756. }
  757. /**
  758. * Transform this:
  759. * export class A {}
  760. * into this:
  761. * class A {} exports.A = A;
  762. */
  763. processExportClass() {
  764. this.tokens.removeInitialToken();
  765. this.copyDecorators();
  766. if (this.tokens.matches1(_types.TokenType._abstract)) {
  767. this.tokens.removeToken();
  768. }
  769. const name = this.rootTransformer.processNamedClass();
  770. this.tokens.appendCode(` exports.${name} = ${name};`);
  771. }
  772. /**
  773. * Transform this:
  774. * export {a, b as c};
  775. * into this:
  776. * exports.a = a; exports.c = b;
  777. *
  778. * OR
  779. *
  780. * Transform this:
  781. * export {a, b as c} from './foo';
  782. * into the pre-generated Object.defineProperty code from the ImportProcessor.
  783. *
  784. * For the first case, if the TypeScript transform is enabled, we need to skip
  785. * exports that are only defined as types.
  786. */
  787. processExportBindings() {
  788. this.tokens.removeInitialToken();
  789. this.tokens.removeToken();
  790. const isReExport = _isExportFrom2.default.call(void 0, this.tokens);
  791. const exportStatements = [];
  792. while (true) {
  793. if (this.tokens.matches1(_types.TokenType.braceR)) {
  794. this.tokens.removeToken();
  795. break;
  796. }
  797. const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens);
  798. while (this.tokens.currentIndex() < specifierInfo.endIndex) {
  799. this.tokens.removeToken();
  800. }
  801. const shouldRemoveExport =
  802. specifierInfo.isType ||
  803. (!isReExport && this.shouldElideExportedIdentifier(specifierInfo.leftName));
  804. if (!shouldRemoveExport) {
  805. const exportedName = specifierInfo.rightName;
  806. if (exportedName === "default") {
  807. this.hadDefaultExport = true;
  808. } else {
  809. this.hadNamedExport = true;
  810. }
  811. const localName = specifierInfo.leftName;
  812. const newLocalName = this.importProcessor.getIdentifierReplacement(localName);
  813. exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`);
  814. }
  815. if (this.tokens.matches1(_types.TokenType.braceR)) {
  816. this.tokens.removeToken();
  817. break;
  818. }
  819. if (this.tokens.matches2(_types.TokenType.comma, _types.TokenType.braceR)) {
  820. this.tokens.removeToken();
  821. this.tokens.removeToken();
  822. break;
  823. } else if (this.tokens.matches1(_types.TokenType.comma)) {
  824. this.tokens.removeToken();
  825. } else {
  826. throw new Error(`Unexpected token: ${JSON.stringify(this.tokens.currentToken())}`);
  827. }
  828. }
  829. if (this.tokens.matchesContextual(_keywords.ContextualKeyword._from)) {
  830. // This is an export...from, so throw away the normal named export code
  831. // and use the Object.defineProperty code from ImportProcessor.
  832. this.tokens.removeToken();
  833. const path = this.tokens.stringValue();
  834. this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
  835. _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
  836. } else {
  837. // This is a normal named export, so use that.
  838. this.tokens.appendCode(exportStatements.join(" "));
  839. }
  840. if (this.tokens.matches1(_types.TokenType.semi)) {
  841. this.tokens.removeToken();
  842. }
  843. }
  844. processExportStar() {
  845. this.tokens.removeInitialToken();
  846. while (!this.tokens.matches1(_types.TokenType.string)) {
  847. this.tokens.removeToken();
  848. }
  849. const path = this.tokens.stringValue();
  850. this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
  851. _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
  852. if (this.tokens.matches1(_types.TokenType.semi)) {
  853. this.tokens.removeToken();
  854. }
  855. }
  856. shouldElideExportedIdentifier(name) {
  857. return (
  858. this.isTypeScriptTransformEnabled &&
  859. !this.keepUnusedImports &&
  860. !this.declarationInfo.valueDeclarations.has(name)
  861. );
  862. }
  863. } exports.default = CJSImportTransformer;