rewrite-modules.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. 'use strict';
  8. /**
  9. * Rewrites module string literals according to the `map` and `prefix` options.
  10. * This allows other npm packages to be published and used directly without
  11. * being a part of the same build.
  12. */
  13. function mapModule(state, module) {
  14. var moduleMap = state.opts.map || {};
  15. if (moduleMap.hasOwnProperty(module)) {
  16. return moduleMap[module];
  17. }
  18. // Jest understands the haste module system, so leave modules intact.
  19. if (process.env.NODE_ENV !== 'test') {
  20. var modulePrefix = state.opts.prefix;
  21. if (modulePrefix == null) {
  22. modulePrefix = './';
  23. }
  24. return modulePrefix + module;
  25. }
  26. return null;
  27. }
  28. var jestMethods = [
  29. 'dontMock',
  30. 'genMockFromModule',
  31. 'mock',
  32. 'setMock',
  33. 'unmock',
  34. ];
  35. function isJestProperty(t, property) {
  36. return t.isIdentifier(property) && jestMethods.indexOf(property.name) !== -1;
  37. }
  38. module.exports = function(babel) {
  39. var t = babel.types;
  40. /**
  41. * Transforms `require('Foo')` and `require.requireActual('Foo')`.
  42. */
  43. function transformRequireCall(path, state) {
  44. var calleePath = path.get('callee');
  45. if (
  46. !t.isIdentifier(calleePath.node, {name: 'require'}) &&
  47. !(
  48. t.isMemberExpression(calleePath.node) &&
  49. t.isIdentifier(calleePath.node.object, {name: 'require'}) &&
  50. t.isIdentifier(calleePath.node.property, {name: 'requireActual'})
  51. )
  52. ) {
  53. return;
  54. }
  55. var args = path.get('arguments');
  56. if (!args.length) {
  57. return;
  58. }
  59. var moduleArg = args[0];
  60. if (moduleArg.node.type === 'StringLiteral') {
  61. var module = mapModule(state, moduleArg.node.value);
  62. if (module) {
  63. moduleArg.replaceWith(t.stringLiteral(module));
  64. }
  65. }
  66. }
  67. /**
  68. * Transforms `import type Bar from 'foo'`
  69. */
  70. function transformTypeImport(path, state) {
  71. var source = path.get('source');
  72. if (source.type === 'StringLiteral') {
  73. var module = mapModule(state, source.node.value);
  74. if (module) {
  75. source.replaceWith(t.stringLiteral(module));
  76. }
  77. }
  78. }
  79. /**
  80. * Transforms either individual or chained calls to `jest.dontMock('Foo')`,
  81. * `jest.mock('Foo')`, and `jest.genMockFromModule('Foo')`.
  82. */
  83. function transformJestHelper(path, state) {
  84. var calleePath = path.get('callee');
  85. var args = path.get('arguments');
  86. if (!args.length) {
  87. return;
  88. }
  89. var moduleArg = args[0];
  90. if (
  91. moduleArg.node.type === 'StringLiteral' &&
  92. calleePath.node &&
  93. isJestProperty(t, calleePath.node.property)
  94. ) {
  95. var module = mapModule(state, moduleArg.node.value);
  96. if (module) {
  97. moduleArg.replaceWith(t.stringLiteral(module));
  98. }
  99. }
  100. }
  101. const jestIdentifier = {
  102. Identifier(path) {
  103. if (path.node.name === 'jest') {
  104. this.isJest = true;
  105. }
  106. },
  107. };
  108. function transformJestCall(path, state) {
  109. let params = {isJest: false};
  110. path.traverse(jestIdentifier, params);
  111. if (params.isJest) {
  112. transformJestHelper(path, state);
  113. }
  114. }
  115. return {
  116. visitor: {
  117. CallExpression: {
  118. exit(path, state) {
  119. if (path.node.seen) {
  120. return;
  121. }
  122. transformRequireCall(path, state);
  123. transformJestCall(path, state);
  124. path.node.seen = true;
  125. },
  126. },
  127. ImportDeclaration: {
  128. exit(path, state) {
  129. let importKind = path.node.importKind;
  130. if (importKind === 'type' || importKind === 'typeof') {
  131. transformTypeImport(path, state);
  132. }
  133. }
  134. }
  135. },
  136. };
  137. };