Skip to content

Commit 66a1503

Browse files
committed
loader: introduce --experimental-raw-imports flag and implementation
1 parent 3db2206 commit 66a1503

File tree

6 files changed

+87
-6
lines changed

6 files changed

+87
-6
lines changed

lib/internal/modules/esm/assert.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
ObjectValues,
99
} = primordials;
1010
const { validateString } = require('internal/validators');
11+
const { getOptionValue } = require('internal/options');
1112

1213
const {
1314
ERR_IMPORT_ATTRIBUTE_TYPE_INCOMPATIBLE,
@@ -32,6 +33,12 @@ const formatTypeMap = {
3233
'wasm': kImplicitTypeAttribute, // It's unclear whether the HTML spec will require an type attribute or not for Wasm; see https://github.com/WebAssembly/esm-integration/issues/42
3334
};
3435

36+
const experimentalFormatTypeMap = {
37+
'__proto__': null,
38+
'bytes': 'bytes',
39+
'text': 'text',
40+
};
41+
3542
/**
3643
* The HTML spec disallows the default type to be explicitly specified
3744
* (for now); so `import './file.js'` is okay but
@@ -42,6 +49,17 @@ const supportedTypeAttributes = ArrayPrototypeFilter(
4249
ObjectValues(formatTypeMap),
4350
(type) => type !== kImplicitTypeAttribute);
4451

52+
function getExperimentalFormatType(format) {
53+
const type = experimentalFormatTypeMap[format];
54+
if (type === undefined) {
55+
return undefined;
56+
}
57+
if (!getOptionValue('--experimental-raw-imports')) {
58+
return undefined;
59+
}
60+
return type;
61+
}
62+
4563

4664
/**
4765
* Test a module's import attributes.
@@ -60,7 +78,7 @@ function validateAttributes(url, format,
6078
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]], url);
6179
}
6280
}
63-
const validType = formatTypeMap[format];
81+
const validType = formatTypeMap[format] ?? getExperimentalFormatType(format);
6482

6583
switch (validType) {
6684
case undefined:
@@ -101,7 +119,9 @@ function handleInvalidType(url, type) {
101119
validateString(type, 'type');
102120

103121
// `type` might not have been one of the types we understand.
104-
if (!ArrayPrototypeIncludes(supportedTypeAttributes, type)) {
122+
if (!ArrayPrototypeIncludes(supportedTypeAttributes, type) &&
123+
!(ObjectPrototypeHasOwnProperty(experimentalFormatTypeMap, type) &&
124+
getOptionValue('--experimental-raw-imports'))) {
105125
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED('type', type, url);
106126
}
107127

lib/internal/modules/esm/get_format.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@ const protocolHandlers = {
7878
'node:'() { return 'builtin'; },
7979
};
8080

81+
function getFormatFromImportAttributes(importAttributes) {
82+
if (
83+
!getOptionValue('--experimental-raw-imports') ||
84+
!importAttributes ||
85+
!ObjectPrototypeHasOwnProperty(importAttributes, 'type') ||
86+
typeof importAttributes.type !== 'string'
87+
) {
88+
return undefined;
89+
}
90+
91+
switch (importAttributes.type) {
92+
case 'text':
93+
case 'bytes':
94+
return importAttributes.type;
95+
default:
96+
return undefined;
97+
}
98+
}
99+
81100
/**
82101
* Determine whether the given ambiguous source contains CommonJS or ES module syntax.
83102
* @param {string | Buffer | undefined} [source]
@@ -238,10 +257,15 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
238257

239258
/**
240259
* @param {URL} url
241-
* @param {{parentURL: string}} context
260+
* @param {{parentURL: string, importAttributes?: Record<string, string>}} context
242261
* @returns {Promise<string> | string | undefined} only works when enabled
243262
*/
244263
function defaultGetFormatWithoutErrors(url, context) {
264+
const format = getFormatFromImportAttributes(context?.importAttributes);
265+
if (format !== undefined) {
266+
return format;
267+
}
268+
245269
const protocol = url.protocol;
246270
if (!ObjectPrototypeHasOwnProperty(protocolHandlers, protocol)) {
247271
return null;
@@ -251,10 +275,15 @@ function defaultGetFormatWithoutErrors(url, context) {
251275

252276
/**
253277
* @param {URL} url
254-
* @param {{parentURL: string}} context
278+
* @param {{parentURL: string, importAttributes?: Record<string, string>}} context
255279
* @returns {Promise<string> | string | undefined} only works when enabled
256280
*/
257281
function defaultGetFormat(url, context) {
282+
const format = getFormatFromImportAttributes(context?.importAttributes);
283+
if (format !== undefined) {
284+
return format;
285+
}
286+
258287
const protocol = url.protocol;
259288
if (!ObjectPrototypeHasOwnProperty(protocolHandlers, protocol)) {
260289
return null;

lib/internal/modules/esm/load.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ function defaultLoad(url, context = kEmptyObject) {
9191

9292
if (format == null) {
9393
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
94-
format = defaultGetFormat(urlInstance, context);
94+
format = defaultGetFormat(urlInstance, {
95+
__proto__: context,
96+
importAttributes,
97+
});
9598

9699
if (format === 'commonjs') {
97100
// For backward compatibility reasons, we need to discard the source in
@@ -155,7 +158,10 @@ function defaultLoadSync(url, context = kEmptyObject) {
155158
}
156159

157160
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
158-
format ??= defaultGetFormat(urlInstance, context);
161+
format ??= defaultGetFormat(urlInstance, {
162+
__proto__: context,
163+
importAttributes,
164+
});
159165

160166
// For backward compatibility reasons, we need to let go through Module._load
161167
// again.

lib/internal/modules/esm/translators.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,23 @@ translators.set('module-typescript', function(url, translateContext, parentURL)
642642
translateContext.source = stripTypeScriptModuleTypes(stringify(source), url);
643643
return FunctionPrototypeCall(translators.get('module'), this, url, translateContext, parentURL);
644644
});
645+
646+
// Strategy for loading source as text.
647+
translators.set('text', function textStrategy(url, translateContext) {
648+
let { source } = translateContext;
649+
assertBufferSource(source, true, 'load');
650+
source = stringify(source);
651+
return new ModuleWrap(url, undefined, ['default'], function() {
652+
this.setExport('default', source);
653+
});
654+
});
655+
656+
// Strategy for loading source as raw bytes.
657+
translators.set('bytes', function bytesStrategy(url, translateContext) {
658+
let { source } = translateContext;
659+
assertBufferSource(source, false, 'load');
660+
source = new Uint8Array(Buffer.from(source));
661+
return new ModuleWrap(url, undefined, ['default'], function() {
662+
this.setExport('default', source);
663+
});
664+
});

src/node_options.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
632632
AddAlias("--loader", "--experimental-loader");
633633
AddOption("--experimental-modules", "", NoOp{}, kAllowedInEnvvar);
634634
AddOption("--experimental-wasm-modules", "", NoOp{}, kAllowedInEnvvar);
635+
AddOption("--experimental-raw-imports",
636+
"experimental support for raw source imports with import "
637+
"attributes",
638+
&EnvironmentOptions::experimental_raw_imports,
639+
kAllowedInEnvvar);
635640
AddOption("--experimental-import-meta-resolve",
636641
"experimental ES Module import.meta.resolve() parentURL support",
637642
&EnvironmentOptions::experimental_import_meta_resolve,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class EnvironmentOptions : public Options {
134134
std::string localstorage_file;
135135
bool experimental_global_navigator = true;
136136
bool experimental_global_web_crypto = true;
137+
bool experimental_raw_imports = false;
137138
bool experimental_import_meta_resolve = false;
138139
std::string input_type; // Value of --input-type
139140
bool entry_is_url = false;

0 commit comments

Comments
 (0)