Нет описания

CJSImportTransformer.js 32KB

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