1'use strict'; 2 3const { 4 ArrayPrototypePush, 5 RegExpPrototypeExec, 6 decodeURIComponent, 7} = primordials; 8 9const { defaultGetFormat } = require('internal/modules/esm/get_format'); 10const { validateAssertions } = require('internal/modules/esm/assert'); 11const { getOptionValue } = require('internal/options'); 12const { fetchModule } = require('internal/modules/esm/fetch_module'); 13 14// Do not eagerly grab .manifest, it may be in TDZ 15const policy = getOptionValue('--experimental-policy') ? 16 require('internal/process/policy') : 17 null; 18const experimentalNetworkImports = 19 getOptionValue('--experimental-network-imports'); 20 21const { Buffer: { from: BufferFrom } } = require('buffer'); 22 23const { readFile: readFileAsync } = require('internal/fs/promises').exports; 24const { URL } = require('internal/url'); 25const { 26 ERR_INVALID_URL, 27 ERR_UNSUPPORTED_ESM_URL_SCHEME, 28} = require('internal/errors').codes; 29 30const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/; 31 32/** 33 * @param {URL} url URL to the module 34 * @param {ESModuleContext} context used to decorate error messages 35 * @returns {{ responseURL: string, source: string | BufferView }} 36 */ 37async function getSource(url, context) { 38 const { protocol, href } = url; 39 let responseURL = href; 40 let source; 41 if (protocol === 'file:') { 42 source = await readFileAsync(url); 43 } else if (protocol === 'data:') { 44 const match = RegExpPrototypeExec(DATA_URL_PATTERN, url.pathname); 45 if (!match) { 46 throw new ERR_INVALID_URL(responseURL); 47 } 48 const { 1: base64, 2: body } = match; 49 source = BufferFrom(decodeURIComponent(body), base64 ? 'base64' : 'utf8'); 50 } else if (experimentalNetworkImports && ( 51 protocol === 'https:' || 52 protocol === 'http:' 53 )) { 54 const res = await fetchModule(url, context); 55 source = await res.body; 56 responseURL = res.resolvedHREF; 57 } else { 58 const supportedSchemes = ['file', 'data']; 59 if (experimentalNetworkImports) { 60 ArrayPrototypePush(supportedSchemes, 'http', 'https'); 61 } 62 throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url, supportedSchemes); 63 } 64 if (policy?.manifest) { 65 policy.manifest.assertIntegrity(href, source); 66 } 67 return { __proto__: null, responseURL, source }; 68} 69 70 71/** 72 * Node.js default load hook. 73 * @param {string} url 74 * @param {object} context 75 * @returns {object} 76 */ 77async function defaultLoad(url, context) { 78 let responseURL = url; 79 const { importAssertions } = context; 80 let { 81 format, 82 source, 83 } = context; 84 85 const urlInstance = new URL(url); 86 87 throwIfUnsupportedURLScheme(urlInstance, experimentalNetworkImports); 88 89 format ??= await defaultGetFormat(urlInstance, context); 90 91 validateAssertions(url, format, importAssertions); 92 93 if ( 94 format === 'builtin' || 95 format === 'commonjs' 96 ) { 97 source = null; 98 } else if (source == null) { 99 ({ responseURL, source } = await getSource(urlInstance, context)); 100 } 101 102 return { 103 __proto__: null, 104 format, 105 responseURL, 106 source, 107 }; 108} 109 110/** 111 * throws an error if the protocol is not one of the protocols 112 * that can be loaded in the default loader 113 * @param {URL} parsed 114 * @param {boolean} experimentalNetworkImports 115 */ 116function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) { 117 // Avoid accessing the `protocol` property due to the lazy getters. 118 const protocol = parsed?.protocol; 119 if ( 120 protocol && 121 protocol !== 'file:' && 122 protocol !== 'data:' && 123 protocol !== 'node:' && 124 ( 125 !experimentalNetworkImports || 126 ( 127 protocol !== 'https:' && 128 protocol !== 'http:' 129 ) 130 ) 131 ) { 132 const schemes = ['file', 'data', 'node']; 133 if (experimentalNetworkImports) { 134 ArrayPrototypePush(schemes, 'https', 'http'); 135 } 136 throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes); 137 } 138} 139 140module.exports = { 141 defaultLoad, 142}; 143