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