Skip to content

Commit 5390033

Browse files
committed
feat: add support for the ?inline query by importing SVG file in JS as data URL
1 parent 7b6d3e9 commit 5390033

18 files changed

Lines changed: 196 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
# Change log
22

3-
## 4.11.2 (2025-01-05)
3+
## 4.12.0-beta.0 (2025-01-06)
44

5+
- feat: add support for the `?inline` query by importing SVG file in JS as data URL
6+
```js
7+
import file from './image.svg'; // import according the matched webpack config, defaults as output filename
8+
9+
import file from './image.svg?inline'; // import as UTF-8 data URL
10+
import file from './image.svg?inline=utf8'; // import as UTF-8 data URL
11+
import file from './image.svg?inline=base64'; // import as base64-encoded data URL
12+
```
513
- chore: update dependencies
614
- chore: update license to current date
715
- docs: add in readme the recipe "How to import SVG in JavaScript"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "html-bundler-webpack-plugin",
3-
"version": "4.11.2",
3+
"version": "4.12.0-beta.0",
44
"description": "Generates complete single-page or multi-page website from source assets. Build-in support for Markdown, Eta, EJS, Handlebars, Nunjucks, Pug. Alternative to html-webpack-plugin.",
55
"keywords": [
66
"html",

src/Plugin/AssetCompiler.js

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,8 @@ class AssetCompiler {
711711
*/
712712
afterResolve(resolveData) {
713713
const { request, contextInfo, dependencyType, createData, _bundlerPluginMeta: meta } = resolveData;
714-
const { resource } = createData;
715-
const [file] = resource.split('?', 1);
714+
const { resource, rawRequest } = createData;
715+
const [file, query] = resource.split('?', 2);
716716
// note: the contextInfo.issuer is the filename w/o a query
717717
const { issuer } = contextInfo;
718718

@@ -724,6 +724,51 @@ class AssetCompiler {
724724
if (meta.isLoaderImport || meta.isCSSStyleSheet || meta.isDependencyUrl || request.startsWith('data:')) return;
725725

726726
if (issuer) {
727+
// TODO: refactor into AssetInline
728+
const isSvgFile = this.assetInline.isSvgFile(resource);
729+
const isIssuerScript = this.pluginOption.isScript(issuer);
730+
//const isInlineSvg = this.assetInline.isInlineSvg(resource, issuer);
731+
const urlParams = new URLSearchParams(query);
732+
const inlineValue = urlParams.get('inline');
733+
const hasQueryInline = inlineValue != null;
734+
735+
if (isSvgFile && isIssuerScript && hasQueryInline) {
736+
if (this.IS_WEBPACK_VERSION_LOWER_5_96_0) {
737+
// TODO: refactor into Exceptions
738+
throw new Error(`\nThe support for the \`?inline\` query for SVG is available since Webpack >= 5.96.`);
739+
}
740+
741+
const availableValues = ['base64', 'utf8'];
742+
if (inlineValue !== '' && !availableValues.includes(inlineValue)) {
743+
// TODO: refactor into Exceptions
744+
throw new Error(
745+
`\nUnsupported \`?inline\` query value ${yellowBright(inlineValue)} in the request ${cyanBright(rawRequest)}\nAvailable values: ${yellowBright(availableValues.join(', '))} or ${yellowBright`undefined`}.`
746+
);
747+
}
748+
749+
/** @type {"base64" | false} encoding, false for `utf8`, see /webpack/lib/asset/AssetGenerator.js:encodeDataUri() */
750+
const encoding = inlineValue === 'base64' ? 'base64' : false;
751+
752+
const moduleGraph = this.compilation.moduleGraph;
753+
const dataUrlOptions = { encoding };
754+
const filename = undefined;
755+
const publicPath = undefined;
756+
const outputPath = undefined;
757+
const emit = false;
758+
759+
resolveData.createData.generator = new AssetGenerator(
760+
moduleGraph,
761+
dataUrlOptions,
762+
filename,
763+
publicPath,
764+
outputPath,
765+
emit
766+
);
767+
768+
resolveData.createData.type = ASSET_MODULE_TYPE_INLINE;
769+
resolveData.createData.parser = new AssetParser(true);
770+
}
771+
727772
const isIssuerStyle = this.pluginOption.isStyle(issuer);
728773
const parentModule = resolveData.dependencies[0]?._parentModule;
729774
const { isLoaderImport } = parentModule?.resourceResolveData?._bundlerPluginMeta || {};
@@ -852,11 +897,12 @@ class AssetCompiler {
852897
} else {
853898
// Webpack >= 5.96
854899
const moduleGraph = this.compilation.moduleGraph;
855-
const dataUrl = undefined;
900+
const dataUrlOptions = undefined;
856901
const publicPath = undefined;
857902
const outputPath = undefined;
858903
const emit = true;
859-
createData.generator = new AssetGenerator(moduleGraph, dataUrl, filename, publicPath, outputPath, emit);
904+
905+
createData.generator = new AssetGenerator(moduleGraph, dataUrlOptions, filename, publicPath, outputPath, emit);
860906
}
861907

862908
createData.parser = new AssetParser(false);

src/Plugin/AssetInline.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class AssetInline {
129129
*/
130130
isSvgFile(request) {
131131
const [file] = request.split('?', 1);
132-
return file.endsWith('.svg');
132+
return file.toLowerCase().endsWith('.svg');
133133
}
134134

135135
/**
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Test</title>
5+
<link href="style.css" rel="stylesheet">
6+
<script src="main.js" defer="defer"></script>
7+
</head>
8+
<body>
9+
<h1>Import SVG file in JavaScript!</h1>
10+
<b>SVG as filename</b>
11+
<div id="svg-filename"></div>
12+
13+
<b>SVG as base64-encoded data URL</b>
14+
<div id="svg-base64-data-url"></div>
15+
16+
<b>SVG as UTF-8 data URL</b>
17+
<div id="svg-utf8-data-url"></div>
18+
</body>
19+
</html>

test/cases/js-import-image-svg-uri-utf8/expected/main.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
h1 {
2+
color: red;
3+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Test</title>
5+
<link href="./style.css" rel="stylesheet">
6+
<script src="./main.js" defer="defer"></script>
7+
</head>
8+
<body>
9+
<h1>Import SVG file in JavaScript!</h1>
10+
<b>SVG as filename</b>
11+
<div id="svg-filename"></div>
12+
13+
<b>SVG as base64-encoded data URL</b>
14+
<div id="svg-base64-data-url"></div>
15+
16+
<b>SVG as UTF-8 data URL</b>
17+
<div id="svg-utf8-data-url"></div>
18+
</body>
19+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import svgFilename from './token.svg'; // import as output filename
2+
import svgBase64DataUrl from './token.svg?inline=base64'; // import as base64-encoded data URL
3+
import svgUtf8DataUrl from './token.svg?inline'; // import as UTF-8 data URL
4+
import svgUtf8DataUrl2 from './token.svg?inline=utf8'; // import as UTF-8 data URL
5+
//import svgSource from './token.svg?raw'; // import as source content
6+
7+
// filename
8+
let img1 = document.createElement('img');
9+
img1.setAttribute('src', svgFilename);
10+
document.getElementById('svg-filename').append(img1);
11+
12+
// base64-encoded data URL
13+
let img2 = document.createElement('img');
14+
img2.setAttribute('src', svgBase64DataUrl);
15+
document.getElementById('svg-base64-data-url').append(img2);
16+
17+
// // UTF-8 data URL
18+
let img3 = document.createElement('img');
19+
img3.setAttribute('src', svgUtf8DataUrl);
20+
document.getElementById('svg-utf8-data-url').append(img3);
21+
22+
console.log('>> svgFilename: ', svgFilename);
23+
console.log('>> svgBase64DataUrl: ', svgBase64DataUrl);
24+
console.log('>> svgUtf8DataUrl: ', svgUtf8DataUrl);
25+
console.log('>> svgUtf8DataUrl2: ', svgUtf8DataUrl2);

0 commit comments

Comments
 (0)