• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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