diff --git a/README.md b/README.md index ffb4024..a9b3321 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,8 @@ Options include: imports, resolutions, builtins, - conditions + conditions, + attributes } ``` diff --git a/index.js b/index.js index 42d63e9..96507c5 100644 --- a/index.js +++ b/index.js @@ -112,6 +112,10 @@ const Module = module.exports = exports = class Module { Module._modules.delete(this) } + _run () { + binding.runModule(this._handle, Module._handle, Module._onrun) + } + _transform (isImport, isDynamicImport) { if (isDynamicImport) { this._synthesize() @@ -183,10 +187,6 @@ const Module = module.exports = exports = class Module { this._handle = binding.createSyntheticModule(this._url.href, this._names, Module._handle) } - _run () { - binding.runModule(this._handle, Module._handle, Module._onrun) - } - _evaluate (eagerRun = false) { if ((this._state & constants.states.EVALUATED) !== 0) return @@ -208,16 +208,12 @@ const Module = module.exports = exports = class Module { ) if (eagerRun) this._run() - } - - if (this._type === constants.types.MODULE) { + } else if (this._type === constants.types.MODULE) { this._run() this._exports = binding.getNamespace(this._handle) - } - - if (this._type === constants.types.ADDON) { - if (eagerRun) this._run() + } else if (eagerRun) { + this._run() } } @@ -245,7 +241,7 @@ const Module = module.exports = exports = class Module { static _handle = binding.init(this, this._onimport, this._onevaluate, this._onmeta) - static _onimport (href, assertions, referrerHref, isDynamicImport) { + static _onimport (href, attributes, referrerHref, isDynamicImport) { const referrer = this._cache[referrerHref] || null if (referrer === null) { @@ -256,28 +252,9 @@ const Module = module.exports = exports = class Module { throw errors.MODULE_NOT_FOUND(msg) } - const url = this.resolve(href, referrer._url, { - isImport: true, - referrer - }) - - let type + const url = this.resolve(href, referrer._url, { isImport: true, referrer }) - switch (assertions.type) { - case 'module': - type = constants.types.MODULE - break - case 'json': - type = constants.types.JSON - break - } - - const module = this.load(url, { - isImport: true, - isDynamicImport, - referrer, - type - }) + const module = this.load(url, { isImport: true, isDynamicImport, referrer, attributes }) return module._handle } @@ -383,7 +360,8 @@ const Module = module.exports = exports = class Module { isDynamicImport = false, referrer = null, - type = 0, + attributes, + type = typeForAttributes(attributes), defaultType = referrer ? referrer._defaultType : 0, cache = referrer ? referrer._cache : self._cache, main = referrer ? referrer._main : null, @@ -414,10 +392,10 @@ const Module = module.exports = exports = class Module { module._builtins = builtins module._conditions = conditions - let extension = self._extensionFor(type) || path.extname(url.pathname) + let extension = extensionForType(type) || path.extname(url.pathname) if (extension in self._extensions === false) { - if (defaultType) extension = self._extensionFor(defaultType) || '.js' + if (defaultType) extension = extensionForType(defaultType) || '.js' else extension = '.js' } @@ -537,22 +515,41 @@ const Module = module.exports = exports = class Module { return null } } +} + +function extensionForType (type) { + switch (type) { + case constants.types.SCRIPT: + return '.cjs' + case constants.types.MODULE: + return '.esm' + case constants.types.JSON: + return '.json' + case constants.types.BUNDLE: + return '.bundle' + case constants.types.ADDON: + return '.bare' + default: + return null + } +} - static _extensionFor (type) { - switch (type) { - case constants.types.SCRIPT: - return '.cjs' - case constants.types.MODULE: - return '.esm' - case constants.types.JSON: - return '.json' - case constants.types.BUNDLE: - return '.bundle' - case constants.types.ADDON: - return '.bare' - default: - return null - } +function typeForAttributes (attributes) { + if (typeof attributes !== 'object' || attributes === null) return 0 + + switch (attributes.type) { + case 'script': + return constants.types.SCRIPT + case 'module': + return constants.types.MODULE + case 'json': + return constants.types.JSON + case 'bundle': + return constants.types.BUNDLE + case 'addon': + return constants.types.ADDON + default: + return 0 } } @@ -613,10 +610,10 @@ const createRequire = exports.createRequire = function createRequire (parentURL, return require - function require (specifier) { + function require (specifier, opts = {}) { const resolved = self.resolve(specifier, referrer._url, { referrer }) - const module = self.load(resolved, { referrer }) + const module = self.load(resolved, { referrer, attributes: opts.with }) return module._exports } diff --git a/test.js b/test.js index 8688920..267c6e3 100644 --- a/test.js +++ b/test.js @@ -1416,7 +1416,7 @@ test('import.meta', (t) => { t.comment(meta.addon.host) }) -test('import assertions', (t) => { +test('import attributes', (t) => { t.teardown(onteardown) const protocol = new Module.Protocol({ @@ -1440,6 +1440,54 @@ test('import assertions', (t) => { t.alike(Module.load(new URL(root + '/foo.mjs'), { protocol }).exports.default, { hello: 'world' }) }) +test('dynamic import attributes', async (t) => { + t.teardown(onteardown) + + const protocol = new Module.Protocol({ + exists (url) { + return url.href === root + '/bar' + }, + + read (url) { + if (url.href === root + '/foo.mjs') { + return 'export default import(\'/bar\', { with: { type: \'json\' } })' + } + + if (url.href === root + '/bar') { + return '{ "hello": "world" }' + } + + t.fail() + } + }) + + t.comment(await Module.load(new URL(root + '/foo.mjs'), { protocol }).exports.default) +}) + +test('require attributes', (t) => { + t.teardown(onteardown) + + const protocol = new Module.Protocol({ + exists (url) { + return url.href === root + '/bar' + }, + + read (url) { + if (url.href === root + '/foo.js') { + return 'module.exports = require(\'/bar\', { with: { type: \'json\' } })' + } + + if (url.href === root + '/bar') { + return '{ "hello": "world" }' + } + + t.fail() + } + }) + + t.alike(Module.load(new URL(root + '/foo.js'), { protocol }).exports, { hello: 'world' }) +}) + test('createRequire', (t) => { t.teardown(onteardown)