Skip to content

Commit 18ccc59

Browse files
authored
docs(experiments): document experiments.html and its HTML features (#8243)
* docs(experiments): document experiments.html and its HTML behaviors Webpack 5.107 introduces experiments.html, the html-loader side of native HTML module support. With the flag on, importing an HTML file from JavaScript runs its tag references through the webpack pipeline. What this PR documents: - The experiments.html flag and its TOC entry. - The JS-import-HTML usage pattern (no entry-point support yet). - Inline <style> tags routed through the CSS pipeline as virtual exportType: "text" modules, so url() and @import resolve relative to the HTML file. - Inline <script> tags routed through the same entry pipeline as <script src>: classic bodies bundle as CommonJS, type="module" bodies as ESM, tags rewritten to <script src="...">, auto-upgrade to type="module" under output.module. - <script src> and <link rel="modulepreload"> references becoming real webpack entries with shared runtime via dependOn, independent modulepreload chunks, and type="module" auto-upgrade. - webpackIgnore magic comment for HTML (cross-references the existing magic comments docs in api/module-methods). What is explicitly NOT supported in 5.107 (called out in the warning admonition): HTML entry points (the html-webpack-plugin part). That is planned for the next minor release. The full story is tracked in issue #536. Refs: - webpack/webpack#20902 (experiments.html flag) - webpack/webpack#20962 (inline <style>) - webpack/webpack#20967 (inline <script>) - webpack/webpack#20949 (<script src> / modulepreload) - webpack/webpack#20950 (webpackIgnore in HTML) * docs(magic-comments): document webpackIgnore support in HTML modules Webpack 5.107 supports the webpackIgnore magic comment inside HTML modules (when experiments.html is enabled). Placing <!-- webpackIgnore: true --> before a tag skips URL resolution for its src/href/srcset attributes, leaving the tag untouched in the emitted HTML. Adds a new HTML Usage section under the existing webpackIgnore docs in api/module-methods. Refs: webpack/webpack#20950
1 parent b2c1e64 commit 18ccc59

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

src/content/api/module-methods.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,22 @@ We support `webpackIgnore` in the following cases:
183183
184184
T> For other CSS scenarios, [`css-loader` fully supports `webpackIgnore`](/loaders/css-loader/#disable-url-resolving-using-the--webpackignore-true--comment), allowing more flexibility if needed.
185185
186+
**HTML Usage**
187+
188+
<Badge text="5.107.0+" />
189+
190+
When [`experiments.html`](/configuration/experiments/#experimentshtml) is enabled, `webpackIgnore` can be placed as an HTML comment immediately before a tag to skip URL resolution for that tag's `src`, `href`, `srcset`, and similar attributes. The tag is left untouched in the emitted HTML. This mirrors the behavior provided by `html-loader`.
191+
192+
```html
193+
<!-- webpackIgnore: true -->
194+
<img src="https://cdn.example.com/logo.png" />
195+
196+
<!-- webpackIgnore: true -->
197+
<script src="/legacy/external.js"></script>
198+
```
199+
200+
The magic-comment value is parsed with the same context used by the JS and CSS parsers; non-boolean values emit an `UnsupportedFeatureWarning`.
201+
186202
##### webpackChunkName
187203
188204
A name for the new chunk. Since webpack 2.6.0, the placeholders `[index]` and `[request]` are supported within the given string to an incremented number or the actual resolved filename respectively. Adding this comment will cause our separate chunk to be named [my-chunk-name].js instead of [id].js.

src/content/configuration/experiments.mdx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Available options:
2727
- [`css`](#experimentscss)
2828
- [`deferImport`](#experimentsdeferimport)
2929
- [`futureDefaults`](#experimentsfuturedefaults)
30+
- [`html`](#experimentshtml)
3031
- [`lazyCompilation`](#experimentslazycompilation)
3132
- [`outputModule`](#experimentsoutputmodule)
3233
- [`sourceImport`](#experimentssourceimport)
@@ -374,6 +375,118 @@ export default {
374375
};
375376
```
376377

378+
### experiments.html
379+
380+
<Badge text="5.107.0+" />
381+
382+
Enable native HTML module support. Importing a `.html` file from JavaScript runs its tag references through the normal webpack pipeline, replacing the role `html-loader` has played for years. The flag registers the `html` module type on `NormalModuleFactory` and unlocks the HTML behaviors described below.
383+
384+
- Type: `boolean`
385+
- Default: `false`
386+
387+
**webpack.config.js**
388+
389+
```js
390+
export default {
391+
// ...
392+
experiments: {
393+
html: true,
394+
},
395+
};
396+
```
397+
398+
Then import the HTML file from JavaScript. The default export is the processed HTML as a string, with all asset references resolved through webpack:
399+
400+
```js
401+
// src/index.js
402+
import page from "./page.html";
403+
404+
document.documentElement.innerHTML = page;
405+
```
406+
407+
W> **This feature is experimental and partial.** Webpack 5.107 only implements the `html-loader` side: importing an HTML file from JS runs its tag references through the webpack pipeline. **HTML entry points** (using a `.html` file directly as `entry`, the `html-webpack-plugin` part) are **not supported yet** and are planned for the next minor release. The full story is tracked in issue [#536](https://github.com/webpack/webpack/issues/536).
408+
409+
#### Inline `<style>` tags
410+
411+
Inline `<style>` blocks inside an HTML module are routed through webpack's CSS pipeline as virtual CSS modules with `exportType: "text"`. `url()` and `@import` references are resolved relative to the HTML file, and the processed CSS text is written back into the original `<style>` tag in the emitted HTML string.
412+
413+
```html
414+
<!-- src/page.html -->
415+
<!doctype html>
416+
<html>
417+
<head>
418+
<style>
419+
@import "./reset.css";
420+
421+
body {
422+
background: url("./bg.png");
423+
}
424+
</style>
425+
</head>
426+
<body>
427+
...
428+
</body>
429+
</html>
430+
```
431+
432+
`<style type="text/css">` and `<style>` with no `type` attribute are processed. Anything with a non-CSS `type` is passed through unchanged.
433+
434+
#### Inline `<script>` tags
435+
436+
Inline `<script>` bodies are routed through the same entry pipeline used for [`<script src>`](#script-src-and-link-relmodulepreload). Each `<script>` body becomes its own webpack entry: classic inline scripts are bundled as CommonJS, and `<script type="module">` bodies are bundled as ESM. The tag in the emitted HTML is rewritten to `<script src="…">` pointing at the generated chunk, with the body cleared.
437+
438+
```html
439+
<!-- src/page.html -->
440+
<!doctype html>
441+
<html>
442+
<body>
443+
<script type="module">
444+
import { greet } from "./lib.js";
445+
greet("world");
446+
</script>
447+
448+
<script>
449+
console.log("classic inline script");
450+
</script>
451+
</body>
452+
</html>
453+
```
454+
455+
The same behaviors that apply to external `<script src>` apply here too:
456+
457+
- When [`output.module`](/configuration/output/#outputmodule) is enabled, classic inline `<script>` tags are auto-upgraded to `type="module"`, matching the auto-upgrade for `<script src>`.
458+
- `webpackIgnore` works on inline `<script>` tags as well, leaving the original body untouched.
459+
- Non-JS `type` values such as `application/ld+json` and `importmap` pass through unchanged.
460+
461+
#### `<script src>` and `<link rel="modulepreload">`
462+
463+
`<script src>` and `<link rel="modulepreload">` references inside an HTML module become real webpack entries. The emitted chunk URL is rewritten back into the HTML string, so hashed filenames work the same way they do for JavaScript imports.
464+
465+
```html
466+
<!-- src/page.html -->
467+
<!doctype html>
468+
<html>
469+
<head>
470+
<link rel="modulepreload" href="./preloaded.js" />
471+
</head>
472+
<body>
473+
<script src="./entry.js"></script>
474+
<script src="./second.js"></script>
475+
</body>
476+
</html>
477+
```
478+
479+
A few behaviors to keep in mind:
480+
481+
- Multiple `<script src>` tags on the same page share a single runtime. Within each group (classic or `type="module"`), the leader holds the runtime and the rest declare `dependOn` on it.
482+
- `<link rel="modulepreload">` entries stay independent and are never imported by sibling scripts, preserving "preload without execute" semantics.
483+
- When [`output.module`](/configuration/output/#outputmodule) is enabled, classic `<script src>` tags are auto-upgraded to `<script type="module" src>` so the emitted ES-module chunks load in the correct mode.
484+
- Non-JS script types (`application/ld+json`, `importmap`, …) and data URIs flow through unchanged and are not bundled as JS.
485+
486+
#### `webpackIgnore` magic comment
487+
488+
Placing an HTML `<!-- webpackIgnore: true -->` comment immediately before a tag tells webpack to skip URL resolution for that tag's `src`, `href`, `srcset`, and similar attributes. See the full description under [magic comments](/api/module-methods/#webpackignore).
489+
377490
### experiments.lazyCompilation
378491

379492
Compile entrypoints and dynamic `import`s only when they are in use. It can be used for either Web or Node.js.

0 commit comments

Comments
 (0)