1/** 2 * Copyright © 2023 650 Industries. 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 */ 7import fs from 'fs'; 8import path from 'path'; 9 10// A list of the Node.js standard library modules. 11export const NODE_STDLIB_MODULES = [ 12 'assert', 13 'async_hooks', 14 'buffer', 15 'child_process', 16 'cluster', 17 'crypto', 18 'dgram', 19 'dns', 20 'domain', 21 'events', 22 'fs', 23 'fs/promises', 24 'http', 25 'https', 26 'net', 27 'os', 28 'path', 29 'punycode', 30 'querystring', 31 'readline', 32 'repl', 33 'stream', 34 'string_decoder', 35 'tls', 36 'tty', 37 'url', 38 'util', 39 'v8', 40 'vm', 41 'zlib', 42]; 43 44export const EXTERNAL_REQUIRE_POLYFILL = '.expo/metro/polyfill.js'; 45export const EXTERNAL_REQUIRE_NATIVE_POLYFILL = '.expo/metro/polyfill.native.js'; 46export const METRO_EXTERNALS_FOLDER = '.expo/metro/externals'; 47 48export function getNodeExternalModuleId(fromModule: string, moduleId: string) { 49 return path.relative( 50 path.dirname(fromModule), 51 path.join(METRO_EXTERNALS_FOLDER, moduleId, 'index.js') 52 ); 53} 54 55export async function setupNodeExternals(projectRoot: string) { 56 await tapExternalRequirePolyfill(projectRoot); 57 await tapNodeShims(projectRoot); 58} 59 60async function tapExternalRequirePolyfill(projectRoot: string) { 61 await fs.promises.mkdir(path.join(projectRoot, path.dirname(EXTERNAL_REQUIRE_POLYFILL)), { 62 recursive: true, 63 }); 64 await fs.promises.writeFile( 65 path.join(projectRoot, EXTERNAL_REQUIRE_POLYFILL), 66 'global.$$require_external = typeof window === "undefined" ? require : () => null;' 67 ); 68 await fs.promises.writeFile( 69 path.join(projectRoot, EXTERNAL_REQUIRE_NATIVE_POLYFILL), 70 'global.$$require_external = (moduleId) => {throw new Error(`Node.js standard library module ${moduleId} is not available in this JavaScript environment`);}' 71 ); 72} 73 74export function isNodeExternal(moduleName: string): string | null { 75 const moduleId = moduleName.replace(/^node:/, ''); 76 if (NODE_STDLIB_MODULES.includes(moduleId)) { 77 return moduleId; 78 } 79 return null; 80} 81 82function tapNodeShimContents(moduleId: string): string { 83 return `module.exports = $$require_external('node:${moduleId}');`; 84} 85 86// Ensure Node.js shims which require using `$$require_external` are available inside the project. 87async function tapNodeShims(projectRoot: string) { 88 const externals: Record<string, string> = {}; 89 for (const moduleId of NODE_STDLIB_MODULES) { 90 const shimDir = path.join(projectRoot, METRO_EXTERNALS_FOLDER, moduleId); 91 const shimPath = path.join(shimDir, 'index.js'); 92 externals[moduleId] = shimPath; 93 94 if (!fs.existsSync(shimPath)) { 95 await fs.promises.mkdir(shimDir, { recursive: true }); 96 await fs.promises.writeFile(shimPath, tapNodeShimContents(moduleId)); 97 } 98 } 99} 100