Skip to content

Commit

Permalink
esm: consolidate ESM loader hooks
Browse files Browse the repository at this point in the history
doc: update ESM hook examples

esm: fix unsafe primordial

doc: fix ESM example linting

esm: allow source of type ArrayBuffer

doc: update ESM hook changelog to include resolve format

esm: allow all ArrayBuffers and TypedArrays for load hook source

doc: tidy code & API docs

doc: convert ESM source table header from Title Case to Sentence case

doc: add detailed explanation for getPackageType

esm: add caveat that ESMLoader::import() must NOT be renamed

esm: tidy code declaration of getFormat protocolHandlers

doc: correct ESM doc link (bad conflict resolution)

doc: update ESM hook limitation for CJS

esm: tweak preload description

doc: update ESM getPackageType() example explanation

PR-URL: #37468
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Geoffrey Booth <webmaster@geoffreybooth.com>
  • Loading branch information
JakobJingleheimer authored and danielleadams committed Oct 12, 2021
1 parent 1ef2cf8 commit 3743406
Show file tree
Hide file tree
Showing 46 changed files with 971 additions and 545 deletions.
312 changes: 163 additions & 149 deletions doc/api/esm.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions lib/internal/modules/cjs/loader.js
Expand Up @@ -981,14 +981,14 @@ Module.prototype.load = function(filename) {
Module._extensions[extension](this, filename);
this.loaded = true;

const ESMLoader = asyncESM.ESMLoader;
const esmLoader = asyncESM.esmLoader;
// Create module entry at load time to snapshot exports correctly
const exports = this.exports;
// Preemptively cache
if ((module?.module === undefined ||
module.module.getStatus() < kEvaluated) &&
!ESMLoader.cjsCache.has(this))
ESMLoader.cjsCache.set(this, exports);
!esmLoader.cjsCache.has(this))
esmLoader.cjsCache.set(this, exports);
};


Expand Down Expand Up @@ -1022,7 +1022,7 @@ function wrapSafe(filename, content, cjsModuleInstance) {
lineOffset: 0,
displayErrors: true,
importModuleDynamically: async (specifier) => {
const loader = asyncESM.ESMLoader;
const loader = asyncESM.esmLoader;
return loader.import(specifier, normalizeReferrerURL(filename));
},
});
Expand All @@ -1037,7 +1037,7 @@ function wrapSafe(filename, content, cjsModuleInstance) {
], {
filename,
importModuleDynamically(specifier) {
const loader = asyncESM.ESMLoader;
const loader = asyncESM.esmLoader;
return loader.import(specifier, normalizeReferrerURL(filename));
},
});
Expand Down
36 changes: 23 additions & 13 deletions lib/internal/modules/esm/get_format.js
@@ -1,7 +1,9 @@
'use strict';
const {
ObjectAssign,
ObjectCreate,
ObjectPrototypeHasOwnProperty,
RegExpPrototypeExec,
StringPrototypeStartsWith,
} = primordials;
const { extname } = require('path');
const { getOptionValue } = require('internal/options');
Expand Down Expand Up @@ -36,26 +38,25 @@ if (experimentalWasmModules)
if (experimentalJsonModules)
extensionFormatMap['.json'] = legacyExtensionFormatMap['.json'] = 'json';

function defaultGetFormat(url, context, defaultGetFormatUnused) {
if (StringPrototypeStartsWith(url, 'node:')) {
return { format: 'builtin' };
}
const parsed = new URL(url);
if (parsed.protocol === 'data:') {
const protocolHandlers = ObjectAssign(ObjectCreate(null), {
'data:'(parsed) {
const { 1: mime } = RegExpPrototypeExec(
/^([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/,
parsed.pathname,
) || [ , null ];
) || [, null];
const format = ({
'__proto__': null,
'text/javascript': 'module',
'application/json': experimentalJsonModules ? 'json' : null,
'application/wasm': experimentalWasmModules ? 'wasm' : null
})[mime] || null;
return { format };
} else if (parsed.protocol === 'file:') {

return format;
},
'file:'(parsed, url) {
const ext = extname(parsed.pathname);
let format;

if (ext === '.js') {
format = getPackageType(parsed.href) === 'module' ? 'module' : 'commonjs';
} else {
Expand All @@ -71,9 +72,18 @@ function defaultGetFormat(url, context, defaultGetFormatUnused) {
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, fileURLToPath(url));
}
}
return { format: format || null };
}
return { format: null };

return format || null;
},
'node:'() { return 'builtin'; },
});

function defaultGetFormat(url, context) {
const parsed = new URL(url);

return ObjectPrototypeHasOwnProperty(protocolHandlers, parsed.protocol) ?
protocolHandlers[parsed.protocol](parsed, url) :
null;
}

module.exports = {
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/modules/esm/get_source.js
Expand Up @@ -40,6 +40,6 @@ async function defaultGetSource(url, { format } = {}, defaultGetSource) {
if (policy?.manifest) {
policy.manifest.assertIntegrity(parsed, source);
}
return { source };
return source;
}
exports.defaultGetSource = defaultGetSource;
32 changes: 32 additions & 0 deletions lib/internal/modules/esm/load.js
@@ -0,0 +1,32 @@
'use strict';

const { defaultGetFormat } = require('internal/modules/esm/get_format');
const { defaultGetSource } = require('internal/modules/esm/get_source');
const { translators } = require('internal/modules/esm/translators');

async function defaultLoad(url, context) {
let {
format,
source,
} = context;

if (!translators.has(format)) format = defaultGetFormat(url);

if (
format === 'builtin' ||
format === 'commonjs'
) {
source = null;
} else if (source == null) {
source = await defaultGetSource(url, { format });
}

return {
format,
source,
};
}

module.exports = {
defaultLoad,
};

0 comments on commit 3743406

Please sign in to comment.