diff --git a/.changeset/early-geese-fly.md b/.changeset/early-geese-fly.md
new file mode 100644
index 0000000000..fc04a48e9b
--- /dev/null
+++ b/.changeset/early-geese-fly.md
@@ -0,0 +1,6 @@
+---
+'@web/rollup-plugin-import-meta-assets': minor
+---
+
+Add option `preserveDynamicStructure` that emits dynamic assets and rewrites the URL pattern to resolve the original dynamic path relative to the first emitted asset.
+It requires that the output preserves both filenames (no hashing) and directory structure from the dynamic expression onwards.
diff --git a/.changeset/ten-scissors-explain.md b/.changeset/ten-scissors-explain.md
new file mode 100644
index 0000000000..15edc585ca
--- /dev/null
+++ b/.changeset/ten-scissors-explain.md
@@ -0,0 +1,6 @@
+---
+'@web/rollup-plugin-html': minor
+---
+
+Support preserving the assets input structure in the output.
+Support transforming of assets found in CSS.
diff --git a/docs/docs/building/rollup-plugin-import-meta-assets.md b/docs/docs/building/rollup-plugin-import-meta-assets.md
index 3272a7be37..80a12d81f2 100644
--- a/docs/docs/building/rollup-plugin-import-meta-assets.md
+++ b/docs/docs/building/rollup-plugin-import-meta-assets.md
@@ -121,6 +121,52 @@ export default {
};
```
+### `preserveDynamicStructure`
+
+Type: `Boolean`
+Default: `false`
+
+When enabled, dynamic asset URLs (using template literals) are emitted to the Rollup pipeline and the URL pattern is rewritten to resolve relative to the first emitted asset.
+
+**Requirements:** The output must preserve both filenames (no hashing) and the directory structure from the dynamic expression onwards.
+If filenames are hashed or the directory structure changes, the runtime URL resolution will fail.
+
+This is useful when your application or CDN already has versioned URLs, so you don't need filename hashing.
+It also avoids generating a large switch statement in the output when you have many dynamic assets (e.g. an icon library).
+
+```js
+import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
+
+const projectRoot = process.cwd();
+
+export default {
+ input: 'src/index.js',
+ output: {
+ dir: 'output',
+ format: 'es',
+ // preserve original file paths, relative to the project root
+ assetFileNames: asset =>
+ path.relative(projectRoot, asset.originalFileNames[0]).split(path.sep).join('/'),
+ },
+ plugins: [
+ importMetaAssets({
+ preserveDynamicStructure: true,
+ }),
+ ],
+};
+```
+
+Given this source code:
+
+```js
+const icon = new URL(`./assets/icons/${category}/${name}.svg`, import.meta.url);
+```
+
+The plugin will:
+
+1. Emit all matching assets (e.g. `./assets/icons/outline/arrow.svg`, `./assets/icons/solid/check.svg`, etc..)
+2. Rewrite the URL to resolve relative to the first emitted asset
+
## Examples
Source directory:
diff --git a/package-lock.json b/package-lock.json
index a453ac4182..728c6aa44c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33798,6 +33798,12 @@
"node": ">=0.4"
}
},
+ "node_modules/xxhash-wasm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz",
+ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==",
+ "license": "MIT"
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -35130,7 +35136,8 @@
"html-minifier-terser": "^7.1.0",
"lightningcss": "^1.24.0",
"parse5": "^6.0.1",
- "picomatch": "^2.2.2"
+ "picomatch": "^2.2.2",
+ "xxhash-wasm": "^1.1.0"
},
"devDependencies": {
"@prettier/sync": "^0.6.1",
diff --git a/packages/rollup-plugin-html/package.json b/packages/rollup-plugin-html/package.json
index cc784b0fbe..6d4d1414c7 100644
--- a/packages/rollup-plugin-html/package.json
+++ b/packages/rollup-plugin-html/package.json
@@ -49,7 +49,8 @@
"html-minifier-terser": "^7.1.0",
"lightningcss": "^1.24.0",
"parse5": "^6.0.1",
- "picomatch": "^2.2.2"
+ "picomatch": "^2.2.2",
+ "xxhash-wasm": "^1.1.0"
},
"devDependencies": {
"@prettier/sync": "^0.6.1",
diff --git a/packages/rollup-plugin-html/src/RollupPluginHTMLOptions.ts b/packages/rollup-plugin-html/src/RollupPluginHTMLOptions.ts
index 884f669fb5..48e6fe977f 100644
--- a/packages/rollup-plugin-html/src/RollupPluginHTMLOptions.ts
+++ b/packages/rollup-plugin-html/src/RollupPluginHTMLOptions.ts
@@ -31,7 +31,7 @@ export interface RollupPluginHTMLOptions {
extractAssets?: boolean | 'legacy-html' | 'legacy-html-and-css';
/** Whether to bundle extracted CSS assets. Bundling is done via Lightning CSS. Defaults to true. */
bundleCss?: boolean;
- /** Whether to minify extracted CSS assets. Minificaiton is done via Lightning CSS. Defaults to false. */
+ /** Whether to minify extracted CSS assets. Minification is done via Lightning CSS. Defaults to false. */
minifyCss?: boolean;
/** Whether to ignore assets referenced in HTML and CSS with glob patterns. */
externalAssets?: string | string[];
diff --git a/packages/rollup-plugin-html/src/output/css.ts b/packages/rollup-plugin-html/src/output/css.ts
new file mode 100644
index 0000000000..95551190ec
--- /dev/null
+++ b/packages/rollup-plugin-html/src/output/css.ts
@@ -0,0 +1,89 @@
+import path from 'path';
+import type { OutputBundle, PluginContext } from 'rollup';
+import { toBrowserPath } from './utils.js';
+
+/**
+ * Regular expression to match asset URL placeholders in CSS content.
+ * Captures the hashes encoded as HEX strings like "abc123" from placeholders like "__ROLLUP_ASSET_URL_abc123__".
+ */
+const ASSET_URL_PLACEHOLDER_REGEX = /__ROLLUP_ASSET_URL_([a-f0-9]+)__/g;
+
+/**
+ * Creates a placeholder string for the given hash.
+ *
+ * @param hash - Hash encoded as a HEX string (e.g. "abc123")
+ * @returns Placeholder string like "__ROLLUP_ASSET_URL_abc123__"
+ */
+export function createAssetPlaceholder(hash: string): string {
+ return `__ROLLUP_ASSET_URL_${hash}__`;
+}
+
+/**
+ * Replaces all asset URL placeholders in CSS content with resolved paths.
+ *
+ * @param cssContent - The CSS content with placeholders
+ * @param resolver - Function that resolves a hash to the final path
+ * @returns CSS content with placeholders replaced
+ */
+export function replacePlaceholders(
+ cssContent: string,
+ resolver: (hash: string) => string | undefined,
+): string {
+ return cssContent.replace(ASSET_URL_PLACEHOLDER_REGEX, (match, hash) => {
+ const resolvedPath = resolver(hash);
+ return resolvedPath ?? match;
+ });
+}
+
+/**
+ * Calculates the path from a CSS file to a referenced asset in the output.
+ * If publicPath is provided, returns an absolute path. Otherwise returns a relative path.
+ *
+ * @param cssFilePath - The CSS file's path in the bundle (e.g. 'styles/main.css')
+ * @param assetFilePath - The asset's path in the bundle (e.g. 'assets/image.png')
+ * @param publicPath - Optional public path prefix (e.g. '/static/')
+ * @returns Absolute path if publicPath provided, otherwise relative path from CSS to asset
+ */
+export function calculateRelativePath(
+ cssFilePath: string,
+ assetFilePath: string,
+ publicPath?: string,
+): string {
+ if (publicPath) {
+ return toBrowserPath(path.join(publicPath, assetFilePath));
+ }
+
+ const cssDir = path.dirname(cssFilePath);
+ const relativePath = path.relative(cssDir, assetFilePath);
+ return toBrowserPath(relativePath);
+}
+
+/**
+ * Processes all CSS files in the bundle, replacing placeholders with resolved paths.
+ *
+ * @param {PluginContext} pluginContext - The Rollup plugin context
+ * @param {OutputBundle} bundle - The Rollup output bundle
+ * @param {Record} assetsInCssByHash - Map of asset hashes to their Rollup refs for assets found in CSS
+ * @param {string} [publicPath] - Optional public path prefix for absolute URLs (e.g. '/static/')
+ */
+export function processCssAssets(
+ pluginContext: PluginContext,
+ bundle: OutputBundle,
+ assetsInCssByHash: Record,
+ publicPath?: string,
+): void {
+ for (const [filePath, asset] of Object.entries(bundle)) {
+ if (asset.type !== 'asset' || !filePath.endsWith('.css')) continue;
+
+ const cssContent =
+ typeof asset.source === 'string' ? asset.source : Buffer.from(asset.source).toString('utf-8');
+
+ const resolvedContent = replacePlaceholders(cssContent, (hash: string) => {
+ const ref = assetsInCssByHash[hash]!.ref;
+ const assetFilePath = pluginContext.getFileName(ref);
+ return calculateRelativePath(filePath, assetFilePath, publicPath);
+ });
+
+ asset.source = resolvedContent;
+ }
+}
diff --git a/packages/rollup-plugin-html/src/output/emitAssets.ts b/packages/rollup-plugin-html/src/output/emitAssets.ts
index 377db1f25a..9cadc152fa 100644
--- a/packages/rollup-plugin-html/src/output/emitAssets.ts
+++ b/packages/rollup-plugin-html/src/output/emitAssets.ts
@@ -2,15 +2,17 @@ import { PluginContext } from 'rollup';
import path from 'path';
import { bundleAsync, transform } from 'lightningcss';
import fs from 'fs';
+import xxhash from 'xxhash-wasm';
import { InputAsset, InputData } from '../input/InputData';
-import { toBrowserPath } from './utils.js';
import { createAssetPicomatchMatcher } from '../assets/utils.js';
import { RollupPluginHTMLOptions, TransformAssetFunction } from '../RollupPluginHTMLOptions';
+import { createAssetPlaceholder } from './css.js';
export interface EmittedAssets {
static: Map;
hashed: Map;
+ assetsInCssByHash: Record;
}
const allowedFileExtensions = [
@@ -40,6 +42,8 @@ export async function emitAssets(
inputs: InputData[],
options: RollupPluginHTMLOptions,
) {
+ const { create64 } = await xxhash();
+
const extractAssets = options.extractAssets ?? true;
const bundleCss = options.bundleCss ?? true;
const minifyCss = options.minifyCss ?? false;
@@ -56,9 +60,28 @@ export async function emitAssets(
transforms.push(options.transformAsset);
}
}
+
+ async function getTransformedAsset(content: Buffer, filePath: string): Promise {
+ let source: Buffer = content;
+ for (const transform of transforms) {
+ const result = await transform(content, filePath);
+ if (result != null) {
+ source = typeof result === 'string' ? Buffer.from(result, 'utf-8') : result;
+ }
+ }
+ return source;
+ }
+
const staticAssets: InputAsset[] = [];
const hashedAssets: InputAsset[] = [];
+ let assetsInCssCounter = 0;
+ const assetsInCssByAbsPath: Record<
+ string,
+ { tempPlaceholder: string; ref?: string; outputPath?: string; hash?: string }
+ > = {};
+ const assetsInCssByHash: Record = {};
+
for (const input of inputs) {
for (const asset of input.assets) {
if (asset.hashed) {
@@ -75,20 +98,10 @@ export async function emitAssets(
for (const asset of allAssets) {
const map = asset.hashed ? emittedHashedAssets : emittedStaticAssets;
if (!map.has(asset.filePath)) {
- let source: Buffer = asset.content;
-
- // run user's transform functions
- for (const transform of transforms) {
- const result = await transform(asset.content, asset.filePath);
- if (result != null) {
- source = typeof result === 'string' ? Buffer.from(result, 'utf-8') : result;
- }
- }
-
+ let source = await getTransformedAsset(asset.content, asset.filePath);
let ref: string;
let basename = path.basename(asset.filePath);
const isExternal = createAssetPicomatchMatcher(options.externalAssets);
- const emittedExternalAssets = new Map();
if (asset.hashed) {
if (basename.endsWith('.css') && extractAssets) {
const { code } = await (bundleCss ? bundleAsync : transform)({
@@ -99,69 +112,90 @@ export async function emitAssets(
Url: url => {
// Support foo.svg#bar
// https://www.w3.org/TR/html4/types.html#:~:text=ID%20and%20NAME%20tokens%20must,tokens%20defined%20by%20other%20attributes.
- const [filePath, idRef] = url.url.split('#');
-
- if (shouldHandleAsset(filePath) && !isExternal(filePath)) {
- // Read the asset file, get the asset from the source location on the FS using asset.filePath
- const assetLocation = path.resolve(path.dirname(asset.filePath), filePath);
- const assetContent = fs.readFileSync(assetLocation);
-
- // Avoid duplicates
- if (!emittedExternalAssets.has(assetLocation)) {
- const basename = path.basename(filePath);
- const fileRef = this.emitFile({
- type: 'asset',
- name: extractAssetsLegacyCss ? `assets/${basename}` : basename,
- source: assetContent,
- });
- const emittedAssetFilepath = this.getFileName(fileRef);
- const emittedAssetBasename = path.basename(emittedAssetFilepath);
- emittedExternalAssets.set(assetLocation, emittedAssetFilepath);
- // Update the URL in the original CSS file to point to the emitted asset file
- if (extractAssetsLegacyCss) {
- url.url = `assets/${emittedAssetBasename}`;
- } else {
- if (options.publicPath) {
- url.url = toBrowserPath(
- path.join(options.publicPath, emittedAssetFilepath),
- );
- } else {
- url.url = emittedAssetBasename;
- }
- }
- if (idRef) {
- url.url = `${url.url}#${idRef}`;
- }
- } else {
- const emittedAssetFilepath = emittedExternalAssets.get(assetLocation);
- const emittedAssetBasename = path.basename(emittedAssetFilepath);
- if (extractAssetsLegacyCss) {
- url.url = `assets/${emittedAssetBasename}`;
- } else {
- if (options.publicPath) {
- url.url = toBrowserPath(
- path.join(options.publicPath, emittedAssetFilepath),
- );
- } else {
- url.url = emittedAssetBasename;
- }
- }
- if (idRef) {
- url.url = `${url.url}#${idRef}`;
- }
+ const [srcAssetPath, srcAssetId] = url.url.split('#');
+
+ if (shouldHandleAsset(srcAssetPath) && !isExternal(srcAssetPath)) {
+ const assetAbsPath = path.resolve(path.dirname(asset.filePath), srcAssetPath);
+
+ let assetInCss = assetsInCssByAbsPath[assetAbsPath];
+
+ if (!assetInCss) {
+ // Avoid duplicates
+ assetsInCssCounter++;
+ assetInCss = {
+ tempPlaceholder: createAssetPlaceholder(assetsInCssCounter.toString()),
+ ref: undefined,
+ hash: undefined,
+ };
+ assetsInCssByAbsPath[assetAbsPath] = assetInCss;
}
+
+ url.url = srcAssetId
+ ? `${assetInCss.tempPlaceholder}#${srcAssetId}`
+ : assetInCss.tempPlaceholder;
}
+
return url;
},
},
});
- const codeBuffer = Buffer.from(code);
+
+ let codeString = code.toString();
+
+ for (const assetInCssAbsPath of Object.keys(assetsInCssByAbsPath)) {
+ const assetInCss = assetsInCssByAbsPath[assetInCssAbsPath];
+
+ if (!assetInCss.ref) {
+ const basename = path.basename(assetInCssAbsPath);
+ const content = await fs.promises.readFile(assetInCssAbsPath);
+ const transformedContent = await getTransformedAsset(content, assetInCssAbsPath);
+ const ref = this.emitFile({
+ type: 'asset',
+ name: extractAssetsLegacyCss ? `assets/${basename}` : basename,
+ originalFileName: assetInCssAbsPath,
+ source: transformedContent,
+ });
+ assetInCss.ref = ref;
+ assetInCss.outputPath = this.getFileName(ref);
+ if (!extractAssetsLegacyCss) {
+ assetInCss.hash = create64()
+ .update(transformedContent)
+ .update('\0')
+ .update(assetInCss.outputPath)
+ .digest()
+ .toString(16)
+ .padStart(16, '0');
+ }
+ }
+
+ if (extractAssetsLegacyCss) {
+ const outputName = path.basename(assetInCss.outputPath!);
+ codeString = codeString.replaceAll(
+ assetInCss.tempPlaceholder,
+ `assets/${outputName}`,
+ );
+ } else {
+ const hash = assetInCss.hash!;
+ assetsInCssByHash[hash] = { ref: assetInCss.ref };
+ codeString = codeString.replaceAll(
+ assetInCss.tempPlaceholder,
+ createAssetPlaceholder(hash),
+ );
+ }
+ }
+
+ const codeBuffer = Buffer.from(codeString);
if (!asset.content.equals(codeBuffer)) {
source = codeBuffer;
}
}
- ref = this.emitFile({ type: 'asset', name: basename, source });
+ ref = this.emitFile({
+ type: 'asset',
+ name: basename,
+ originalFileName: asset.filePath,
+ source,
+ });
} else {
// ensure the output filename is unique
let i = 1;
@@ -179,5 +213,5 @@ export async function emitAssets(
}
}
- return { static: emittedStaticAssets, hashed: emittedHashedAssets };
+ return { static: emittedStaticAssets, hashed: emittedHashedAssets, assetsInCssByHash };
}
diff --git a/packages/rollup-plugin-html/src/output/getEntrypointBundles.ts b/packages/rollup-plugin-html/src/output/getEntrypointBundles.ts
index 12c1bbb5ca..871f009de3 100644
--- a/packages/rollup-plugin-html/src/output/getEntrypointBundles.ts
+++ b/packages/rollup-plugin-html/src/output/getEntrypointBundles.ts
@@ -74,9 +74,9 @@ export function getEntrypointBundles(params: GetEntrypointBundlesParams) {
outputDir,
fileOutputDir: options.dir ?? '',
htmlFileName,
- fileName: chunkOrAsset.fileName,
+ fileName: chunk.fileName,
});
- entrypoints.push({ importPath, chunk: chunkOrAsset, attributes: found.attributes });
+ entrypoints.push({ importPath, chunk, attributes: found.attributes });
}
}
}
diff --git a/packages/rollup-plugin-html/src/rollupPluginHTML.ts b/packages/rollup-plugin-html/src/rollupPluginHTML.ts
index 388c329675..0207cb50f3 100644
--- a/packages/rollup-plugin-html/src/rollupPluginHTML.ts
+++ b/packages/rollup-plugin-html/src/rollupPluginHTML.ts
@@ -14,6 +14,7 @@ import {
} from './RollupPluginHTMLOptions.js';
import { createError, NOOP_IMPORT } from './utils.js';
import { emitAssets } from './output/emitAssets.js';
+import { processCssAssets } from './output/css.js';
export interface RollupPluginHtml extends Plugin {
api: {
@@ -140,6 +141,9 @@ export function rollupPluginHTML(pluginOptions: RollupPluginHTMLOptions = {}): R
generatedBundles.push({ name: 'default', options, bundle });
const emittedAssets = await emitAssets.call(this, inputs, pluginOptions);
+
+ processCssAssets(this, bundle, emittedAssets.assetsInCssByHash, pluginOptions.publicPath);
+
const outputs = await createHTMLOutput({
outputDir: path.resolve(options.dir),
inputs,
@@ -199,6 +203,14 @@ export function rollupPluginHTML(pluginOptions: RollupPluginHTMLOptions = {}): R
}
const emittedAssets = await emitAssets.call(this, inputs, pluginOptions);
+
+ processCssAssets(
+ this,
+ bundle,
+ emittedAssets.assetsInCssByHash,
+ pluginOptions.publicPath,
+ );
+
const outputs = await createHTMLOutput({
outputDir: path.resolve(options.dir),
inputs,
diff --git a/packages/rollup-plugin-html/test/rollup-plugin-html.test.ts b/packages/rollup-plugin-html/test/rollup-plugin-html.test.ts
index 564cb96c28..4681ce60bd 100644
--- a/packages/rollup-plugin-html/test/rollup-plugin-html.test.ts
+++ b/packages/rollup-plugin-html/test/rollup-plugin-html.test.ts
@@ -2,7 +2,15 @@ import { rollup, OutputChunk, OutputOptions, Plugin } from 'rollup';
import { expect } from 'chai';
import path from 'path';
import { rollupPluginHTML } from '../src/index.js';
-import { html, css, js, svg, generateTestBundle, createApp, cleanApp } from './utils.js';
+import {
+ html,
+ css,
+ js,
+ svg,
+ generateTestBundle,
+ createApp,
+ cleanApp,
+} from '../../../test-utils/rollup-test-utils.js';
const outputConfig: OutputOptions = {
format: 'es',
@@ -2115,20 +2123,20 @@ describe('rollup-plugin-html', () => {
expect(assets).to.have.keys([
'assets/font-normal-Cht9ZB76.woff2',
'assets/font-bold-eQjSonqH.woff2',
- 'assets/styles-Dhs3ufep.css',
+ 'assets/styles-CecXjwpw.css',
'index.html',
]);
expect(assets['index.html']).to.equal(html`
-
+
`);
- expect(assets['assets/styles-Dhs3ufep.css']).to.equal(css`
+ expect(assets['assets/styles-CecXjwpw.css']).to.equal(css`
@font-face {
font-family: Font;
src: url('font-normal-Cht9ZB76.woff2') format('woff2');
@@ -2280,20 +2288,20 @@ describe('rollup-plugin-html', () => {
expect(assets).to.have.keys([
'assets/font-normal-Cht9ZB76.woff2',
'assets/font-bold-eQjSonqH.woff2',
- 'assets/styles-Dhs3ufep.css',
+ 'assets/styles-CecXjwpw.css',
'index.html',
]);
expect(assets['index.html']).to.equal(html`
-
+
`);
- expect(assets['assets/styles-Dhs3ufep.css']).to.equal(css`
+ expect(assets['assets/styles-CecXjwpw.css']).to.equal(css`
@font-face {
font-family: Font;
src: url('font-normal-Cht9ZB76.woff2') format('woff2');
@@ -2445,22 +2453,22 @@ describe('rollup-plugin-html', () => {
expect(assets).to.have.keys([
'assets/font-normal-Cht9ZB76.woff2',
- 'assets/styles-a-jFIfrzm8.css',
- 'assets/styles-b-B-8m1N7T.css',
+ 'assets/styles-a-rwPD8h6c.css',
+ 'assets/styles-b-DbSntrij.css',
'index.html',
]);
expect(assets['index.html']).to.equal(html`
-
-
+
+
`);
- expect(assets['assets/styles-a-jFIfrzm8.css']).to.equal(css`
+ expect(assets['assets/styles-a-rwPD8h6c.css']).to.equal(css`
@font-face {
font-family: Font;
src: url('font-normal-Cht9ZB76.woff2') format('woff2');
@@ -2470,7 +2478,7 @@ describe('rollup-plugin-html', () => {
}
`);
- expect(assets['assets/styles-b-B-8m1N7T.css']).to.equal(css`
+ expect(assets['assets/styles-b-DbSntrij.css']).to.equal(css`
@font-face {
font-family: Font2;
src: url('font-normal-Cht9ZB76.woff2') format('woff2');
@@ -2557,20 +2565,20 @@ describe('rollup-plugin-html', () => {
'assets/star-CXig10q7.png',
'assets/star-CwhgM_z4.svg',
'assets/star-CKbh5mKn.webp',
- 'assets/styles-mywkihBc.css',
+ 'assets/styles-BDpW-xH8.css',
'index.html',
]);
expect(assets['index.html']).to.equal(html`
-
+
`);
- expect(assets['assets/styles-mywkihBc.css']).to.equal(css`
+ expect(assets['assets/styles-BDpW-xH8.css']).to.equal(css`
#a {
background-image: url('star-D_LO5feX.avif');
}
@@ -2806,7 +2814,7 @@ describe('rollup-plugin-html', () => {
expect(assets).to.have.keys([
'assets/image-a-XOCPHCrV.png',
'assets/image-b-BgQHKcRn.png',
- 'assets/styles-Bv-4gk2N.css',
+ 'assets/styles-ChLnYIUS.css',
'assets/webmanifest-BkrOR1WG.json',
'index.html',
]);
@@ -2819,7 +2827,7 @@ describe('rollup-plugin-html', () => {
-
+
@@ -2832,7 +2840,7 @@ describe('rollup-plugin-html', () => {