Skip to content

Commit

Permalink
Add examples
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavweiss committed Aug 1, 2024
1 parent ccb6ec3 commit a77a35b
Showing 1 changed file with 144 additions and 10 deletions.
154 changes: 144 additions & 10 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -108619,6 +108619,11 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
<p>Each <code>Window</code> has a <dfn>resolved module set</dfn> <span>set</span>, initially
empty.</p>

<p class="note">The <span>resolved module set</span> ensures that module specifier resolution
returns the same result when called multiple times with the same (referrer, specifier) pair. It
does that by ensuring that an <span>import map</span> rules that impact that (referrer,
specifier) pair cannot be defined after an initial resolution of that pair.</p>

<p>A <dfn>referring script specifier pair</dfn> is a <span>struct</span>. It has the following
<span data-x="struct item">items</span>:</p>
<dl>
Expand Down Expand Up @@ -108787,6 +108792,12 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
<li><p>Let <var>oldImportMap</var> be <var>global</var>'s <span
data-x="concept-window-import-map">import map</span>.</p></li>

<p class="note">The loops below have O(N^2) complexity. Implementations could optimize that by
e.g. sorting the set based on referrer script can enable O(NlogN) lookups for a prefix.<br>
Breaking each referring script URL to its potential prefixes and adding all of them to a map
that would return the specifier for any prefix. This can work because scopes always end with
‘/’.</p>

<li>
<p><span data-x="map iterate">For each</span> <var>scopePrefix</var> →
<var>scopeImports</var> of <var>newImportMap</var>:</p>
Expand All @@ -108811,17 +108822,9 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
to <var>oldImportMap</var>'s <span
data-x="concept-import-map-scopes">scopes</span>[<var>scopePrefix</var>].</p></li>

<li>
<p>Otherwise, set <var>oldImportMap</var>'s <span
<li><p>Otherwise, set <var>oldImportMap</var>'s <span
data-x="concept-import-map-scopes">scopes</span>[<var>scopePrefix</var>] to
<var>scopeImports</var>.</p>

<p class="note"> The above has O(N^2) complexity. Implementations could optimize that by e.g.
Sorting the set based on referrer script can enable O(NlogN) lookups for a prefix.<br>
Breaking each referring script URL to its potential prefixes and adding all of them to a map
that would return the specifier for any prefix. This can work because scopes always end with
‘/’.</p>
</li>
<var>scopeImports</var>.</p></li>
</ol>
</li>

Expand All @@ -108833,6 +108836,137 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
to <var>global</var>'s <span>import map</span>.</p></li>
</ol>

<p class="note">The above algorithm merges two import maps into a coherent one. Let's examine a
few examples:</p>

<div class="example" id="example-import-map-merge-unrelated">
<p>When two import maps that have no conflicting rules are being merged, and no resolved modules
correspond to the rules defined, the resulting map would be a combination of the two maps.</p>

<p>So, the following two import maps:</p>
<pre><code class="json" data-x="">{
"imports": {
"/app/helper": "./helper/index.mjs",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code>
<code class="json" data-x="">{
"imports": {
"/app/main": "./main/index.mjs"
}
}</code></pre>
<p>Would be equivalent to the following single import map:</p>
<pre><code class="json" data-x="">{
"imports": {
"/app/helper": "./helper/index.mjs",
"lodash": "/node_modules/lodash-es/lodash.js",
"/app/main": "./main/index.mjs"
}
}</code></pre>
</div>

<div class="example" id="example-import-map-merge-conflict-imports">
<p>When an import map tries to define a rule that would have resolved a module that was already
resolved, that rule gets dropped from the import map.</p>

<p>So, if the <span>resolved module set</span> already contains the pair (null, "/app/helper"),
the following import map:</p>
<pre><code class="json" data-x="">{
"imports": {
"/app/helper": "./helper/index.mjs",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code></pre>
<p>Would be equivalent to the following one:</p>
<pre><code class="json" data-x="">{
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code></pre>
</div>

<div class="example" id="example-import-map-merge-conflict-scopes">
<p>The same it true for rules defined in specific scopes. If the <span>resolved module set</span>
contains the pair ("/app/main.mjs", "/app/helper"), the following import map:</p>
<pre><code class="json" data-x="">{
"scopes": {
"/app/": {
"/app/helper": "./helper/index.mjs"
},
}
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code></pre>
<p>Would similarly be equivalent to:</p>
<pre><code class="json" data-x="">{
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code></pre>
<p class="note">The script in the pair is the script object itself, rather than its URL, so these
examples are somwhat simplistic in that regard.</p>
</div>

<div class="example" id="example-import-map-merge-conflict-imports-and-scopes">
<p>We could also have cases where a single already-resolved specifier have multiple rules for its
resolution, depending on the referring script. In such cases, only the most specific rule would
not be added to the map.</p>
<p>For example, if the <span>resolved module set</span> contains the pair ("/app/main.mjs",
"/app/helper"), the following import map:</p>
<pre><code class="json" data-x="">{
"scopes": {
"/app/": {
"/app/helper": "./helper/index.mjs"
},
}
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js"
"/app/helper": "./other_path/helper/index.mjs"
}
}</code></pre>
<p>Would similarly be equivalent to:</p>
<pre><code class="json" data-x="">{
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js"
"/app/helper": "./other_path/helper/index.mjs"
}
}</code></pre>

<p>This is achieved by the fact that the merge algorithm uses a copy of the resolved module set
and removes already referring script specifier pairs from it if they already resulted in a rule
being ignored.</p>
</div>

<div class="example" id="example-import-map-merge-two-map-conflict">
<p>When two import maps that have conflicting rules are being merged, and no resolved modules
correspond to the rules defined, the first defined rules persist.</p>

<p>For example, the following two import maps:</p>
<pre><code class="json" data-x="">{
"imports": {
"/app/helper": "./helper/index.mjs",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}</code>
<code class="json" data-x="">{
"imports": {
"/app/helper": "./main/helper/index.mjs"
}
}</code></pre>
<p>Would be equivalent to the following single import map:</p>
<pre><code class="json" data-x="">{
"imports": {
"/app/helper": "./helper/index.mjs",
"lodash": "/node_modules/lodash-es/lodash.js",
}
}</code></pre>
</div>

<p class="note">TODO - verify that specifiers of the form "./foo/../js/app.js" behave as expected
here.</p>


<p>To <dfn data-x="sorting and normalizing a module specifier map">sort and normalize a module
specifier map</dfn>, given an <span>ordered map</span> <var>originalMap</var> and a
<span>URL</span> <var>baseURL</var>:</p>
Expand Down

0 comments on commit a77a35b

Please sign in to comment.