1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.createMultiRuleTransformer = createMultiRuleTransformer;
7exports.loaders = void 0;
8function _chalk() {
9  const data = _interopRequireDefault(require("chalk"));
10  _chalk = function () {
11    return data;
12  };
13  return data;
14}
15function _debug() {
16  const data = _interopRequireDefault(require("debug"));
17  _debug = function () {
18    return data;
19  };
20  return data;
21}
22function _resolveFrom() {
23  const data = _interopRequireDefault(require("resolve-from"));
24  _resolveFrom = function () {
25    return data;
26  };
27  return data;
28}
29function _generateFunctionMap() {
30  const data = require("./generateFunctionMap");
31  _generateFunctionMap = function () {
32    return data;
33  };
34  return data;
35}
36function _getBabelConfig() {
37  const data = require("./getBabelConfig");
38  _getBabelConfig = function () {
39    return data;
40  };
41  return data;
42}
43function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44// Copyright 2021-present 650 Industries (Expo). All rights reserved.
45
46const debug = (0, _debug().default)('expo:metro:exotic-babel-transformer');
47let babelCore;
48function getBabelCoreFromProject(projectRoot) {
49  if (babelCore) return babelCore;
50  babelCore = require((0, _resolveFrom().default)(projectRoot, '@babel/core'));
51  return babelCore;
52}
53let babelParser;
54function getBabelParserFromProject(projectRoot) {
55  if (babelParser) return babelParser;
56  babelParser = require((0, _resolveFrom().default)(projectRoot, '@babel/parser'));
57  return babelParser;
58}
59function sucrase(args, {
60  transforms
61}) {
62  const {
63    src,
64    filename,
65    options: {
66      dev
67    }
68  } = args;
69  const {
70    transform
71  } = require('sucrase');
72  const results = transform(src, {
73    filePath: filename,
74    production: !dev,
75    transforms
76  });
77  return {
78    code: results.code,
79    functionMap: null
80  };
81}
82const getExpensiveSucraseTransforms = filename => ['jsx', 'imports', /\.tsx?$/.test(filename) ? 'typescript' : 'flow'];
83function parseAst(projectRoot, sourceCode) {
84  const babylon = getBabelParserFromProject(projectRoot);
85  return babylon.parse(sourceCode, {
86    sourceType: 'unambiguous'
87  });
88}
89/** Create a transformer that emulates Webpack's loader system. */
90function createMultiRuleTransformer({
91  getRuleType,
92  rules
93}) {
94  // const warnings: string[] = [];
95  return function transform(args) {
96    const {
97      filename,
98      options
99    } = args;
100    const OLD_BABEL_ENV = process.env.BABEL_ENV;
101    process.env.BABEL_ENV = options !== null && options !== void 0 && options.dev ? 'development' : process.env.BABEL_ENV || 'production';
102    try {
103      const ruleType = getRuleType(args);
104      for (const rule of rules) {
105        // optimization for checking node modules
106        if (rule.type && rule.type !== ruleType) {
107          continue;
108        }
109        const isMatched = typeof rule.test === 'function' ? rule.test(args) : rule.test.test(args.filename);
110        if (isMatched) {
111          const results = rule.transform(args);
112          // @ts-ignore: Add extra property for testing
113          results._ruleName = rule.name;
114          // Perform a basic parse if none exists, this enables us to control the output, but only if it changed.
115          if (results.code && !results.ast) {
116            // Parse AST with babel otherwise Metro transformer will throw away the returned results.
117            results.ast = parseAst(options === null || options === void 0 ? void 0 : options.projectRoot, results.code);
118          }
119
120          // TODO: Suboptimal warnings
121          // if (rule.warn) {
122          //   const matchName =
123          //     filename.match(/node_modules\/((:?@[\w\d-]+\/[\w\d-]+)|(:?[\w\d-]+))\/?/)?.[1] ??
124          //     filename;
125          //   if (matchName && !warnings.includes(matchName)) {
126          //     warnings.push(matchName);
127          //     console.warn(chalk.yellow.bold`warn `, matchName);
128          //     console.warn(
129          //       chalk.yellow`untranspiled module is potentially causing bundler slowdown, using modules that support commonjs will make your dev server much faster.`
130          //     );
131          //   }
132          // }
133
134          return results;
135        }
136      }
137      throw new Error('no loader rule to handle file: ' + filename);
138    } finally {
139      if (OLD_BABEL_ENV) {
140        process.env.BABEL_ENV = OLD_BABEL_ENV;
141      }
142    }
143  };
144}
145function app(args) {
146  debug('app:', args.filename);
147  const {
148    filename,
149    options,
150    src,
151    plugins
152  } = args;
153  const babelConfig = {
154    // ES modules require sourceType='module' but OSS may not always want that
155    sourceType: 'unambiguous',
156    ...(0, _getBabelConfig().getBabelConfig)(filename, options, plugins),
157    // Variables that are exposed to the user's babel preset.
158    caller: {
159      name: 'metro',
160      platform: options.platform
161    },
162    ast: true
163  };
164
165  // Surface a warning function so babel linters can be used.
166  Object.defineProperty(babelConfig.caller, 'onWarning', {
167    enumerable: false,
168    writable: false,
169    value: babelConfig.caller.onWarning = function (msg) {
170      // Format the file path first so users know where the warning came from.
171      console.warn(_chalk().default.bold.yellow`warn ` + args.filename);
172      console.warn(msg);
173    }
174  });
175  const {
176    parseSync,
177    transformFromAstSync
178  } = getBabelCoreFromProject(options.projectRoot);
179  const sourceAst = parseSync(src, babelConfig);
180
181  // Should never happen.
182  if (!sourceAst) return {
183    ast: null
184  };
185  const result = transformFromAstSync(sourceAst, src, babelConfig);
186
187  // TODO: Disable by default
188  const functionMap = (0, _generateFunctionMap().generateFunctionMap)(sourceAst, {
189    filename
190  });
191  // The result from `transformFromAstSync` can be null (if the file is ignored)
192  if (!result) {
193    return {
194      ast: null,
195      functionMap
196    };
197  }
198  return {
199    ast: result.ast,
200    functionMap
201  };
202}
203const loaders = {
204  // Perform the standard, and most expensive transpilation sequence.
205  app,
206  // Transpile react-native with sucrase.
207  reactNativeModule(args) {
208    // Special file needs full transpilation.
209    if (args.filename.includes('react-native/Libraries/Events/EventPolyfill.js')) {
210      // Match React Native modules which use non-standard flow features, convert them using babel (most expensive).
211      return app(args);
212    }
213    debug('rn:', args.filename);
214    return sucrase(args, {
215      transforms: ['jsx', 'flow', 'imports']
216    });
217  },
218  // Transpile expo modules with sucrase.
219  expoModule(args) {
220    debug('expo:', args.filename);
221    // TODO: Fix all expo packages
222    return sucrase(args, {
223      transforms: ['imports',
224      // TODO: fix expo-processing, expo/vector-icons
225      /(expo-processing|expo\/vector-icons)/.test(args.filename) && 'jsx',
226      // TODO: fix expo-asset-utils
227      /(expo-asset-utils)/.test(args.filename) && 'flow'].filter(Boolean)
228    });
229  },
230  // Transpile known community modules with the most expensive sucrase
231  untranspiledModule(args) {
232    debug('known issues:', args.filename);
233    return sucrase(args, {
234      transforms: getExpensiveSucraseTransforms(args.filename)
235    });
236  },
237  // Pass all modules through without transpiling them.
238  passthroughModule(args) {
239    const {
240      filename,
241      options,
242      src
243    } = args;
244    debug('passthrough:', filename);
245
246    // Perform a basic ast parse, this doesn't matter since the worker will parse and ignore anyways.
247    const ast = parseAst(options.projectRoot, src);
248
249    // TODO: Disable by default
250    const functionMap = (0, _generateFunctionMap().generateFunctionMap)(ast, {
251      filename
252    });
253    return {
254      code: src,
255      functionMap,
256      ast
257    };
258  }
259};
260exports.loaders = loaders;
261//# sourceMappingURL=createMultiRuleTransformer.js.map