Skip to content

Commit d5ee691

Browse files
authored
Merge pull request #3011 from modernweb-dev/feat/improve-assets-handling
Improve assets handling
2 parents b9f4a32 + b00f8fb commit d5ee691

File tree

110 files changed

+3885
-1536
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+3885
-1536
lines changed

.changeset/empty-carrots-sneeze.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@web/rollup-plugin-html': major
3+
---
4+
5+
1. Enabled CSS assets extraction by default (as a result, removed configuration option bundleAssetsFromCss).
6+
2. Made extraction of assets from all link rel types
7+
3. Fixed "assetFileNames" behavior
8+
4. Refactored all tests, added tests for more corner cases
9+
5. Added legacy modes for old 2.x.x behavior.
10+
11+
See MIGRATION.md for migration notes.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ local.log
4343
docs/_merged_data/
4444
docs/_merged_assets/
4545
docs/_merged_includes/
46+
47+
## temp
48+
.tmp

docs/docs/building/rollup-plugin-html.md

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -105,65 +105,98 @@ export default {
105105

106106
### Bundling assets
107107

108-
The HTML plugin will bundle assets referenced from `img` and `link` and social media tag elements in your HTML. The assets are emitted as rollup assets, and the paths are updated to the rollup output paths.
108+
The HTML plugin will bundle assets referenced in `img` and `link` and social media tag elements in your HTML:
109109

110-
By default rollup will hash the asset filenames, enabling long term caching. You can customize the filename pattern using the [assetFileNames option](https://rollupjs.org/guide/en/#outputassetfilenames) in your rollup config.
111-
112-
To turn off bundling assets completely, set the `extractAssets` option to false:
113-
114-
```js
115-
import { rollupPluginHTML as html } from '@web/rollup-plugin-html';
116-
117-
export default {
118-
input: 'index.html',
119-
output: { dir: 'dist' },
120-
plugins: [
121-
html({
122-
extractAssets: false,
123-
}),
124-
],
125-
};
110+
```html
111+
<html>
112+
<head>
113+
<link rel="apple-touch-icon" sizes="180x180" href="./images/icon-a.png" />
114+
<link rel="icon" type="image/png" sizes="32x32" href="./images/icon-b.png" />
115+
<link rel="mask-icon" href="./images/icon-c.svg" color="#3f93ce" />
116+
<link rel="manifest" href="./webmanifest.json" />
117+
<link rel="stylesheet" href="./styles.css" />
118+
<link rel="preload" href="./fonts/font.woff2" as="font" type="font/woff2" crossorigin />
119+
</head>
120+
<body>
121+
<img src="./images/image.png" />
122+
</body>
123+
</html>
126124
```
127125

128-
#### Including assets referenced from css
129-
130-
If your css files reference other assets via `url`, like for example:
126+
And the assets referenced in CSS via `url`:
131127

132128
```css
133129
body {
134-
background-image: url('images/star.gif');
130+
background-image: url('images/image.png');
135131
}
136132

137-
/* or */
138133
@font-face {
139-
src: url('fonts/font-bold.woff2') format('woff2');
140-
/* ...etc */
134+
src: url('fonts/font.woff2') format('woff2');
141135
}
142136
```
143137

144-
You can enable the `bundleAssetsFromCss` option:
138+
The assets are emitted as rollup assets, and the paths are updated to the rollup output paths:
145139

146-
```js
147-
rollupPluginHTML({
148-
bundleAssetsFromCss: true,
149-
// ...etc
150-
});
140+
```html
141+
<html>
142+
<head>
143+
<link rel="apple-touch-icon" sizes="180x180" href="assets/icon-a-XOCPHCrV.png" />
144+
<link rel="icon" type="image/png" sizes="32x32" href="assets/icon-b-BgQHKcRn.png" />
145+
<link rel="mask-icon" href="assets/icon-c-BCCvKrTe.svg" color="#3f93ce" />
146+
<link rel="manifest" href="assets/webmanifest-BkrOR1WG.json" />
147+
<link rel="stylesheet" href="assets/styles-CF2Iy5n1.css" />
148+
<link rel="preload" href="assets/font-f0mNRiTD.woff2" as="font" type="font/woff2" crossorigin />
149+
</head>
150+
<body>
151+
<img src="assets/image-C4yLPiIL.png" />
152+
</body>
153+
</html>
151154
```
152155

153-
And those assets will get output to the `assets/` dir, and the source css file will get updated with the output locations of those assets, e.g.:
154-
155156
```css
156157
body {
157-
background-image: url('assets/star-P4TYRBwL.gif');
158+
background-image: url('assets/image-C4yLPiIL.png');
158159
}
159160

160-
/* or */
161161
@font-face {
162-
src: url('assets/font-bold-f0mNRiTD.woff2') format('woff2');
163-
/* ...etc */
162+
src: url('assets/font-f0mNRiTD.woff2') format('woff2');
164163
}
165164
```
166165

166+
The images are deduped when same ones are referenced in different tags and files.
167+
168+
You can configure the output paths via [assetFileNames option](https://rollupjs.org/guide/en/#outputassetfilenames) (by default `assets/[name]-[hash][extname]` at the time of writing).
169+
The hash in the asset filenames enables long term caching.
170+
171+
#### Disable assets bundling
172+
173+
To turn off bundling assets completely, set the `extractAssets` option to false:
174+
175+
```js
176+
import { rollupPluginHTML as html } from '@web/rollup-plugin-html';
177+
178+
export default {
179+
input: 'index.html',
180+
output: { dir: 'dist' },
181+
plugins: [
182+
html({
183+
extractAssets: false,
184+
}),
185+
],
186+
};
187+
```
188+
189+
#### Enable legacy behavior
190+
191+
For smooth migration we added legacy modes:
192+
193+
- `extractAssets: 'legacy-html'` is the same as 2.x.x behavior when `bundleAssetsFromCss: false`
194+
- `extractAssets: 'legacy-html-and-css'` is the same as 2.x.x behavior when `bundleAssetsFromCss: true`
195+
196+
The 2.x.x behavior was limited to `<link rel="stylesheet" href="..." />` only and the assets referenced in the CSS files were hardcoded to be put into the nested `assets/` dir.
197+
198+
We recommend to use legacy modes only during the migration of large multi-project codebases to 3.x.x in order to temporarily keep the old behavior until all projects can reliably use the new behavior, while at the same time upgrading tools centrally to the new version of `@web/rollup-plugin-html`.
199+
167200
### Handling absolute paths
168201

169202
If your HTML file contains any absolute paths they will be resolved against the current working directory. You can set a different root directory in the config. Input paths will be resolved relative to this root directory as well.
@@ -361,7 +394,7 @@ export interface RollupPluginHTMLOptions {
361394
/** Transform HTML file before output. */
362395
transformHtml?: TransformHtmlFunction | TransformHtmlFunction[];
363396
/** Whether to extract and bundle assets referenced in HTML. Defaults to true. */
364-
extractAssets?: boolean;
397+
extractAssets?: boolean | 'legacy-html' | 'legacy-html-and-css';
365398
/** Whether to ignore assets referenced in HTML and CSS with glob patterns. */
366399
externalAssets?: string | string[];
367400
/** Define a full absolute url to your site (e.g. https://domain.com) */

package-lock.json

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Migration
2+
3+
## From version 2.x.x to 3.x.x
4+
5+
Remove `bundleAssetsFromCss` configuration option, now we bundle assets referenced in CSS by default.
6+
7+
Check all output assets since now we handle all link `rel` types, specifically:
8+
9+
- icon
10+
- apple-touch-icon
11+
- mask-icon
12+
- stylesheet
13+
- manifest
14+
- preload
15+
- prefetch
16+
- modulepreload
17+
18+
If any of them reference external assets or assets that don't need to be bundled, you can exclude such assets using the `externalAssets` configuration option.
19+
20+
For old behavior which is only recommeneded during the migration you can check legacy modes `extractAssets: 'legacy-html'` and `extractAssets: 'legacy-html-and-css'` in the documentation.

packages/rollup-plugin-html/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@
5252
"picomatch": "^2.2.2"
5353
},
5454
"devDependencies": {
55+
"@prettier/sync": "^0.6.1",
5556
"@types/html-minifier-terser": "^7.0.0",
5657
"@types/picomatch": "^2.2.1",
58+
"@types/prettier": "^3.0.0",
59+
"prettier": "^3.6.2",
5760
"rollup": "^4.4.0"
5861
}
5962
}

packages/rollup-plugin-html/src/RollupPluginHTMLOptions.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export interface RollupPluginHTMLOptions {
2727
transformAsset?: TransformAssetFunction | TransformAssetFunction[];
2828
/** Transform HTML file before output. */
2929
transformHtml?: TransformHtmlFunction | TransformHtmlFunction[];
30-
/** Whether to extract and bundle assets referenced in HTML. Defaults to true. */
31-
extractAssets?: boolean;
30+
/** Whether to extract and bundle assets referenced in HTML and CSS. Defaults to true. */
31+
extractAssets?: boolean | 'legacy-html' | 'legacy-html-and-css';
3232
/** Whether to ignore assets referenced in HTML and CSS with glob patterns. */
3333
externalAssets?: string | string[];
3434
/** Define a full absolute url to your site (e.g. https://domain.com) */
@@ -43,8 +43,6 @@ export interface RollupPluginHTMLOptions {
4343
absolutePathPrefix?: string;
4444
/** When set to true, will insert meta tags for CSP and add script-src values for inline scripts by sha256-hashing the contents */
4545
strictCSPInlineScripts?: boolean;
46-
/** Bundle assets reference from CSS via `url` */
47-
bundleAssetsFromCss?: boolean;
4846
}
4947

5048
export interface GeneratedBundle {

packages/rollup-plugin-html/src/assets/utils.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@ import { findElements, getTagName, getAttribute } from '@web/parse5-utils';
55
import { createError } from '../utils.js';
66
import { serialize } from 'v8';
77

8-
const hashedLinkRels = ['stylesheet'];
9-
const linkRels = [...hashedLinkRels, 'icon', 'manifest', 'apple-touch-icon', 'mask-icon'];
8+
const assetLinkRels = [
9+
'icon',
10+
'apple-touch-icon',
11+
'mask-icon',
12+
'stylesheet',
13+
'manifest',
14+
'preload',
15+
'prefetch',
16+
'modulepreload',
17+
];
18+
const legacyHashedLinkRels = ['stylesheet'];
19+
const assetMetaProperties = ['og:image'];
1020

1121
function getSrcSetUrls(srcset: string) {
1222
if (!srcset) {
@@ -41,13 +51,14 @@ function isAsset(node: Element) {
4151
path = extractFirstUrlOfSrcSet(node) ?? '';
4252
}
4353
break;
44-
case 'link':
45-
if (linkRels.includes(getAttribute(node, 'rel') ?? '')) {
54+
case 'link': {
55+
if (assetLinkRels.includes(getAttribute(node, 'rel') ?? '')) {
4656
path = getAttribute(node, 'href') ?? '';
4757
}
4858
break;
59+
}
4960
case 'meta':
50-
if (getAttribute(node, 'property') === 'og:image' && getAttribute(node, 'content')) {
61+
if (assetMetaProperties.includes(getAttribute(node, 'property') ?? '')) {
5162
path = getAttribute(node, 'content') ?? '';
5263
}
5364
break;
@@ -70,16 +81,24 @@ function isAsset(node: Element) {
7081
}
7182
}
7283

73-
export function isHashedAsset(node: Element) {
84+
export function isHashedAsset(
85+
node: Element,
86+
extractAssets: boolean | 'legacy-html' | 'legacy-html-and-css',
87+
) {
7488
switch (getTagName(node)) {
7589
case 'img':
7690
return true;
7791
case 'source':
7892
return true;
7993
case 'script':
8094
return true;
81-
case 'link':
82-
return hashedLinkRels.includes(getAttribute(node, 'rel')!);
95+
case 'link': {
96+
if (extractAssets === 'legacy-html' || extractAssets === 'legacy-html-and-css') {
97+
return legacyHashedLinkRels.includes(getAttribute(node, 'rel') ?? '');
98+
} else {
99+
return true;
100+
}
101+
}
83102
case 'meta':
84103
return true;
85104
default:

0 commit comments

Comments
 (0)