Skip to content

Commit c91de3a

Browse files
committed
fix: serialization issue when used the cache.type = filesystem, #73
1 parent bc12d1f commit c91de3a

26 files changed

Lines changed: 278 additions & 81 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change log
22

3+
## 3.4.12 (2024-01-29)
4+
5+
- fix: serialization issue when used the `cache.type = 'filesystem'`
6+
- fix: missing output js files after second run build when used the `cache.type = 'filesystem'`
7+
38
## 3.4.11 (2024-01-22)
49

510
- fix: error by resolving url() in the CSS file defined in the entry option

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": "3.4.11",
3+
"version": "3.4.12",
44
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
55
"keywords": [
66
"html",

src/Plugin/AssetCompiler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ class AssetCompiler {
275275
AssetEntry.init({ compilation, entryLibrary: this.entryLibrary, collection: Collection });
276276
AssetTrash.init(compilation);
277277
CssExtractModule.init(compilation);
278-
Collection.init(compilation, AssetCompiler.getHooks(compilation));
278+
Collection.init({ compilation, assetEntry: AssetEntry, hooks: AssetCompiler.getHooks(compilation) });
279279

280280
Resolver.init({
281281
fs,

src/Plugin/AssetEntry.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ const loader = require.resolve('../Loader');
3131
* [name], [base], [path], [ext], [id], [contenthash], [contenthash:nn]
3232
* See https://webpack.js.org/configuration/output/#outputfilename
3333
* @property {Function} filenameFn The function to generate the output filename dynamically.
34-
* TODO: remove filename or assetFile, currently the assetFile is undeined (unused?)
35-
* @property {string=} assetFile The output asset file with the relative path by webpack output path.
36-
* Note: the method compilation.emitAsset() use this file as the key of an asset object
37-
* and save the file relative by output path, defined in webpack.options.output.path.
3834
* @property {string} resource The absolute import file with a query.
3935
* @property {string} importFile The original import entry file.
4036
* @property {string} sourceFile The absolute import file only w/o a query.
@@ -114,7 +110,7 @@ class AssetEntry {
114110

115111
for (let name in pluginEntry) {
116112
const entry = pluginEntry[name];
117-
const entryName = `${this.entryNamePrefix}${name}`;
113+
const entryName = this.createEntryName(name);
118114

119115
if (entry.import == null) {
120116
if (Option.isEntry(entry)) {
@@ -162,13 +158,21 @@ class AssetEntry {
162158
files.forEach((file) => {
163159
const outputFile = path.relative(dir, file);
164160
const name = outputFile.slice(0, outputFile.lastIndexOf('.'));
165-
const entryName = `${this.entryNamePrefix}${name}`;
161+
const entryName = this.createEntryName(name);
166162
entry[entryName] = { import: [file] };
167163
});
168164

169165
return entry;
170166
}
171167

168+
/**
169+
* @param {string} name
170+
* @return {string}
171+
*/
172+
static createEntryName(name) {
173+
return `${this.entryNamePrefix}${name}`;
174+
}
175+
172176
/**
173177
* @param {string} name
174178
* @return {string}
@@ -457,7 +461,6 @@ class AssetEntry {
457461
originalName,
458462
filenameTemplate,
459463
filename: undefined,
460-
assetFile: undefined,
461464
resource,
462465
importFile,
463466
sourceFile,

src/Plugin/Collection.js

Lines changed: 36 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class Collection {
3535
/** @type {Compilation} */
3636
static compilation;
3737

38+
/** @type {AssetEntry} */
39+
static assetEntry;
40+
3841
static assets = new Map();
3942

4043
/** @type {Map<string, {entry: AssetEntryOptions, assets: Array<{}>} >} Entries data */
@@ -330,10 +333,12 @@ class Collection {
330333

331334
/**
332335
* @param {Compilation} compilation
336+
* @param {AssetEntry} assetEntry
333337
* @param {HtmlBundlerPlugin.Hooks} hooks
334338
*/
335-
static init(compilation, hooks) {
339+
static init({ compilation, assetEntry, hooks }) {
336340
this.compilation = compilation;
341+
this.assetEntry = assetEntry;
337342
this.hooks = hooks;
338343
}
339344

@@ -1005,41 +1010,6 @@ class Collection {
10051010
});
10061011
}
10071012

1008-
// TODO: add hook for addHeadTags: beforeStyles, beforeScripts, endHead
1009-
// let res = this.hooks.insertHeadTags.call(content);
1010-
// if (res && Array.isArray(res.tags)) {
1011-
// let headTags = res.tags;
1012-
// let pos = res.pos;
1013-
// let sep = Option.isMinify() ? '' : '\n';
1014-
// let headBlock = headTags.join(sep);
1015-
//
1016-
// let startPos;
1017-
// let headEndPos = content.indexOf('</head>');
1018-
// let headEndPosRegExp = /<\/head>/;
1019-
// let posRegexp;
1020-
//
1021-
// if (!isNumber(pos)) {
1022-
// switch (pos) {
1023-
// case 'beforePreload':
1024-
// posRegexp = /<link.+rel="preload"/;
1025-
// break;
1026-
// case 'beforeStyles':
1027-
// posRegexp = /<link.+rel=".*stylesheet.*"/;
1028-
// break;
1029-
// case 'beforeScripts':
1030-
// posRegexp = /<script/;
1031-
// break;
1032-
// case 'endHead':
1033-
// // through
1034-
// case 'default':
1035-
// startPos;
1036-
// break;
1037-
// }
1038-
//
1039-
// const match = posRegexp.exec(content);
1040-
// }
1041-
// }
1042-
10431013
// 9. beforeEmit hook allows plugins to change the html after chunks and inlined assets are injected
10441014
promise = promise.then((content) => hooks.beforeEmit.promise(content, compileEntry) || content);
10451015

@@ -1118,17 +1088,44 @@ class Collection {
11181088
this.importStyleSources.clear();
11191089
}
11201090

1091+
/* istanbul ignore next: test it manual using `cache.type` as `filesystem` after 2nd run the same project */
1092+
/**
1093+
* Called by first start or after changes.
1094+
*
1095+
* @param {Function} write The serialize function.
1096+
*/
11211097
static serialize({ write }) {
1098+
for (let [, { entry }] of this.data) {
1099+
// note: set the function properties as null to able the serialization of the entry object,
1100+
// the original functions will be recovered by deserialization from the cached object `AssetEntry`
1101+
entry.filenameFn = null;
1102+
entry.filenameTemplate = null;
1103+
}
1104+
11221105
write(this.assets);
11231106
write(this.data);
11241107
}
11251108

1109+
/* istanbul ignore next: test it manual using `cache.type` as `filesystem` after 2nd run the same project */
1110+
/**
1111+
* @param {Function} read The deserialize function.
1112+
*/
11261113
static deserialize({ read }) {
11271114
this.assets = read();
11281115
this.data = read();
1116+
1117+
for (let [, { entry }] of this.data) {
1118+
const cachedEntry = this.assetEntry.entriesById.get(entry.id);
1119+
1120+
// recovery original not serializable functions from the object cached in the memory
1121+
entry.filenameFn = cachedEntry.filenameFn;
1122+
entry.filenameTemplate = cachedEntry.filenameTemplate;
1123+
}
1124+
11291125
this.isDeserialized = true;
11301126
}
11311127

1128+
/* istanbul ignore next: test it manual using `cache.type` as `filesystem` after 2nd run the same project */
11321129
/**
11331130
* Add the script files loaded in the template to the compilation after deserialization.
11341131
*
@@ -1140,45 +1137,13 @@ class Collection {
11401137
*/
11411138
static addToCompilationDeserializedFiles(issuer) {
11421139
for (const [resource, item] of this.assets) {
1143-
const { isCompiled, type, name, entries } = item;
1144-
if (!isCompiled && type === this.type.script && entries.has(issuer)) {
1145-
item.isCompiled = true;
1140+
const { type, name, entries } = item;
1141+
1142+
if (type === this.type.script && entries.has(issuer)) {
11461143
this.#addToCompilation({ name, resource, issuer });
11471144
}
11481145
}
11491146
}
1150-
1151-
/**
1152-
* TODO: Reserved for debug.
1153-
*/
1154-
// static getResourceIssuers(resource) {
1155-
// const item = this.assets.get(resource);
1156-
//
1157-
// if (!item) return [];
1158-
//
1159-
// const issuers = Array.from(item.entries.keys());
1160-
//
1161-
// // extract from all issuer's request only filename, w/o a query
1162-
// issuers.forEach((request, index) => {
1163-
// let [file] = request.split('?', 1);
1164-
// issuers[index] = file;
1165-
// });
1166-
//
1167-
// return issuers;
1168-
// }
1169-
1170-
/**
1171-
* TODO: Reserved for debug.
1172-
*/
1173-
// static getCompilationModule(resource) {
1174-
// const { modules } = this.compilation;
1175-
// for (const module of modules) {
1176-
// if (module.resource === resource) {
1177-
// return module;
1178-
// }
1179-
// }
1180-
// return null;
1181-
// }
11821147
}
11831148

11841149
module.exports = Collection;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Test</title>
5+
</head>
6+
<body>
7+
<h1>Hello World!</h1>
8+
<script src="main.6ad9992e.js"></script>
9+
</body>
10+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("Hello World! Test: 123");
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Test</title>
5+
</head>
6+
<body>
7+
<h1>Hello World!</h1>
8+
<script src="main.js"></script>
9+
</body>
10+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('Hello World! Test: 123');
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const path = require('path');
2+
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
3+
4+
module.exports = {
5+
mode: 'production',
6+
//mode: 'development',
7+
stats: 'errors-warnings',
8+
9+
output: {
10+
path: path.join(__dirname, 'dist/'),
11+
clean: true,
12+
},
13+
14+
plugins: [
15+
new HtmlBundlerPlugin({
16+
entry: {
17+
index: {
18+
import: './src/index.html',
19+
filename: (pathData) => {
20+
//console.log('\n\n*** entry.index.filename: : ', { pathData });
21+
return '[name].html';
22+
},
23+
},
24+
},
25+
26+
// filename: (pathData) => {
27+
// console.log('\n\n*** plugin.filename: : ', { pathData });
28+
// return '[name].html';
29+
// },
30+
31+
js: {
32+
filename: (pathData) => {
33+
//console.log('\n\n*** js.filename: : ', { pathData });
34+
return '[name].[contenthash:8].js';
35+
},
36+
},
37+
}),
38+
],
39+
40+
// test filesystem cache
41+
cache: {
42+
type: 'filesystem',
43+
cacheDirectory: path.join(__dirname, '.cache'),
44+
},
45+
};

0 commit comments

Comments
 (0)