Skip to content

Commit

Permalink
Bug 1929548 [wpt PR 49004] - Multiple import maps, a=testonly
Browse files Browse the repository at this point in the history
Automatic update from web-platform-tests
Multiple import maps

Import maps currently have to load before any ES module and there can
only be a single import map per document. That makes them fragile and
potentially slow to use in real-life scenarios: Any module that loads
before them breaks the entire app, and in apps with many modules the
become a large blocking resource, as the entire map for all possible
modules needs to load first.

This implements whatwg/html#10528 to solve that.

Change-Id: I54e1b9cdfe989d61c85d73a5fd384f860273ad9a
Bug: 358379381
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5776262
Commit-Queue: Yoav Weiss (@Shopify) <yoavweiss@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1378943}

--

wpt-commits: 3f26764c67f20c6f5306063fc9bcda13930a51ca
wpt-pr: 49004
  • Loading branch information
Yoav Weiss authored and moz-wptsync-bot committed Nov 7, 2024
1 parent 49aad56 commit b3328ee
Show file tree
Hide file tree
Showing 27 changed files with 637 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const t = async_test(
'After dynamic imports, import maps should fire error events');
const log = [];
// To ensure we are testing that the flag is cleared at the beginning of module
// script loading unconditionally, not at the end of loading or not at the
Expand All @@ -14,7 +12,7 @@
promise_test(() => import('../resources/empty.js?pipe=trickle(d1)'),
"A dynamic import succeeds");
</script>
<script type="importmap" onload="t.assert_unreached('onload')" onerror="t.done()">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
Expand All @@ -24,7 +22,6 @@
<script>
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
},
'After a dynamic import(), import maps are not effective');
.then(() => assert_array_equals(log, ["log:B"]))
}, 'After a dynamic import(), import maps work fine');
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const t = async_test(
'With modulepreload link header, import maps should fire error events');
const log = [];
</script>
<script type="importmap" onerror="t.done()">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
Expand All @@ -17,7 +15,7 @@
<script>
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
.then(() => assert_array_equals(log, ["log:B"]))
},
'With modulepreload link header, import maps are not effective');
'With modulepreload link header, import maps work fine');
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const t = async_test(
'After <link rel=modulepreload> import maps should fire error events');
const log = [];
</script>
<link rel="modulepreload" href="../resources/empty.js?pipe=trickle(d1)"></link>
<script type="importmap" onerror="t.done()">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
Expand All @@ -18,7 +16,7 @@
<script>
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
.then(() => assert_array_equals(log, ["log:B"]))
},
'After <link rel=modulepreload> import maps are not effective');
'After <link rel=modulepreload> import maps should work fine');
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const t = async_test(
'After inline <script type="module"> import maps should fire error events');
const log = [];
</script>
<script type="module">
// While this inline module script doesn't have any specifiers and doesn't fetch
// anything, this still disables subsequent import maps, because
// https://wicg.github.io/import-maps/#wait-for-import-maps
// is anyway called at the beginning of
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-inline-module-script-graph
</script>
<script type="importmap" onerror="t.done()">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
Expand All @@ -24,7 +17,7 @@
<script>
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
.then(() => assert_array_equals(log, ["log:B"]))
},
'After inline <script type="module"> import maps are not effective');
'After inline <script type="module"> import maps work fine');
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const t = async_test(
'After <script type="module"> import maps should fire error events');
const log = [];
</script>
<script type="module" src="../resources/empty.js?pipe=trickle(d1)"></script>
<script type="importmap" onerror="t.done()">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
Expand All @@ -18,7 +16,7 @@
<script>
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
.then(() => assert_array_equals(log, ["log:B"]))
},
'After <script type="module"> import maps are not effective');
'After <script type="module"> import maps work fine');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/import-maps/resources/test-helper.js"></script>
<script>
// Simulate resolving a module before import maps are processed
import("../resources/log.js?pipe=sub&name=ModuleA").catch(() => {});
</script>
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=ModuleA": "../resources/log.js?pipe=sub&name=ModuleB",
"http:/": "../resources/log.js?pipe=sub&name=scheme",
"https:/": "../resources/log.js?pipe=sub&name=scheme"
}
}
</script>
<script>
test_loaded(
"../resources/log.js?pipe=sub&name=ModuleA",
["log:ModuleA"],
"Rules for already resolved modules are dropped"
);
</script>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@
}
</script>
<script>
// Currently the spec doesn't allow multiple import maps, by setting acquiring
// import maps to false on preparing the first import map.
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A1")
.then(() => import("../resources/log.js?pipe=sub&name=A2"))
.then(() => import("../resources/log.js?pipe=sub&name=A3"))
.then(() => assert_array_equals(
log,
["onerror 2", "log:B1", "log:B2", "log:A3"]))
["log:B1", "log:B2", "log:C3"]))
},
"Second import map should be rejected");
"Second import map should be used for resolution");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="importmap">
{
"imports": {
"module-a": "../resources/log.js?pipe=sub&name=ModuleA",
"module-b/something": "../resources/log.js?pipe=sub&name=ModuleB"
}
}
</script>
<script type="importmap">
{
"imports": {
"module-a": "../resources/log.js?pipe=sub&name=OtherModuleA",
"module-b/": "../resources/log.js?pipe=sub&name=PrefixModuleB",
"module-b": "../resources/log.js?pipe=sub&name=OtherModuleB"
}
}
</script>
<script>
const test_loaded = (specifier, expected_log, description) => {
promise_test(async t => {
log = [];
await import(specifier);
assert_array_equals(log, expected_log);
}, description);
};

test_loaded(
"module-a",
["log:ModuleA"],
"First defined rule persists in case of conflict"
);

test_loaded(
"module-b/something",
["log:ModuleB"],
"First defined rule persists in case of conflict - prefixed bare specifiers"
);

test_loaded(
"module-b",
["log:OtherModuleB"],
"First defined rule persists in case of conflict - non-prefix bare specifier"
);
</script>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
function waitForBool(boolName) {
return new Promise((resolve) => {
const checkVariable = setInterval(() => {
if (window[boolName]) {
clearInterval(checkVariable);
resolve();
}
}, 0);
});
}

step_timeout(() => {
const importMapScript = document.createElement('script');
importMapScript.type = 'importmap';
importMapScript.textContent = JSON.stringify({
imports: {
"../resources/log.sub.js?name=A": "../resources/log.sub.js?name=B"
}
});
document.head.appendChild(importMapScript);
}, 100);
const log = [];
</script>
<script type="module">
import "../resources/importer.sub.js?pipe=trickle(d0.5)&name=..%2Fresources%2Flog.sub.js%3Fname%3DA";
</script>
<script type="module">
test(() => {
assert_array_equals(log, ["log:B"], "Import should use the new import map");
}, "Module tree that started to download before a new import map should still take it into account");
</script>
</body>
</html>




Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script type="module" async>
step_timeout(() => {
const importMapScript = document.createElement('script');
importMapScript.type = 'importmap';
importMapScript.textContent = JSON.stringify({
imports: {
"../resources/log.sub.js?name=A": "../resources/log.sub.js?name=B"
}
});
document.head.appendChild(importMapScript);
}, 100);
</script>
<script>
const log = [];
</script>
<script type="module" src="../resources/importer.sub.js?pipe=trickle(d0.5)&name=..%2Fresources%2Flog.sub.js%3Fname%3DA"></script>
<script type="module">
test(() => {
assert_array_equals(log, ["log:B"], "Import should use the new import map");
}, "Module tree that started to download before a new import map should still take it into account");
</script>
</body>
</html>



Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const log = [];
</script>
<script type="importmap">
{
"scopes": {
"/": {
"../resources/../resources/app.js": "../resources/log.js?pipe=sub&name=first"
}
}
}
</script>
<script type="importmap">
{
"scopes": {
"/": {
"../resources/app.js": "../resources/log.js?pipe=sub&name=second"
}
}
}
</script>
<script type="module">
promise_test(async () => {
await import("../resources/app.js");
assert_array_equals(log, ["log:first"]);
},
"Second import map should not override same resolved URL");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@
}
</script>
<script>
// Currently the spec doesn't allow multiple import maps, by setting acquiring
// import maps to false on preparing the first import map.
// Even the first import map has errors and thus Document's import map is not
// updated, the second import map is still rejected at preparationg.
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(
log,
["onerror 2", "log:A"]))
["log:C"]))
},
"Second import map should be rejected after an import map with errors");
"Second import map should be used for resolution even after an import map with errors");
</script>
40 changes: 40 additions & 0 deletions testing/web-platform/tests/import-maps/not-overridden/dynamic.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
const log = [];

// First, use a module specifier
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => {
assert_array_equals(log, ["log:A"], "First import should use original module");
});
}, "Initial import before import map");

// Now try to redefine it with multiple import map rules
</script>
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B",
"http:/": "../resources/log.js?pipe=sub&name=scheme",
"https:/": "../resources/log.js?pipe=sub&name=scheme"
}
}
</script>
<script type="module">
// Testing that the resolution is correct using `resolve`, as you can't import
// the same module twice.
test(() => {
assert_true(import.meta.resolve("../resources/log.js?pipe=sub&name=A")
.endsWith("/resources/log.js?pipe=sub&name=A"));
}, "Resolution after import map should not be redefined");
</script>
</body>
</html>

Loading

0 comments on commit b3328ee

Please sign in to comment.