• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayPrototypeForEach,
5  ArrayPrototypeJoin,
6  ArrayPrototypeSome,
7  ObjectDefineProperty,
8  ObjectPrototypeHasOwnProperty,
9  SafeMap,
10  SafeSet,
11  StringPrototypeCharCodeAt,
12  StringPrototypeIncludes,
13  StringPrototypeSlice,
14  StringPrototypeStartsWith,
15} = primordials;
16const {
17  ERR_INVALID_ARG_TYPE,
18  ERR_MANIFEST_DEPENDENCY_MISSING,
19  ERR_UNKNOWN_BUILTIN_MODULE,
20} = require('internal/errors').codes;
21const { BuiltinModule } = require('internal/bootstrap/loaders');
22
23const { validateString } = require('internal/validators');
24const path = require('path');
25const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
26
27const { getOptionValue } = require('internal/options');
28const { setOwnProperty } = require('internal/util');
29const userConditions = getOptionValue('--conditions');
30
31const {
32  privateSymbols: {
33    require_private_symbol,
34  },
35} = internalBinding('util');
36
37let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
38  debug = fn;
39});
40
41const noAddons = getOptionValue('--no-addons');
42const addonConditions = noAddons ? [] : ['node-addons'];
43
44// TODO: Use this set when resolving pkg#exports conditions in loader.js.
45const cjsConditions = new SafeSet([
46  'require',
47  'node',
48  ...addonConditions,
49  ...userConditions,
50]);
51
52function loadBuiltinModule(filename, request) {
53  const mod = BuiltinModule.map.get(filename);
54  if (mod?.canBeRequiredByUsers) {
55    debug('load built-in module %s', request);
56    // compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
57    mod.compileForPublicLoader();
58    return mod;
59  }
60}
61
62let $Module = null;
63function lazyModule() {
64  $Module = $Module || require('internal/modules/cjs/loader').Module;
65  return $Module;
66}
67
68// Invoke with makeRequireFunction(module) where |module| is the Module object
69// to use as the context for the require() function.
70// Use redirects to set up a mapping from a policy and restrict dependencies
71const urlToFileCache = new SafeMap();
72function makeRequireFunction(mod, redirects) {
73  // lazy due to cycle
74  const Module = lazyModule();
75  if (mod instanceof Module !== true) {
76    throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod);
77  }
78
79  let require;
80  if (redirects) {
81    const id = mod.filename || mod.id;
82    const conditions = cjsConditions;
83    const { resolve, reaction } = redirects;
84    require = function require(specifier) {
85      let missing = true;
86      const destination = resolve(specifier, conditions);
87      if (destination === true) {
88        missing = false;
89      } else if (destination) {
90        const { href, protocol } = destination;
91        if (protocol === 'node:') {
92          const specifier = destination.pathname;
93          const mod = loadBuiltinModule(specifier, href);
94          if (mod && mod.canBeRequiredByUsers) {
95            return mod.exports;
96          }
97          throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier);
98        } else if (protocol === 'file:') {
99          let filepath = urlToFileCache.get(href);
100          if (!filepath) {
101            filepath = fileURLToPath(destination);
102            urlToFileCache.set(href, filepath);
103          }
104          return mod[require_private_symbol](mod, filepath);
105        }
106      }
107      if (missing) {
108        reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(
109          id,
110          specifier,
111          ArrayPrototypeJoin([...conditions], ', '),
112        ));
113      }
114      return mod[require_private_symbol](mod, specifier);
115    };
116  } else {
117    require = function require(path) {
118      // When no policy manifest, the original prototype.require is sustained
119      return mod.require(path);
120    };
121  }
122
123  function resolve(request, options) {
124    validateString(request, 'request');
125    return Module._resolveFilename(request, mod, false, options);
126  }
127
128  require.resolve = resolve;
129
130  function paths(request) {
131    validateString(request, 'request');
132    return Module._resolveLookupPaths(request, mod);
133  }
134
135  resolve.paths = paths;
136
137  setOwnProperty(require, 'main', process.mainModule);
138
139  // Enable support to add extra extension types.
140  require.extensions = Module._extensions;
141
142  require.cache = Module._cache;
143
144  return require;
145}
146
147/**
148 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
149 * because the buffer-to-string conversion in `fs.readFileSync()`
150 * translates it to FEFF, the UTF-16 BOM.
151 */
152function stripBOM(content) {
153  if (StringPrototypeCharCodeAt(content) === 0xFEFF) {
154    content = StringPrototypeSlice(content, 1);
155  }
156  return content;
157}
158
159function addBuiltinLibsToObject(object, dummyModuleName) {
160  // Make built-in modules available directly (loaded lazily).
161  const Module = require('internal/modules/cjs/loader').Module;
162  const { builtinModules } = Module;
163
164  // To require built-in modules in user-land and ignore modules whose
165  // `canBeRequiredByUsers` is false. So we create a dummy module object and not
166  // use `require()` directly.
167  const dummyModule = new Module(dummyModuleName);
168
169  ArrayPrototypeForEach(builtinModules, (name) => {
170    // Neither add underscored modules, nor ones that contain slashes (e.g.,
171    // 'fs/promises') or ones that are already defined.
172    if (StringPrototypeStartsWith(name, '_') ||
173        StringPrototypeIncludes(name, '/') ||
174        ObjectPrototypeHasOwnProperty(object, name)) {
175      return;
176    }
177    // Goals of this mechanism are:
178    // - Lazy loading of built-in modules
179    // - Having all built-in modules available as non-enumerable properties
180    // - Allowing the user to re-assign these variables as if there were no
181    //   pre-existing globals with the same name.
182
183    const setReal = (val) => {
184      // Deleting the property before re-assigning it disables the
185      // getter/setter mechanism.
186      delete object[name];
187      object[name] = val;
188    };
189
190    ObjectDefineProperty(object, name, {
191      __proto__: null,
192      get: () => {
193        const lib = dummyModule.require(name);
194
195        try {
196          // Override the current getter/setter and set up a new
197          // non-enumerable property.
198          ObjectDefineProperty(object, name, {
199            __proto__: null,
200            get: () => lib,
201            set: setReal,
202            configurable: true,
203            enumerable: false,
204          });
205        } catch {
206          // If the property is no longer configurable, ignore the error.
207        }
208
209        return lib;
210      },
211      set: setReal,
212      configurable: true,
213      enumerable: false,
214    });
215  });
216}
217
218/**
219 *
220 * @param {string | URL} referrer
221 * @returns {string}
222 */
223function normalizeReferrerURL(referrer) {
224  if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
225    return pathToFileURL(referrer).href;
226  }
227  return new URL(referrer).href;
228}
229
230// For error messages only - used to check if ESM syntax is in use.
231function hasEsmSyntax(code) {
232  debug('Checking for ESM syntax');
233  const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
234  let root;
235  try {
236    root = parser.parse(code, { sourceType: 'module', ecmaVersion: 'latest' });
237  } catch {
238    return false;
239  }
240
241  return ArrayPrototypeSome(root.body, (stmt) =>
242    stmt.type === 'ExportDefaultDeclaration' ||
243    stmt.type === 'ExportNamedDeclaration' ||
244    stmt.type === 'ImportDeclaration' ||
245    stmt.type === 'ExportAllDeclaration');
246}
247
248module.exports = {
249  addBuiltinLibsToObject,
250  cjsConditions,
251  hasEsmSyntax,
252  loadBuiltinModule,
253  makeRequireFunction,
254  normalizeReferrerURL,
255  stripBOM,
256};
257