Skip to content

Commit

Permalink
module: unflag import assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
aduh95 committed Sep 3, 2021
1 parent f26c2ce commit 38e330b
Show file tree
Hide file tree
Showing 16 changed files with 125 additions and 14 deletions.
8 changes: 8 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,14 @@ The JS execution context is not associated with a Node.js environment.
This may occur when Node.js is used as an embedded library and some hooks
for the JS engine are not set up properly.

<a id="ERR_FAILED_IMPORT_ASSERTION"></a>
### `ERR_FAILED_IMPORT_ASSERTION`
<!-- YAML
added: REPLACEME
-->

An import assertion has failed, preventing the specified module to be imported.

<a id="ERR_FALSY_VALUE_REJECTION"></a>
### `ERR_FALSY_VALUE_REJECTION`

Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,9 @@ E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
RangeError);
E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error);
E('ERR_EVENT_RECURSION', 'The event "%s" is already being dispatched', Error);
E('ERR_FAILED_IMPORT_ASSERTION', (request, key, expectedValue, actualValue) => {
return `Failed to load module "${request}", expected ${key} to be ${JSONStringify(expectedValue)}, got ${JSONStringify(actualValue)} instead`;
}, TypeError);
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
this.reason = reason;
return 'Promise was rejected with falsy value';
Expand Down
13 changes: 10 additions & 3 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
} = primordials;

const {
ERR_FAILED_IMPORT_ASSERTION,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_MODULE_SPECIFIER,
Expand Down Expand Up @@ -173,8 +174,8 @@ class Loader {
};
}

async import(specifier, parent) {
const job = await this.getModuleJob(specifier, parent);
async import(specifier, parent, assertions) {
const job = await this.getModuleJob(specifier, parent, assertions);
const { module } = await job.run();
return module.getNamespace();
}
Expand Down Expand Up @@ -238,9 +239,15 @@ class Loader {
});
}

async getModuleJob(specifier, parentURL) {
async getModuleJob(specifier, parentURL, import_assertions) {
const url = await this.resolve(specifier, parentURL);
const format = await this.getFormat(url);

if (import_assertions?.type === 'json' && format !== 'json') {
throw new ERR_FAILED_IMPORT_ASSERTION(
url, 'type', import_assertions.type, format);
}

let job = this.moduleMap.get(url);
// CommonJS will set functions for lazy job evaluation.
if (typeof job === 'function')
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/modules/esm/module_job.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ class ModuleJob {
// so that circular dependencies can't cause a deadlock by two of
// these `link` callbacks depending on each other.
const dependencyJobs = [];
const promises = this.module.link(async (specifier) => {
const jobPromise = this.loader.getModuleJob(specifier, url);
const promises = this.module.link(async (specifier, assertions) => {
const jobPromise = this.loader.getModuleJob(specifier, url, assertions);
ArrayPrototypePush(dependencyJobs, jobPromise);
const job = await jobPromise;
return job.modulePromise;
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/modules/esm/translators.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ function errPath(url) {
return url;
}

async function importModuleDynamically(specifier, { url }) {
return asyncESM.ESMLoader.import(specifier, url);
async function importModuleDynamically(specifier, { url }, assertions) {
return asyncESM.ESMLoader.import(specifier, url, assertions);
}

function createImportMetaResolve(defaultParentUrl) {
Expand Down
5 changes: 3 additions & 2 deletions lib/internal/process/esm_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ exports.initializeImportMetaObject = function(wrap, meta) {
}
};

exports.importModuleDynamicallyCallback = async function(wrap, specifier) {
exports.importModuleDynamicallyCallback =
async function importModuleDynamicallyCallback(wrap, specifier, assertions) {
const { callbackMap } = internalBinding('module_wrap');
if (callbackMap.has(wrap)) {
const { importModuleDynamically } = callbackMap.get(wrap);
if (importModuleDynamically !== undefined) {
return importModuleDynamically(
specifier, getModuleFromWrap(wrap) || wrap);
specifier, getModuleFromWrap(wrap) || wrap, assertions);
}
}
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
Expand Down
11 changes: 11 additions & 0 deletions src/module_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,20 @@ static MaybeLocal<Promise> ImportModuleDynamically(
UNREACHABLE();
}

Local<Object> assertions =
Object::New(isolate, v8::Null(env->isolate()), nullptr, nullptr, 0);
for (int i = 0; i < import_assertions->Length(); i += 2) {
assertions
->Set(env->context(),
Local<String>::Cast(import_assertions->Get(env->context(), i)),
Local<Value>::Cast(import_assertions->Get(env->context(), i + 1)))
.ToChecked();
}

Local<Value> import_args[] = {
object,
Local<Value>(specifier),
assertions,
};

Local<Value> result;
Expand Down
6 changes: 3 additions & 3 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -803,11 +803,11 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
return 12;
}

// TODO(mylesborins): remove this when the harmony-top-level-await flag
// TODO(mylesborins): remove this when the harmony-import-assertions flag
// is removed in V8
if (std::find(v8_args.begin(), v8_args.end(),
"--no-harmony-top-level-await") == v8_args.end()) {
v8_args.push_back("--harmony-top-level-await");
"--no-harmony-import-assertions") == v8_args.end()) {
v8_args.push_back("--harmony-import-assertions");
}

auto env_opts = per_process::cli_options->per_isolate->per_env;
Expand Down
43 changes: 43 additions & 0 deletions test/es-module/test-esm-dynamic-import-assertion.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Flags: --experimental-json-modules
import '../common/index.mjs';
import { rejects, strictEqual } from 'assert';

const jsModuleDataUrl = 'data:text/javascript,export{}';

await rejects(
import(`data:text/javascript,import${JSON.stringify(jsModuleDataUrl)}assert{type:"json"}`),
{ code: 'ERR_FAILED_IMPORT_ASSERTION' }
);

await rejects(
import(jsModuleDataUrl, { assert: { type: 'json' } }),
{ code: 'ERR_FAILED_IMPORT_ASSERTION' }
);

{
const [secret0, secret1] = await Promise.all([
import('../fixtures/experimental.json'),
import(
'../fixtures/experimental.json',
{ assert: { type: 'json' } }
),
]);

strictEqual(secret0.default.ofLife, 42);
strictEqual(secret1.default.ofLife, 42);
strictEqual(secret0.default, secret1.default);
strictEqual(secret0, secret1);
}

{
const [secret0, secret1] = await Promise.all([
import('data:application/json,{"ofLife":42}'),
import(
'data:application/json,{"ofLife":42}',
{ assert: { type: 'json' } }
),
]);

strictEqual(secret0.default.ofLife, 42);
strictEqual(secret1.default.ofLife, 42);
}
8 changes: 8 additions & 0 deletions test/es-module/test-esm-import-assertion-2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Flags: --experimental-json-modules
import '../common/index.mjs';
import { strictEqual } from 'assert';

// eslint-disable-next-line max-len
import secret from '../fixtures/experimental.json' assert { type: 'json', unsupportedAssertion: 'should ignore' };

strictEqual(secret.ofLife, 42);
9 changes: 9 additions & 0 deletions test/es-module/test-esm-import-assertion-3.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Flags: --experimental-json-modules
import '../common/index.mjs';
import { strictEqual } from 'assert';

import secret0 from '../fixtures/experimental.json' assert { type: 'json' };
import secret1 from '../fixtures/experimental.json';

strictEqual(secret0.ofLife, 42);
strictEqual(secret1.ofLife, 42);
7 changes: 7 additions & 0 deletions test/es-module/test-esm-import-assertion.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Flags: --experimental-json-modules
import '../common/index.mjs';
import { strictEqual } from 'assert';

import secret from '../fixtures/experimental.json' assert { type: 'json' };

strictEqual(secret.ofLife, 42);
2 changes: 2 additions & 0 deletions test/message/esm_import_assertion_failing.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../common/index.mjs';
import 'data:text/javascript,export{}' assert {type:'json'};
12 changes: 12 additions & 0 deletions test/message/esm_import_assertion_failing.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node:internal/errors:*
ErrorCaptureStackTrace(err);
^

TypeError [ERR_FAILED_IMPORT_ASSERTION]: Failed to load module "data:text/javascript,export{}", expected type to be "json", got "module" instead
at new NodeError (node:internal/errors:*:*)
at Loader.getModuleJob (node:internal/modules/esm/loader:*:*)
at async ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:*:*)
at async Promise.all (index 0)
at async link (node:internal/modules/esm/module_job:*:*) {
code: 'ERR_FAILED_IMPORT_ASSERTION'
}
2 changes: 1 addition & 1 deletion test/parallel/test-vm-module-link.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

// Flags: --experimental-vm-modules --harmony-import-assertions
// Flags: --experimental-vm-modules

const common = require('../common');

Expand Down
2 changes: 1 addition & 1 deletion tools/code_cache/mkcodecache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ int main(int argc, char* argv[]) {
#endif // _WIN32

v8::V8::SetFlagsFromString("--random_seed=42");
v8::V8::SetFlagsFromString("--harmony-top-level-await");
v8::V8::SetFlagsFromString("--harmony-import-assertions");

if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <path/to/output.cc>\n";
Expand Down

0 comments on commit 38e330b

Please sign in to comment.