Skip to content

Commit c333304

Browse files
committed
fix: resolve public path base on html-webpack-plugin and webpack config
1 parent cea06cd commit c333304

File tree

7 files changed

+115
-8
lines changed

7 files changed

+115
-8
lines changed

.eslintrc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
}],
3333
"import/extensions": ["error", {
3434
"js": "never",
35-
"ts": "never",
36-
}]
35+
"ts": "never"
36+
}],
37+
"class-methods-use-this": "off"
3738
},
3839
"overrides": [
3940
{
@@ -51,15 +52,15 @@
5152
"airbnb-base",
5253
"plugin:jest/recommended",
5354
"plugin:@typescript-eslint/eslint-recommended",
54-
"plugin:@typescript-eslint/recommended",
55+
"plugin:@typescript-eslint/recommended"
5556
],
5657
"env": { "node": true },
5758
"rules": {
5859
"@typescript-eslint/no-var-requires": ["off"],
5960
"no-console": ["off"],
6061
"import/extensions": ["error", {
6162
"js": "never",
62-
"ts": "never",
63+
"ts": "never"
6364
}]
6465
}
6566
}

__tests__/HtmlInlineScriptPlugin.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import ignoreHtmlsConfig from './cases/ignore-htmls/webpack.config';
1414
import ignoreScriptsAndHtmlsConfig from './cases/ignore-scripts-and-htmls/webpack.config';
1515
import filenameWithSpecialCharactersConfig from './cases/filename-with-special-characters/webpack.config';
1616
import escapeScriptTagEndConfig from './cases/escape-script-end-tag/webpack.config';
17+
import htmlInsideSubfolderConfig from './cases/html-inside-subfolder/webpack.config';
1718

1819
describe('HtmlInlineScriptPlugin', () => {
1920
it('should build simple webpack config without error', async () => {
@@ -205,6 +206,42 @@ describe('HtmlInlineScriptPlugin', () => {
205206
await webpackPromise;
206207
});
207208

209+
it('should build webpack config that outputs html file inside subfolder without error', async () => {
210+
const webpackPromise = new Promise((resolve) => {
211+
const compiler = webpack(htmlInsideSubfolderConfig);
212+
213+
compiler.run((error, stats) => {
214+
expect(error).toBeNull();
215+
216+
const statsErrors = stats?.compilation.errors;
217+
expect(statsErrors?.length).toBe(0);
218+
219+
const result = fs.readFileSync(
220+
path.join(__dirname, 'cases/html-inside-subfolder/dist/frontend/index.html'),
221+
'utf8',
222+
);
223+
224+
const expected = fs.readFileSync(
225+
path.join(__dirname, 'cases/html-inside-subfolder/expected/frontend/index.html'),
226+
'utf8',
227+
);
228+
expect(result).toBe(expected);
229+
230+
const expectedParentFileList = fs.readdirSync(path.join(__dirname, 'cases/html-inside-subfolder/expected/'));
231+
const generatedParentFileList = fs.readdirSync(path.join(__dirname, 'cases/html-inside-subfolder/dist/'));
232+
expect(expectedParentFileList.sort()).toEqual(generatedParentFileList.sort());
233+
234+
const expectedChildFileList = fs.readdirSync(path.join(__dirname, 'cases/html-inside-subfolder/expected/'));
235+
const generatedChildFileList = fs.readdirSync(path.join(__dirname, 'cases/html-inside-subfolder/dist/'));
236+
expect(expectedChildFileList.sort()).toEqual(generatedChildFileList.sort());
237+
238+
resolve(true);
239+
});
240+
});
241+
242+
await webpackPromise;
243+
});
244+
208245
it('should inline filename with spacial characters without error', async () => {
209246
const webpackPromise = new Promise((resolve) => {
210247
const compiler = webpack(filenameWithSpecialCharactersConfig);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="language" content="English"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="minimum-scale=1,initial-scale=1,width=device-width,shrink-to-fit=no"/><title>webpack test</title><script defer="defer">console.log("Hello world");</script></head><body><p>This is minimal code to demonstrate webpack usage</p></body></html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6+
<meta name="language" content="English" />
7+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
8+
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
9+
<title>webpack test</title>
10+
</head>
11+
<body>
12+
<p>This is minimal code to demonstrate webpack usage</p>
13+
</body>
14+
</html>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line no-console
2+
console.log('Hello world');
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import path from 'path';
2+
import type { Configuration } from 'webpack';
3+
import HtmlWebpackPlugin from 'html-webpack-plugin';
4+
import Self from '../../../dist';
5+
6+
const config: Configuration = {
7+
mode: 'production',
8+
entry: path.join(__dirname, './fixtures/index.js'),
9+
output: {
10+
path: path.join(__dirname, './dist'),
11+
filename: '[name].js'
12+
},
13+
plugins: [
14+
new HtmlWebpackPlugin({
15+
template: path.resolve(__dirname, './fixtures/index.html'),
16+
filename: path.join(__dirname, './dist/frontend/index.html')
17+
}),
18+
new Self()
19+
]
20+
};
21+
22+
export default config;

src/HtmlInlineScriptPlugin.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import { Compilation } from 'webpack';
23
import type { Compiler, WebpackPluginInstance } from 'webpack';
34
import htmlWebpackPlugin from 'html-webpack-plugin';
@@ -37,7 +38,7 @@ class HtmlInlineScriptPlugin implements WebpackPluginInstance {
3738
const {
3839
scriptMatchPattern = [/.+[.]js$/],
3940
htmlMatchPattern = [/.+[.]html$/],
40-
assetPreservePattern = [],
41+
assetPreservePattern = []
4142
} = options;
4243

4344
this.scriptMatchPattern = scriptMatchPattern;
@@ -59,7 +60,6 @@ class HtmlInlineScriptPlugin implements WebpackPluginInstance {
5960
return this.assetPreservePattern.some((test) => assetName.match(test));
6061
}
6162

62-
6363
shouldProcessHtml(
6464
templateName: string
6565
): boolean {
@@ -102,18 +102,48 @@ class HtmlInlineScriptPlugin implements WebpackPluginInstance {
102102
};
103103
}
104104

105-
apply(compiler: Compiler): void {
106-
let publicPath = compiler.options?.output?.publicPath as string || '';
105+
getPublicPath(
106+
compilation: Compilation,
107+
htmlFileName: string,
108+
customPublicPath: string
109+
): string {
110+
const webpackPublicPath = compilation.getAssetPath(
111+
compilation.outputOptions.publicPath as string,
112+
{ hash: compilation.hash }
113+
);
114+
// Webpack 5 introduced "auto" as default value
115+
const isPublicPathDefined = webpackPublicPath !== 'auto';
116+
117+
let publicPath = '';
118+
119+
if (customPublicPath !== 'auto') {
120+
// If the html-webpack-plugin options contain a custom public path uset it
121+
publicPath = customPublicPath;
122+
} else if (isPublicPathDefined) {
123+
// If a hard coded public path exists in webpack config use it
124+
publicPath = webpackPublicPath;
125+
} else if (compilation.options.output.path) {
126+
// If no public path for webpack and html-webpack-plugin was set get a relative url path
127+
publicPath = path.relative(
128+
path.resolve(compilation.options.output.path, path.dirname(htmlFileName)),
129+
compilation.options.output.path
130+
).split(path.sep).join('/');
131+
}
107132

108133
if (publicPath && !publicPath.endsWith('/')) {
109134
publicPath += '/';
110135
}
111136

137+
return publicPath;
138+
}
139+
140+
apply(compiler: Compiler): void {
112141
compiler.hooks.compilation.tap(`${PLUGIN_PREFIX}_compilation`, (compilation) => {
113142
const hooks = htmlWebpackPlugin.getHooks(compilation);
114143

115144
hooks.alterAssetTags.tap(`${PLUGIN_PREFIX}_alterAssetTags`, (data) => {
116145
const htmlFileName = data.plugin.options?.filename;
146+
const publicPath = this.getPublicPath(compilation, data.outputName, data.publicPath);
117147

118148
if (htmlFileName && !this.shouldProcessHtml(htmlFileName)) {
119149
this.ignoredHtmlFiles.push(htmlFileName);

0 commit comments

Comments
 (0)