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

feat: Add support for import assertions and JSON modules #12866

Merged
merged 43 commits into from
Dec 15, 2021

Conversation

bartlomieju
Copy link
Member

@bartlomieju bartlomieju commented Nov 22, 2021

Closes #7623

This commit adds proper support for import assertions and JSON modules.

Implementation of "core/modules.rs" was changed to account for multiple possible
module types, instead of always assuming that the code is an "ES module". In
effect "ModuleMap" now has knowledge about each modules' type (stored via
"ModuleType" enum). Module loading pipeline now stores information about
expected module type for each request and validates that expected type matches
discovered module type based on file's "MediaType".

Relevant tests were added to "core/modules.rs" and integration tests,
additionally multiple WPT tests were enabled.

There are still some rough edges in the implementation and not all WPT were
enabled, due to:
a) unclear BOM handling in source code by "FileFetcher"
b) design limitation of Deno's "FileFetcher" that doesn't download the same
module multiple times in a single run

@dsherret
Copy link
Member

dsherret commented Nov 23, 2021

Probably will need an swc upgrade once this is fixed swc-project/swc#2802

Edit: Not relevant

@bartlomieju bartlomieju added this to the 1.17.0 milestone Dec 10, 2021
@bartlomieju
Copy link
Member Author

Blocked by #12989

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

I can not get this to work:

import x from "./.dlint.json" asserts { "type": "json" };
$ cargo run --example fs_module_loader test.js
Run test.js
cargo:rerun-if-changed=/mnt/starship/Projects/github.com/denoland/deno/core/00_primordials.js
cargo:rerun-if-changed=/mnt/starship/Projects/github.com/denoland/deno/core/01_core.js
cargo:rerun-if-changed=/mnt/starship/Projects/github.com/denoland/deno/core/02_error.js
Error: Uncaught SyntaxError: Unexpected identifier
    at file:///mnt/starship/Projects/github.com/denoland/deno/test.js:1:30

@bartlomieju
Copy link
Member Author

bartlomieju commented Dec 15, 2021

I have trouble getting

"UTF-16BE BOM should result in parse error in JSON module script",
"UTF-16LE BOM should result in parse error in JSON module script"

to pass.

When I test it locally the proper error is returned, but somehow during WPT it breaks on some utility like dprint and surfaces TypeError instead of SyntaxError.

To be precise I'm getting:

promise_rejects_js: Expected parse error from UTF-16BE BOM function "function() { throw e }" threw object "TypeError: invalid utf-8 sequence of 1 bytes from index 0" ("TypeError") expected instance of function "function SyntaxError() { [native code] }" ("SyntaxError")
    at async Test.<anonymous> (http://web-platform.test:8000/html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js:10:5)

However running a local file that imports JSON with BOM I get:

error: Uncaught (in promise) SyntaxError: Unexpected token � in JSON at position 0
const result = await import("./data.json", { assert: { type: "json" } });
               ^
    at async file:///Users/biwanczuk/dev/deno/test.js:1:16

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

Looking good!

core/modules.rs Show resolved Hide resolved
core/modules.rs Outdated Show resolved Hide resolved
@andreubotella
Copy link
Contributor

andreubotella commented Dec 15, 2021

The UTF-16 TypeError seems to be a std::string::FromUtf8Error thrown when get_source_from_bytes() is called with None as charset. If called with Some("utf-8"), the conversion would replace invalid UTF-8 with replacement characters, which would give the correct SyntaxError.

Edit: That last sentence is wrong, I completely misread the code 😅

Copy link
Member

@dsherret dsherret left a comment

Choose a reason for hiding this comment

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

Looks really good! Will be really nice to have this.

core/modules.rs Show resolved Hide resolved
core/modules.rs Outdated Show resolved Hide resolved
v8::Exception::reference_error(scope, message.try_into().unwrap())
}
_ => v8::Exception::error(scope, message.try_into().unwrap()),
};
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Seems like something that could be extracted out into a function to help reduce the detail here.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is only used in a single place, and the whole function seems like a code smell, I'd rather address this in separate PR that would cleanup modules implementation

core/modules.rs Outdated Show resolved Hide resolved
// For static imports, assertions are triples of (keyword, value and source offset)
let assertions = parse_import_assertions(tc_scope, import_assertions, 3);

// FIXME(bartomieju): there are no stack frames if exception
Copy link
Member

Choose a reason for hiding this comment

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

Is this something to be fixed in this review or later?

Copy link
Member Author

Choose a reason for hiding this comment

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

Later, it's not clear how to fix it

@bartlomieju
Copy link
Member Author

The UTF-16 TypeError seems to be a std::string::FromUtf8Error thrown when get_source_from_bytes() is called with None as charset. If called with Some("utf-8"), the conversion would replace invalid UTF-8 with replacement characters, which would give the correct SyntaxError.

Edit: That last sentence is wrong, I completely misread the code 😅

Thanks for pointing this out, it's not clear to me what would need to change to fix it. @dsherret any suggestions?

@bartlomieju
Copy link
Member Author

Due to the fact that Deno caches HTTP responses on first use, I believe we can't enable "Two modules of different type with the same specifier can load if the server changes its responses" test; as it changes the response's content type on second fetch.

@andreubotella
Copy link
Contributor

andreubotella commented Dec 15, 2021

So according to the HTML spec, the charset declared on the MIME type, or the file's BOM, don't really matter. Instead, all modules –JS, JSON or CSS– are decoded with the Encoding standard's "UTF-8 decode" algorithm (see "fetch a single module script", step 13), which is equivalent to:

let bytes = if bytes.starts_with(&[0xEF, 0xBB, 0xBF]) {
    &bytes[3..]
} else {
    bytes
};
String::from_utf8_lossy(bytes)

I suspect windows-1252 and perhaps UTF-16 might be important for local files in Windows, so we might need to go against the spec there, but the WPT tests expect the JSON parsing to fail, not the UTF-8 decoding.

@bartlomieju
Copy link
Member Author

@andreubotella seems like a niche case - we still fail as expected, just with wrong error type. I'd prefer to address this in a follow up PR.

@andreubotella
Copy link
Contributor

@andreubotella seems like a niche case - we still fail as expected, just with wrong error type. I'd prefer to address this in a follow up PR.

Fair enough

Copy link
Member

@dsherret dsherret left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Import Assertions/JSON Modules
6 participants