Skip to content

Add text imports - #11933

Merged
zcorpan merged 7 commits into
whatwg:mainfrom
eemeli:import-text
Apr 1, 2026
Merged

zcorpan merged 7 commits into
whatwg:mainfrom
eemeli:import-text

Conversation

@eemeli

@eemeli eemeli commented Nov 20, 2025 •

Copy link
Copy Markdown
Member

This is the HTML counterpart for the newly-introduced TC39 Import Text Stage 2 proposal; this has been previously discussed here in #9444, and is closely related (but not blocking on) PR #11657, which correspondingly adds byte imports.

The type: 'text' import attribute is introduced here without any MIME type requirement, and without any validity or well-formedness checks on the returned string value. @sffc has raised some concerns about additional validation steps being desirable for text content, which would be appropriate for consideration in this spec, rather than the JS spec.

The implementer support noted below is based on feedback from delegates at the 2025 November TC39 meeting.


/acknowledgements.html ( diff )
/infrastructure.html ( diff )
/links.html ( diff )
/references.html ( diff )
/webappapis.html ( diff )

Adds support for the `{ type: 'text' }` import attribute,
which enables importing text content as a JavaScript string.
@annevk

annevk commented Nov 20, 2025

Copy link
Copy Markdown
Member
@bakkot

bakkot commented Nov 22, 2025

Copy link
Copy Markdown
Contributor

Looks good. I do wonder whether there ought to be a required MIME type, especially given that the other types all have one. If type: "bytes" had a similar requirement (which IMO would be much more annoying), that would allay the concerns in this issue.

Alternatively there could be a requirement not to have certain MIME types, e.g. to disallow importing JavaScript as text.

I think I am personally inclined not to impose such requirements, but I could see the argument either way.

@zcorpan

zcorpan commented Nov 25, 2025

Copy link
Copy Markdown
Member

Would this make available the same content that is already available via fetch()? Is it possible to use CSP (connect-src ?) to restrict these imports?

@bakkot

bakkot commented Nov 25, 2025

Copy link
Copy Markdown
Contributor

Good question. Looks like type: "json" imports are governed by connect-src, so I assume these should be as well. This will require filing a PR against CSP updating the "Get the effective directive for request" algorithm to list requests with destination "text" as having effective directive connect-src. (Not strictly necessary because connect-src is the fallback, but it would be weird to leave it out given that "json" imports are listed explicitly.)

For clarity it might be good to rename the destination to "text-import" or something, incidentally.

Anyway, yes, this ends up letting you get the exact same content as fetch.

@nicolo-ribaudo

Copy link
Copy Markdown
Member

Do we actually want a text destination? We added one for json because it's a specific format, but in most cases a text import will then be parsed by something else. fetch uses an empty destination because, similarly, we don't actually know how its result is going to be used.

@bakkot

bakkot commented Nov 25, 2025

Copy link
Copy Markdown
Contributor

json doesn't seem any more specific than text to me, really. It's just data either way.

@annevk

annevk commented Nov 25, 2025

Copy link
Copy Markdown
Member

It's specific in that we can ask the server for JSON with Accept: application/json (and we do).

Given that the destination is exposed these days through Sec-Fetch-Dest as well it would probably be useful to have a dedicated value here as well. text seems fine to me.

@bakkot

bakkot commented Nov 25, 2025

Copy link
Copy Markdown
Contributor

Oh, nice, I didn't realize that it got exposed there and wasn't just editorial. In that case I agree text is fine, as long as we don't imagine later introducing a new kind of "text" destination where the distinction is important.

@eemeli

eemeli commented Dec 17, 2025

Copy link
Copy Markdown
Member Author

I've now added PRs for the changes in the Fetch and CSP specs, and for WPT tests.

@eemeli

eemeli commented Mar 11, 2026

Copy link
Copy Markdown
Member Author

The TC39 / ECMA-262 part of this proposal has now advanced to Stage 3.

@smaug---- smaug---- added the agenda+ To be discussed at a triage meeting label Mar 12, 2026

@noamr noamr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this needs to be rebased, and properly support modulepreload like CSS/JSON.

Comment thread source Resolved
@eemeli
eemeli requested a review from noamr March 12, 2026 17:58
@noamr

noamr commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Do the tests cover module preload? The spec text seemed good AFAICT!

@noamr noamr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unofficial LGTM % tests for modulepreload

@eemeli

eemeli commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

Do the tests cover module preload?

@noamr They do now (I just updated the WPT PR).

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks good to me. It would be great if @nicolo-ribaudo could also do a quick skim.

@annevk

annevk commented Mar 20, 2026

Copy link
Copy Markdown
Member

MDN and browser bugs should be completed for this as well before landing.

@eemeli

eemeli commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

MDN and browser bugs should be completed for this as well before landing.

Done.

Comment thread source Resolved
@eemeli
eemeli requested a review from nicolo-ribaudo March 20, 2026 17:09
Comment thread source Resolved
@noamr

noamr commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

@eemeli can you detail how this PR addresses the concerns you've linked to in tc39/proposal-import-text#8?

The PR description says they should be addressed in this PR. Maybe they are? It would be good to flesh it out.
Seems to me from this PR that response bytes for text imports are always decoded as UTF-8. Perhaps it needs to verify that the mime-type is textual as well, so that it doesn't try to UTF8-decode some binary response?

@smaug---- smaug---- removed the agenda+ To be discussed at a triage meeting label Mar 26, 2026
@eemeli

eemeli commented Mar 26, 2026

Copy link
Copy Markdown
Member Author

@noamr The concerns raised in tc39/proposal-import-text#8 lead to tc39/proposal-import-text#10, which added a non-normative qualifier that the String representing the imported module "should represent textual data". This recalls the pre-existing spec description of Strings as "generally used to represent textual data".

As you note, this proposal has imported text modules always being parsed as UTF-8, as required by the Encoding standard. And so if the imported bytes do not in fact represent text, we end up with an unusable string full of lossy � replacements, and not e.g. a base64 representation of it, or some other reversible binary-as-text mapping.

Once a text module has been imported, we can (and should) presume that it'll be used for something; either presented directly to the user, or parsed for some further meaning. In either case, a UTF-8 parsing of binary data will be readily apparent as not fulfilling whatever need is put to it, and the error can be addressed by the developer. However, this assertion of "readily apparent" is rather dependent on having some idea of the expected shape of the textual data, which we don't know for the general case: Maybe it's prose that's been incorrectly encoded and decoded resulting in a smattering of replacement characters; maybe it's an XML document; maybe it's base122 encoded data.

And so if we do want to identify the errors caused by parsing binary data as text a bit earlier, and in a way that would make those errors non-recoverable (you can't try-catch an import statement), I agree that filtering on the MIME type is perhaps the only available recourse. That does leave us with the problem of identifying "textual" MIME types, for which I'm not aware of a pre-existing registry or heuristic -- or is there one I've missed? Relying on the IANA registry's "Encoding Considerations" section is not really appropriate: as noted in its application form,

If the format is based on JSON or XML, "binary" should generally be selected due to the possibility that lines could be longer than 998 octets.

Relying on such a registry would also introduce unnecessary points of divergence between implementations. For example, application/yaml was introduced in 2024, and should definitely qualify as "textual data" for the purposes here -- it's been the example I've used for the proposal from its inception. If filtering like this were used and the proposal was already available, a pre-2024 browser would probably not allow importing an application/yaml file as text, while a post-2024 browser would allow it. This seems like an unnecessary burden to put on the adoption of new MIME types, with no clear user benefit.

To summarize, I think the concerns that have been raised are addressed as well as they can be by this proposal, and that introducing additional filtering, sniffing, or heuristics is not warranted, given the real-world limitations we need to contend with, and the negative consequences of doing so.

@noamr

noamr commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for the detailed response, @eemeli. This is resolved as far as I'm concerned.

@zcorpan
zcorpan merged commit 0045b78 into whatwg:main Apr 1, 2026
2 checks passed
zcorpan pushed a commit to web-platform-tests/wpt that referenced this pull request Apr 1, 2026
annevk pushed a commit to whatwg/fetch that referenced this pull request Apr 1, 2026
annevk pushed a commit to w3c/webappsec-csp that referenced this pull request Apr 1, 2026
@eemeli
eemeli deleted the import-text branch April 1, 2026 18:32
lando-worker Bot pushed a commit to mozilla-firefox/firefox that referenced this pull request Apr 17, 2026
Automatic update from web-platform-tests
Add tests for text modules

This is to support the spec changes in:

- https://github.com/tc39/proposal-import-text
- whatwg/html#11933
- whatwg/fetch#1898
- w3c/webappsec-csp#794
--

wpt-commits: 5419f98d72f4b1ae49980581a6a66710d6a3dee7
wpt-pr: 56812
zerox80 pushed a commit to zerox80/firefox that referenced this pull request Oct 16, 2026
Automatic update from web-platform-tests
Add tests for text modules

This is to support the spec changes in:

- https://github.com/tc39/proposal-import-text
- whatwg/html#11933
- whatwg/fetch#1898
- w3c/webappsec-csp#794
--

wpt-commits: 5419f98d72f4b1ae49980581a6a66710d6a3dee7
wpt-pr: 56812
PaulKinlan added a commit to PaulKinlan/gendn that referenced this pull request Oct 18, 2026
MDN documents type:"text" substantively in the import-attributes
reference (dedicated Text Modules subsection with example, loading
behaviour, specs + compat; verified 2026-10-18), so this is an honest
stub rather than a duplicate reference. Quick reference carries the
verbatim TC39 README syntax example, the merged host-integration trail
(whatwg/html#11933, whatwg/fetch#1898, w3c/webappsec-csp#794), and
webstatus.dev text-modules data (Chrome 155 / Edge 155 / Firefox 153 /
Safari not yet; Baseline limited) labelled as web-features data since
no dedicated BCD key resolved yet. Showcase feature route + all three
concept routes HEAD-checked 200. Conformance green, responsive matrix
ok on both classes (screenshots read).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5RCZeaUQC54N8sSJCvkN
bcardarella added a commit to zig-whatwg/crane that referenced this pull request Oct 18, 2026
…loader; cached import() settles in microtasks; modulepreload into the module map

Lane module1 (brief tmp/orchestration/briefs/module1-final.md), items 1-4.

1. Text module scripts (HTML "create a text module script",
   whatwg/html#11933, merged 2026-04-01; TC39 proposal-import-text
   CreateTextModule): ModuleType.text ("text:" map key), the "text"
   import attribute, "fetch destination from module type" (new
   ModuleType.fetchDestination; Fetch's "text" destination joins
   Destination, isSubresourceRequest, the Accept header
   text/plain,*/*;q=0.5 and Request.destination), and fetch-single
   step 13.7: the body UTF-8 decoded whatever its Content-Type, as a
   default-export synthetic module (engine.createDefaultExportSyntheticModule
   with a string). Golden rule 2: newer spec shipped by Chrome and Firefox
   (text-module/module.html, charset-bom.any.html PASS; Safari ERROR) at
   wpt.fyi stable df642c4142. Worker import() checks "module type allowed"
   instead of special-casing css.

2. Worker import() through module_script.AsyncModuleLoader, the Window's
   callback-list loader: its owner generalised from the Document to the
   settings object's platform object (owner/owner_generation; WebKit's
   ScriptModuleLoader serves Document and WorkerGlobalScope alike). The
   WorkerHost makes it on first import(), each import is a LoaderImport
   client (done/gone finish the engine request), and the realm's end
   destroys it after the loop's tasks are dropped and before
   disposeModules. The request starts at import() (a revoked blob URL
   still loads), joins an in-flight fetch, and its referrer and fetch
   options are the referencing script's (HostLoadImportedModule steps 5-6,
   14). A service worker's import() keeps its old queued path (lane sw1).
   The synchronous path (a module worker's own graph, ShadowRealm import(),
   engine-less contexts) no longer keeps a failed fetch: the fetch_failed
   sentinel is gone (fetch-single step 13.1, whatwg/html#10327), and with
   it a container-level var (global-state baseline 289 -> 288).

3. An import() whose whole graph is in the module map completes from a
   realm microtask, not a task (Client.settle_cached_in_microtask, set by
   the Window's and the workers' import()). ECMA-262 ContinueDynamicImport
   links and evaluates in a reaction to the settled loadPromise, so one
   microtask is the spec's ordering; no protocol change. Golden rule 2,
   first case (integrator, 2026-10-18): wpt PR #35949 with whatwg/html#8264;
   Safari ships it in both globals, Firefox in workers.

4. <link rel=modulepreload> fetches through the document's module loader
   (script_execution.fetchModulePreload -> AsyncModuleLoader.startModulePreload):
   "fetch a modulepreload module script graph" puts the module into the
   module map, so a later import() takes it, or joins its fetch, under the
   link's referrer policy (modulepreload-referrerpolicy 7/7 in all three
   browsers). Step 3 accepts every module preload destination (json,
   style, text, script-like). The load names its fetch by id
   (ModulePreload), so a load that ends first leaves the fetch to the map.
   Resource Timing initiatorType stays "link". The optional descendants
   step is not done.

Tests (red at 3b12eef: tmp/debug/red1-main-3b12eef2f4.log, green here):
tests/html/module1_text_module_test.zig, module1_worker_imports_test.zig
(retry after failure, joined fetches, joined failure with a sibling
refetch, microtask settling - Window, module, classic and shared workers;
termination with imports in flight; two Browsers on two threads loading
one diamond graph in workers, per-Browser DebugAllocator),
module1_modulepreload_test.zig, over a loopback server behind the
Browser's proxy (module1_http_server.zig). Unit tests pin
moduleTypeFromAttribute and fetchDestination.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addition/proposal New features or enhancements topic: script

7 participants