Skip to content

Commit

Permalink
Re-add JSON module scripts
Browse files Browse the repository at this point in the history
Reland JSON modules (#4315), originally found in db03474 but removed in a530f6f. Importing a JSON module now requires the module type to be specified at every import site with an import assertion, addressing the security concern that led to the revert.

Additionally, some of the infrastructure now lives in the TC39 proposal at https://github.com/tc39/proposal-json-modules, and 3d45584 introduced the import assertions integration, so overall this patch is pretty small.
  • Loading branch information
dandclark authored Jul 29, 2021
1 parent afc7950 commit d554d6d
Showing 1 changed file with 97 additions and 41 deletions.
138 changes: 97 additions & 41 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -2888,13 +2888,15 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x="js-HostGetSupportedImportAssertions" data-x-href="https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions">HostGetSupportedImportAssertions</dfn> abstract operation</li>
</ul>

<p>The following terms are defined in the <cite>JSON modules</cite> proposal and used in this
specification: <ref spec=JSJSONMODULES></p>
<p>User agents that support JavaScript must also implement the <cite>JSON modules</cite>
proposal. The following terms are defined there, and used in this specification: <ref
spec=JSJSONMODULES></p>

<ul class="brief">
<li><dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule">CreateSyntheticModule</dfn></li>
<li><dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-setsyntheticmoduleexport">SetSyntheticModuleExport</dfn></li>
<li><dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-synthetic-module-records">Synthetic Module Record</dfn></li>
<li>The <dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-create-default-export-synthetic-module">CreateDefaultExportSyntheticModule</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-setsyntheticmoduleexport">SetSyntheticModuleExport</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-synthetic-module-records">Synthetic Module Record</dfn> specification type</li>
<li>The <dfn data-x-href="https://tc39.es/proposal-json-modules/#sec-parse-json-module">ParseJSONModule</dfn> abstract operation</li>
</ul>
</dd>

Expand Down Expand Up @@ -58396,6 +58398,9 @@ interface <dfn interface>HTMLScriptElement</dfn> : <span>HTMLElement</span> {
<p>The contents of the external script resource for <span data-x="CSS module script">CSS module
scripts</span> must conform to the requirements of the CSS specification. <ref spec=CSS></p>

<p>The contents of the external script resource for <span data-x="JSON module script">JSON module
scripts</span> must conform to the requirements of the JSON specification <ref spec=JSON>.</p>

<p>When used to include <span data-x="data block">data blocks</span>, the data must be embedded
inline, the format of the data must be given using the <code data-x="attr-script-type">type</code>
attribute, and the contents of the <code>script</code> element must conform to the requirements
Expand Down Expand Up @@ -58714,7 +58719,29 @@ o............A....e
where this <code>script</code> element appears in the document, it will not be evaluated until
both document parsing has complete and its dependency (<code data-x="">dom-utils.mjs</code>) has
been fetched and evaluated.</p>
</div>

<div class="example" id="json-module-script-example">
<p>The following sample shows how a <span>JSON module script</span> can be imported from inside
a <span>JavaScript module script</span>:</p>

<pre><code class="html" data-x="">&lt;script type="module">
import peopleInSpace from "http://api.open-notify.org/astros.json" assert { type: "json" };

const list = document.querySelector("#people-in-space");
for (const { craft, name } of peopleInSpace.people) {
const li = document.createElement("li");
li.textContent = `${name} / ${craft}`;
list.append(li);
}
&lt;/script></code></pre>

<p>MIME type checking for module scripts is strict. In order for the fetch of the <span>JSON
module script</span> to succeed, the HTTP reponse must have a <span>JSON MIME type</span>, for
example <code data-x="">Content-Type: text/json</code>. On the other hand, if the <code
data-x="">assert { type: "json" }</code> part of the statement is omitted, it is assumed that the
intent is to import a <span>JavaScript module script</span>, and the fetch will fail if the HTTP
response has a MIME type that is not a <span>JavaScript MIME type</span>.</p>
</div>

<div w-nodev>
Expand Down Expand Up @@ -90154,8 +90181,8 @@ document.querySelector("button").addEventListener("click", bound);
<li><p>a <span>Source Text Module Record</span>, for <span data-x="JavaScript module
script">JavaScript module scripts</span>;</p></li>

<li><p>a <span>Synthetic Module Record</span>, for <span data-x="CSS module script">CSS
module scripts</span>; or</p></li>
<li><p>a <span>Synthetic Module Record</span>, for <span data-x="CSS module script">CSS module
scripts</span> and <span data-x="JSON module script">JSON module scripts</span></p></li>

<li><p>null, representing a parsing failure.</p></li>
</ul>
Expand Down Expand Up @@ -90221,7 +90248,7 @@ document.querySelector("button").addEventListener("click", bound);
data-x="concept-script">script</span>. It has no additional <span data-x="struct
item">items</span>.</p>

<p><span data-x="module script">Module scripts</span> can be classified into two types:</p>
<p><span data-x="module script">Module scripts</span> can be classified into three types:</p>

<ul>
<li><p>A <span>module script</span> is a <dfn data-export="">JavaScript module script</dfn> if
Expand All @@ -90233,23 +90260,33 @@ document.querySelector("button").addEventListener("click", bound);
data-x="concept-script-record">record</span> is a <span>Synthetic Module Record</span>, and it
was created via the <span data-x="creating a CSS module script">create a CSS module
script</span> algorithm. CSS module scripts represent a parsed CSS stylesheet.</p>
<!--
This definition is not super-rigorous, but it doesn't need to be for now. Technically, the
"it was created via..." requirement is redundant, since there are no other Synthetic Module
Record users.

If we ever: (a) get more Synthetic Module Record Users; or (b) start testing if something is a
CSS module script in algorithms, instead of just referring to the concept, then we should
consider adding a type item to the module script struct.
<!--
This definition is not super-rigorous, but it doesn't need to be for now. If we ever start
testing if something is a CSS module script in algorithms, instead of just referring to the
concept, then we should consider adding a type item to the module script struct.
-->

<p class="note">As CSS stylesheets do not import dependent modules, and do not throw exceptions
on evaluation, the <span data-x="concept-script-script-fetch-options">fetch options</span> and
<span data-x="concept-script-base-url">base URL</span> of a <span>CSS module script</span> are
always null.</p>
</li>

<li>
<p>A <span>module script</span> is a <dfn data-export="">JSON module script</dfn> if its <span
data-x="concept-script-record">record</span> is a <span>Synthetic Module Record</span>, and it
was created via the <span data-x="creating a JSON module script">create a JSON module
script</span> algorithm. JSON module scripts represent a parsed JSON document.</p>

<!--
This definition is not super-rigorous, but it doesn't need to be for now. If we ever start
testing if something is a JSON module script in algorithms, instead of just referring to the
concept, then we should consider adding a type item to the module script struct.
-->
</ul>

<p class="note">As CSS stylesheets and JSON documents do not import dependent modules, and do not
throw exceptions on evaluation, the <span data-x="concept-script-script-fetch-options">fetch
options</span> and <span data-x="concept-script-base-url">base URL</span> of <span data-x="CSS
module script">CSS module scripts</span> and <span data-x="JSON module script">JSON module
scripts</span> and are always null.</p>

<p>The <dfn>active script</dfn> is determined by the following algorithm:</p>

<ol>
Expand Down Expand Up @@ -90648,8 +90685,9 @@ document.querySelector("button").addEventListener("click", bound);
type</var> be <var>entry</var>.[[Value]]. Otherwise let <var>module type</var> be "<code
data-x="">javascript</code>".</p></li>

<li><p>If <var>module type</var> is neither "<code data-x="">javascript</code>" nor "<code
data-x="">css</code>", then asynchronously complete this algorithm with null, and return.</p></li>
<li><p>If <var>module type</var> is not "<code data-x="">javascript</code>", "<code
data-x="">css</code>", or "<code data-x="">json</code>", then asynchronously complete this
algorithm with null, and return.</p></li>

<li><p><span>Fetch a single module script</span> given <var>url</var>, <var>settings
object</var>, "<code data-x="">script</code>", <var>options</var>, <var>settings object</var>,
Expand Down Expand Up @@ -91126,11 +91164,12 @@ document.querySelector("button").addEventListener("click", bound);
</ol>
</li>

<li><p>Assert: <var>module type</var> is either "<code data-x="">javascript</code>" or "<code
data-x="">css</code>". Otherwise we would not have reached this point because a failure would
have been raised when inspecting <var>moduleRequest</var>.[[Assertions]] in <a
href="#validate-requested-module-specifiers">create a JavaScript module script</a> or <span>fetch
an import() module script graph</span>.</p></li>
<li><p>Assert: <var>module type</var> is "<code data-x="">javascript</code>", "<code
data-x="">css</code>", or "<code data-x="">json</code>". Otherwise we would not have reached
this point because a failure would have been raised when inspecting
<var>moduleRequest</var>.[[Assertions]] in <a
href="#validate-requested-module-specifiers">create a JavaScript module script</a> or
<span>fetch an import() module script graph</span>.</p></li>

<li><p>Let <var>moduleMap</var> be <var>module map settings object</var>'s <span
data-x="concept-settings-object-module-map">module map</span>.</p></li>
Expand Down Expand Up @@ -91215,6 +91254,11 @@ document.querySelector("button").addEventListener("click", bound);
result of <span>creating a CSS module script</span> given <var>source text</var> and <var>module
map settings object</var>.</p></li>

<li><p>If <var>MIME type essence</var> is a <span>JSON MIME type</span> and <var>module
type</var> is "<code data-x="">json</code>", then set <var>module script</var> to the result of
<span>creating a JSON module script</span> given <var>source text</var> and <var>module map
settings object</var>.</p></li>

<li>
<p><span data-x="map set">Set</span> <var>moduleMap</var>[(<var>url</var>, <var>module
type</var>)] to <var>module script</var>, and asynchronously complete this algorithm with
Expand Down Expand Up @@ -91417,8 +91461,9 @@ document.querySelector("button").addEventListener("click", bound);
data-x="">javascript</code>".</p></li>

<li>
<p>If <var>url</var> is failure, or if <var>module type</var> is neither "<code
data-x="">javascript</code>" nor "<code data-x="">css</code>", then:</p>
<p>If <var>url</var> is failure, or if <var>module type</var> is not "<code
data-x="">javascript</code>", "<code data-x="">css</code>", or "<code data-x="">json</code>",
then:</p>
<ol>
<li><p>Let <var>error</var> be a new <code>TypeError</code> exception.</p></li>

Expand Down Expand Up @@ -91480,27 +91525,38 @@ document.querySelector("button").addEventListener("click", bound);
</li>

<li><p>Set <var>script</var>'s <span data-x="concept-script-record">record</span> to the result
of <span data-x="create a synthetic module record with a default export">creating a synthetic
module record with a default export</span> of <var>sheet</var> with <var>settings</var>.</p>
</li>
of <span>CreateDefaultExportSyntheticModule</span>(<var>sheet</var>).</p></li>

<li><p>Return <var>script</var>.</p></li>
</ol>

<p>To <dfn>create a synthetic module record with a default export</dfn> of a JavaScript value
<var>value</var> with an <span>environment settings object</span> <var>settings</var>:</p>
<p>To <dfn data-x="creating a JSON module script">create a JSON module script</dfn>, given a
string <var>source</var> and an <span>environment settings object</span> <var>settings</var>:</p>

<ol>
<li><p>Let <var>script</var> be a new <span>module script</span> that this algorithm will
subsequently initialize.</p></li>

<li><p>Set <var>script</var>'s <span>settings object</span> to <var>settings</var>.</p></li>

<li><p>Set <var>script</var>'s <span data-x="concept-script-base-url">base URL</span> and
<span data-x="concept-script-script-fetch-options">fetch options</span> to null.</p></li>

<li><p>Set <var>script</var>'s <span data-x="concept-script-parse-error">parse error</span> and
<span data-x="concept-script-error-to-rethrow">error to rethrow</span> to null.</p></li>

<li>
<p>Return <span>CreateSyntheticModule</span>(« "<code data-x="">default</code>" », the following
steps, <var>settings</var>'s <span data-x="environment settings object's Realm">Realm</span>,
<var>value</var>) with the following steps given <var>module</var> as an argument:</p>
<p>Let <var>result</var> be <span>ParseJSONModule</span>(<var>source</var>).</p>

<ol>
<li>Perform ! <span>SetSyntheticModuleExport</span>(<var>module</var>, "<code
data-x="">default</code>", <var>module</var>.[[HostDefined]]).</li>
</ol>
<p>If this throws an exception, set <var>script</var>'s <span
data-x="concept-script-parse-error">parse error</span> to that exception, and return
<var>script</var>.</p>
</li>

<li><p>Set <var>script</var>'s <span data-x="concept-script-record">record</span> to
<var>result</var>.</p></li>

<li><p>Return <var>script</var>.</p></li>
</ol>

<h5 id="calling-scripts">Calling scripts</h5>
Expand Down

0 comments on commit d554d6d

Please sign in to comment.