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