• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayPrototypeForEach,
5  ArrayPrototypeJoin,
6  ObjectDefineProperty,
7  ObjectPrototypeHasOwnProperty,
8  SafeMap,
9  SafeSet,
10  StringPrototypeCharCodeAt,
11  StringPrototypeIncludes,
12  StringPrototypeSlice,
13  StringPrototypeStartsWith,
14} = primordials;
15const {
16  ERR_MANIFEST_DEPENDENCY_MISSING,
17  ERR_UNKNOWN_BUILTIN_MODULE
18} = require('internal/errors').codes;
19const { NativeModule } = require('internal/bootstrap/loaders');
20
21const { validateString } = require('internal/validators');
22const path = require('path');
23const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
24
25const { getOptionValue } = require('internal/options');
26const userConditions = getOptionValue('--conditions');
27
28let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
29  debug = fn;
30});
31
32const noAddons = getOptionValue('--no-addons');
33const addonConditions = noAddons ? [] : ['node-addons'];
34
35// TODO: Use this set when resolving pkg#exports conditions in loader.js.
36const cjsConditions = new SafeSet([
37  'require',
38  'node',
39  ...addonConditions,
40  ...userConditions,
41]);
42
43function loadNativeModule(filename, request) {
44  const mod = NativeModule.map.get(filename);
45  if (mod?.canBeRequiredByUsers) {
46    debug('load native module %s', request);
47    // compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
48    mod.compileForPublicLoader();
49    return mod;
50  }
51}
52
53// Invoke with makeRequireFunction(module) where |module| is the Module object
54// to use as the context for the require() function.
55// Use redirects to set up a mapping from a policy and restrict dependencies
56const urlToFileCache = new SafeMap();
57function makeRequireFunction(mod, redirects) {
58  const Module = mod.constructor;
59
60  let require;
61  if (redirects) {
62    const id = mod.filename || mod.id;
63    const conditions = cjsConditions;
64    const { resolve, reaction } = redirects;
65    require = function require(specifier) {
66      let missing = true;
67      const destination = resolve(specifier, conditions);
68      if (destination === true) {
69        missing = false;
70      } else if (destination) {
71        const href = destination.href;
72        if (destination.protocol === 'node:') {
73          const specifier = destination.pathname;
74          const mod = loadNativeModule(specifier, href);
75          if (mod && mod.canBeRequiredByUsers) {
76            return mod.exports;
77          }
78          throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier);
79        } else if (destination.protocol === 'file:') {
80          let filepath;
81          if (urlToFileCache.has(href)) {
82            filepath = urlToFileCache.get(href);
83          } else {
84            filepath = fileURLToPath(destination);
85            urlToFileCache.set(href, filepath);
86          }
87          return mod.require(filepath);
88        }
89      }
90      if (missing) {
91        reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(
92          id,
93          specifier,
94          ArrayPrototypeJoin([...conditions], ', ')
95        ));
96      }
97      return mod.require(specifier);
98    };
99  } else {
100    require = function require(path) {
101      return mod.require(path);
102    };
103  }
104
105  function resolve(request, options) {
106    validateString(request, 'request');
107    return Module._resolveFilename(request, mod, false, options);
108  }
109
110  require.resolve = resolve;
111
112  function paths(request) {
113    validateString(request, 'request');
114    return Module._resolveLookupPaths(request, mod);
115  }
116
117  resolve.paths = paths;
118
119  require.main = process.mainModule;
120
121  // Enable support to add extra extension types.
122  require.extensions = Module._extensions;
123
124  require.cache = Module._cache;
125
126  return require;
127}
128
129/**
130 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
131 * because the buffer-to-string conversion in `fs.readFileSync()`
132 * translates it to FEFF, the UTF-16 BOM.
133 */
134function stripBOM(content) {
135  if (StringPrototypeCharCodeAt(content) === 0xFEFF) {
136    content = StringPrototypeSlice(content, 1);
137  }
138  return content;
139}
140
141function addBuiltinLibsToObject(object) {
142  // Make built-in modules available directly (loaded lazily).
143  const { builtinModules } = require('internal/modules/cjs/loader').Module;
144  ArrayPrototypeForEach(builtinModules, (name) => {
145    // Neither add underscored modules, nor ones that contain slashes (e.g.,
146    // 'fs/promises') or ones that are already defined.
147    if (StringPrototypeStartsWith(name, '_') ||
148        StringPrototypeIncludes(name, '/') ||
149        ObjectPrototypeHasOwnProperty(object, name)) {
150      return;
151    }
152    // Goals of this mechanism are:
153    // - Lazy loading of built-in modules
154    // - Having all built-in modules available as non-enumerable properties
155    // - Allowing the user to re-assign these variables as if there were no
156    //   pre-existing globals with the same name.
157
158    const setReal = (val) => {
159      // Deleting the property before re-assigning it disables the
160      // getter/setter mechanism.
161      delete object[name];
162      object[name] = val;
163    };
164
165    ObjectDefineProperty(object, name, {
166      get: () => {
167        const lib = require(name);
168
169        // Disable the current getter/setter and set up a new
170        // non-enumerable property.
171        delete object[name];
172        ObjectDefineProperty(object, name, {
173          get: () => lib,
174          set: setReal,
175          configurable: true,
176          enumerable: false
177        });
178
179        return lib;
180      },
181      set: setReal,
182      configurable: true,
183      enumerable: false
184    });
185  });
186}
187
188function normalizeReferrerURL(referrer) {
189  if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
190    return pathToFileURL(referrer).href;
191  }
192  return new URL(referrer).href;
193}
194
195module.exports = {
196  addBuiltinLibsToObject,
197  cjsConditions,
198  loadNativeModule,
199  makeRequireFunction,
200  normalizeReferrerURL,
201  stripBOM,
202};
203