Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esm: remove CLI flag limitation to programmatic registration #48439

Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,22 +151,28 @@ class DefaultModuleLoader {
return this.getJobFromResolveResult(resolveResult, parentURL, importAssertions);
}

getJobFromResolveResult(
async getJobFromResolveResult(
resolveResult,
parentURL,
importAssertions = resolveResult.importAssertions,
) {
const { url, format } = resolveResult;
debug('getJobFromResolveResult:', { [url]: { format, moduleMap: this.moduleMap } });

// `job` may be a Promise, which the engine INSANELY _sometimes_ considers undefined and
// sometimes not. This is why we use `has` instead of `get` to check for its existence.
// ! Do NOT try to check against its value; the engine will **gladly** screw you over.
if (!this.moduleMap.has(url, importAssertions.type)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm confused why a map's "has" function would ever take more than one argument. is moduleMap something that's not a Map?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I also had to ask what this is.

The extra argument (import assertion type) is per spec required for matching cache. Under the hood, the extra argument is stored as part of the value (or maybe the key?).

I'll add some code doc for it after I get this stable/passing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s very confusing to have something called “map” whose method names violate the expected interface contract for a Map, but i assume that predates this PR. (iow, i think instead of using has/set/etc it should have brand new method names)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this PR does not introduce ModuleMap ;)

debug('getJobFromResolveResult: did NOT find existing entry for %s', url)
return this.#createModuleJob(url, parentURL, importAssertions, format);
}

let job = this.moduleMap.get(url, importAssertions.type);
debug('getJobFromResolveResult: found existing entry for %s %o', url, job);

// CommonJS will set functions for lazy job evaluation.
if (typeof job === 'function') {
if (typeof job === 'function') { // CommonJS will set functions for lazy job evaluation.
this.moduleMap.set(url, undefined, job = job());
JakobJingleheimer marked this conversation as resolved.
Show resolved Hide resolved
}

job ??= this.#createModuleJob(url, parentURL, importAssertions, format);

return job;
}

Expand All @@ -189,6 +195,10 @@ debug('getJobFromResolveResult:', { [url]: { format, moduleMap: this.moduleMap }
process.send({ 'watch:import': [url] });
}

debug('#createModuleJob (before load)', { url, parentURL, importAssertions, format })

// ! The module needing creation is not added to ModuleMap before the next `getModuleJob()`,
// ! which is a problem.
const {
format: finalFormat,
responseURL,
Expand All @@ -205,18 +215,20 @@ debug('getJobFromResolveResult:', { [url]: { format, moduleMap: this.moduleMap }
}

const isMain = parentURL === undefined;
debug('#createModuleJob', { url, finalFormat, responseURL, source });
debug('#createModuleJob (loaded, untranslated) %o', { url, finalFormat, responseURL, source });
const moduleWrapper = FunctionPrototypeCall(translator, this, responseURL, source, isMain);

const ModuleJob = require('internal/modules/esm/module_job');
const job = new ModuleJob(
responseURL,
url, // TODO: should this be resolvedURL?
moduleWrapper,
importAssertions,
isMain,
inspectBrk,
);

debug('#createModuleJob (translated) %o', { url, job });

this.moduleMap.set(url, importAssertions.type, job);

return job;
Expand Down
8 changes: 6 additions & 2 deletions lib/internal/modules/esm/module_job.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,13 @@ class ModuleJob {
const dependencyJobs = [];
debug('this.module.link: %o', { [url]: this.module.link.toString() });
const promises = this.module.link(async (specifier, assertions) => {
debug('this.module.link(%o)', { specifier, assertions });
const moduleLoader = require('internal/process/esm_loader').esmLoader;
anonrig marked this conversation as resolved.
Show resolved Hide resolved
debug('this.module.link() moduleLoader: %s', moduleLoader.constructor.name);
debug('this.module.link(%o)', {
specifier,
parentURL: url,
assertions,
moduleLoader: moduleLoader.constructor.name
});
const job = await moduleLoader.getModuleJob(specifier, url, assertions);
debug('this.module.link() job: %o', job);
ArrayPrototypePush(dependencyJobs, job);
Expand Down