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} 145const loaders = { 146 // Perform the standard, and most expensive transpilation sequence. 147 app(args) { 148 debug('app:', args.filename); 149 const { 150 filename, 151 options, 152 src, 153 plugins 154 } = args; 155 const babelConfig = { 156 // ES modules require sourceType='module' but OSS may not always want that 157 sourceType: 'unambiguous', 158 ...(0, _getBabelConfig().getBabelConfig)(filename, options, plugins), 159 // Variables that are exposed to the user's babel preset. 160 caller: { 161 name: 'metro', 162 platform: options.platform 163 }, 164 ast: true 165 }; 166 167 // Surface a warning function so babel linters can be used. 168 Object.defineProperty(babelConfig.caller, 'onWarning', { 169 enumerable: false, 170 writable: false, 171 value: babelConfig.caller.onWarning = function (msg) { 172 // Format the file path first so users know where the warning came from. 173 console.warn(_chalk().default.bold.yellow`warn ` + args.filename); 174 console.warn(msg); 175 } 176 }); 177 const { 178 parseSync, 179 transformFromAstSync 180 } = getBabelCoreFromProject(options.projectRoot); 181 const sourceAst = parseSync(src, babelConfig); 182 183 // Should never happen. 184 if (!sourceAst) return { 185 ast: null 186 }; 187 const result = transformFromAstSync(sourceAst, src, babelConfig); 188 189 // TODO: Disable by default 190 const functionMap = (0, _generateFunctionMap().generateFunctionMap)(options.projectRoot, sourceAst, { 191 filename 192 }); 193 // The result from `transformFromAstSync` can be null (if the file is ignored) 194 if (!result) { 195 return { 196 ast: null, 197 functionMap 198 }; 199 } 200 return { 201 ast: result.ast, 202 functionMap 203 }; 204 }, 205 // Transpile react-native with sucrase. 206 reactNativeModule(args) { 207 debug('rn:', args.filename); 208 return sucrase(args, { 209 transforms: ['jsx', 'flow', 'imports'] 210 }); 211 }, 212 // Transpile expo modules with sucrase. 213 expoModule(args) { 214 debug('expo:', args.filename); 215 // TODO: Fix all expo packages 216 return sucrase(args, { 217 transforms: ['imports', 218 // TODO: fix expo-processing, expo/vector-icons 219 /(expo-processing|expo\/vector-icons)/.test(args.filename) && 'jsx', 220 // TODO: fix expo-asset-utils 221 /(expo-asset-utils)/.test(args.filename) && 'flow'].filter(Boolean) 222 }); 223 }, 224 // Transpile known community modules with the most expensive sucrase 225 untranspiledModule(args) { 226 debug('known issues:', args.filename); 227 return sucrase(args, { 228 transforms: getExpensiveSucraseTransforms(args.filename) 229 }); 230 }, 231 // Pass all modules through without transpiling them. 232 passthroughModule(args) { 233 const { 234 filename, 235 options, 236 src 237 } = args; 238 debug('passthrough:', filename); 239 240 // Perform a basic ast parse, this doesn't matter since the worker will parse and ignore anyways. 241 const ast = parseAst(options.projectRoot, src); 242 243 // TODO: Disable by default 244 const functionMap = (0, _generateFunctionMap().generateFunctionMap)(options.projectRoot, ast, { 245 filename 246 }); 247 return { 248 code: src, 249 functionMap, 250 ast 251 }; 252 } 253}; 254exports.loaders = loaders; 255//# sourceMappingURL=createMultiRuleTransformer.js.map