astro-html-minifier-next 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -40
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +47 -68
- package/dist/minify-html-file-worker-pool.d.ts +0 -26
- package/dist/minify-html-file-worker-pool.d.ts.map +0 -1
- package/dist/minify-html-file-worker-pool.js +0 -89
- package/dist/minify-html-file-worker-pool.js.map +0 -1
- package/dist/minify-html-file-worker.d.ts +0 -2
- package/dist/minify-html-file-worker.d.ts.map +0 -1
- package/dist/minify-html-file-worker.js +0 -16
- package/dist/minify-html-file-worker.js.map +0 -1
- package/dist/minify-html-file.d.ts +0 -7
- package/dist/minify-html-file.d.ts.map +0 -1
- package/dist/minify-html-file.js +0 -22
- package/dist/minify-html-file.js.map +0 -1
- package/src/minify-html-file-worker-pool.ts +0 -123
- package/src/minify-html-file-worker.ts +0 -29
- package/src/minify-html-file.ts +0 -38
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
import type { AstroIntegration } from "astro";
|
|
2
|
-
import type
|
|
3
|
-
export interface HTMLMinifierOptions extends MinifyHTMLOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Option specific to `astro-html-minifier-next` used to specify the maximum
|
|
6
|
-
* number of worker threads to spawn when minifying files.
|
|
7
|
-
* When set to `0`, `astro-html-minifier-next` will not create any worker
|
|
8
|
-
* threads and will do the minification in the main thread.
|
|
9
|
-
*
|
|
10
|
-
* Note: If unable to do a structured clone of the `html-minifier-next`
|
|
11
|
-
* options according to the
|
|
12
|
-
* [HTML structured clone
|
|
13
|
-
* algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm),
|
|
14
|
-
* `astro-html-minifier-next` will do the minification on the main
|
|
15
|
-
* thread, even if this option is not set to `0`.
|
|
16
|
-
*
|
|
17
|
-
* @default `Math.max(1, os.availableParallelism() - 1)`
|
|
18
|
-
*/
|
|
19
|
-
maxWorkers?: number;
|
|
20
|
-
}
|
|
2
|
+
import { type MinifierOptions as HTMLMinifierOptions } from "html-minifier-next";
|
|
21
3
|
/**
|
|
22
4
|
* An Astro integration that minifies HTML assets using
|
|
23
5
|
* [html-minifier-next](https://www.npmjs.com/package/html-minifier-next).
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EACN,KAAK,eAAe,IAAI,mBAAmB,EAE3C,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CACnC,OAAO,EAAE,mBAAmB,GAC1B,gBAAgB,CAyIlB"}
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,9 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
1
2
|
import { availableParallelism as getAvailableParallelism } from "node:os";
|
|
2
3
|
import { relative as getRelativePath } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { styleText } from "node:util";
|
|
5
|
-
import {
|
|
6
|
-
import { MinifyHTMLFileWorkerPool } from "./minify-html-file-worker-pool.js";
|
|
7
|
-
/**
|
|
8
|
-
* Check if a value can be transferred to a worker.
|
|
9
|
-
* @param {*} value
|
|
10
|
-
* @returns {boolean}
|
|
11
|
-
*/
|
|
12
|
-
function isTransferable(value) {
|
|
13
|
-
try {
|
|
14
|
-
// Attempt to do a structured clone of the value.
|
|
15
|
-
// If it succeeds, it should be transferable to a worker.
|
|
16
|
-
structuredClone(value);
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
6
|
+
import { minify as minifyHTML, } from "html-minifier-next";
|
|
23
7
|
/**
|
|
24
8
|
* An Astro integration that minifies HTML assets using
|
|
25
9
|
* [html-minifier-next](https://www.npmjs.com/package/html-minifier-next).
|
|
@@ -33,15 +17,9 @@ export default function htmlMinifier(options) {
|
|
|
33
17
|
return {
|
|
34
18
|
name: "astro-html-minifier-next",
|
|
35
19
|
hooks: {
|
|
36
|
-
"astro:build:done": async ({
|
|
20
|
+
"astro:build:done": async ({ assets, dir: distUrl, logger, }) => {
|
|
37
21
|
logger.info(styleText(["bgGreen", "black"], " minifying html assets "));
|
|
38
|
-
const totalTimeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
39
|
-
const availableParallelism = getAvailableParallelism();
|
|
40
|
-
const { maxWorkers = Math.max(1, availableParallelism - 1), ...minifyHTMLOptions } = options;
|
|
41
|
-
let workerPool;
|
|
42
|
-
if (maxWorkers > 0 && isTransferable(minifyHTMLOptions)) {
|
|
43
|
-
workerPool = new MinifyHTMLFileWorkerPool(maxWorkers, minifyHTMLOptions);
|
|
44
|
-
}
|
|
22
|
+
const totalTimeStart = performance.now(); // --- TOTAL TIMED BLOCK START ---
|
|
45
23
|
const tasks = [];
|
|
46
24
|
let tasksTotal = 0;
|
|
47
25
|
let tasksDone = 0;
|
|
@@ -58,10 +36,24 @@ export default function htmlMinifier(options) {
|
|
|
58
36
|
const relativeAssetPath = getRelativePath(distPath, assetPath);
|
|
59
37
|
const logLineAssetPath = ` ${logLineArrow} /${relativeAssetPath} `;
|
|
60
38
|
tasks.push(async () => {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
:
|
|
64
|
-
|
|
39
|
+
const timeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
40
|
+
const html = await readFile(assetPath, {
|
|
41
|
+
encoding: "utf8",
|
|
42
|
+
signal,
|
|
43
|
+
});
|
|
44
|
+
const minifiedHTML = await minifyHTML(html, options);
|
|
45
|
+
const savings = Buffer.byteLength(html) - Buffer.byteLength(minifiedHTML);
|
|
46
|
+
if (savings > 0) {
|
|
47
|
+
// Only write the minified HTML to the file if it's smaller.
|
|
48
|
+
await writeFile(assetPath, minifiedHTML, {
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
signal,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const timeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
54
|
+
const time = timeEnd - timeStart;
|
|
55
|
+
// Log a nice summary of the minification savings and the time it
|
|
56
|
+
// took.
|
|
65
57
|
const savingsSign = savings > 0 ? "-" : "+";
|
|
66
58
|
const savingsAbs = Math.abs(savings);
|
|
67
59
|
const savingsWithUnit = savingsAbs < 1024
|
|
@@ -79,12 +71,15 @@ export default function htmlMinifier(options) {
|
|
|
79
71
|
tasksTotal++;
|
|
80
72
|
}
|
|
81
73
|
}
|
|
82
|
-
// We use a quadruple of the available parallelism here, even if we
|
|
83
|
-
//
|
|
84
|
-
|
|
74
|
+
// We use a quadruple of the available parallelism here, even if we
|
|
75
|
+
// don't actually run the tasks in different threads or anything. The
|
|
76
|
+
// available parallelism is a good indicator of machine capabilities,
|
|
77
|
+
// and the multiplier gives a good balance of speed and resource usage.
|
|
78
|
+
const maxExecutingTasksSize = getAvailableParallelism() * 4;
|
|
85
79
|
// This holds the current batch of promises that are waiting to fulfill.
|
|
86
80
|
const executingTasks = new Set();
|
|
87
|
-
// Batch the tasks to avoid minifying too many files at once, which
|
|
81
|
+
// Batch the tasks to avoid minifying too many files at once, which
|
|
82
|
+
// could lead to memory and performance issues.
|
|
88
83
|
for (const task of tasks) {
|
|
89
84
|
const taskPromise = task()
|
|
90
85
|
.then(() => {
|
|
@@ -98,8 +93,9 @@ export default function htmlMinifier(options) {
|
|
|
98
93
|
});
|
|
99
94
|
executingTasks.add(taskPromise);
|
|
100
95
|
if (executingTasks.size >= maxExecutingTasksSize) {
|
|
101
|
-
// If the amount of executing tasks reaches the limit, we wait
|
|
102
|
-
//
|
|
96
|
+
// If the amount of executing tasks reaches the limit, we wait
|
|
97
|
+
// until the one of them finishes, and therefore gets deleted from
|
|
98
|
+
// the list, before continuing with the next task.
|
|
103
99
|
await Promise.race(executingTasks);
|
|
104
100
|
}
|
|
105
101
|
if (signal.aborted) {
|
|
@@ -108,13 +104,13 @@ export default function htmlMinifier(options) {
|
|
|
108
104
|
}
|
|
109
105
|
// Wait for any remaining tasks to finish.
|
|
110
106
|
await Promise.all(executingTasks);
|
|
111
|
-
const totalTimeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
112
|
-
// Log how long processing all assets took.
|
|
107
|
+
const totalTimeEnd = performance.now(); // --- TOTAL TIMED BLOCK END ---
|
|
113
108
|
const totalTime = totalTimeEnd - totalTimeStart;
|
|
114
|
-
|
|
109
|
+
// Log how long processing all assets took.
|
|
110
|
+
const totalTimeWithUnit = totalTime < 1000
|
|
115
111
|
? `${Math.round(totalTime)}ms`
|
|
116
112
|
: `${(totalTime / 1000).toFixed(2)}s`;
|
|
117
|
-
logger.info(styleText("green", `✓ Completed in ${
|
|
113
|
+
logger.info(styleText("green", `✓ Completed in ${totalTimeWithUnit}.`));
|
|
118
114
|
},
|
|
119
115
|
},
|
|
120
116
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,QAAQ,IAAI,eAAe,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-html-minifier-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Astro integration for html-minifier-next",
|
|
5
5
|
"homepage": "https://github.com/jonasgeiler/astro-html-minifier-next#readme",
|
|
6
6
|
"bugs": "https://github.com/jonasgeiler/astro-html-minifier-next/issues",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"url": "git+https://github.com/jonasgeiler/astro-html-minifier-next.git"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": "
|
|
15
|
+
"node": ">=22.13.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"astro": "^5.0.0"
|
package/src/index.ts
CHANGED
|
@@ -1,46 +1,13 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
1
2
|
import { availableParallelism as getAvailableParallelism } from "node:os";
|
|
2
3
|
import { relative as getRelativePath } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { styleText } from "node:util";
|
|
5
6
|
import type { AstroIntegration } from "astro";
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export interface HTMLMinifierOptions extends MinifyHTMLOptions {
|
|
11
|
-
/**
|
|
12
|
-
* Option specific to `astro-html-minifier-next` used to specify the maximum
|
|
13
|
-
* number of worker threads to spawn when minifying files.
|
|
14
|
-
* When set to `0`, `astro-html-minifier-next` will not create any worker
|
|
15
|
-
* threads and will do the minification in the main thread.
|
|
16
|
-
*
|
|
17
|
-
* Note: If unable to do a structured clone of the `html-minifier-next`
|
|
18
|
-
* options according to the
|
|
19
|
-
* [HTML structured clone
|
|
20
|
-
* algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm),
|
|
21
|
-
* `astro-html-minifier-next` will do the minification on the main
|
|
22
|
-
* thread, even if this option is not set to `0`.
|
|
23
|
-
*
|
|
24
|
-
* @default `Math.max(1, os.availableParallelism() - 1)`
|
|
25
|
-
*/
|
|
26
|
-
maxWorkers?: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Check if a value can be transferred to a worker.
|
|
31
|
-
* @param {*} value
|
|
32
|
-
* @returns {boolean}
|
|
33
|
-
*/
|
|
34
|
-
function isTransferable(value: unknown): boolean {
|
|
35
|
-
try {
|
|
36
|
-
// Attempt to do a structured clone of the value.
|
|
37
|
-
// If it succeeds, it should be transferable to a worker.
|
|
38
|
-
structuredClone(value);
|
|
39
|
-
return true;
|
|
40
|
-
} catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
7
|
+
import {
|
|
8
|
+
type MinifierOptions as HTMLMinifierOptions,
|
|
9
|
+
minify as minifyHTML,
|
|
10
|
+
} from "html-minifier-next";
|
|
44
11
|
|
|
45
12
|
/**
|
|
46
13
|
* An Astro integration that minifies HTML assets using
|
|
@@ -57,24 +24,14 @@ export default function htmlMinifier(
|
|
|
57
24
|
return {
|
|
58
25
|
name: "astro-html-minifier-next",
|
|
59
26
|
hooks: {
|
|
60
|
-
"astro:build:done": async ({
|
|
27
|
+
"astro:build:done": async ({
|
|
28
|
+
assets,
|
|
29
|
+
dir: distUrl,
|
|
30
|
+
logger,
|
|
31
|
+
}): Promise<void> => {
|
|
61
32
|
logger.info(styleText(["bgGreen", "black"], " minifying html assets "));
|
|
62
33
|
|
|
63
|
-
const totalTimeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
64
|
-
|
|
65
|
-
const availableParallelism = getAvailableParallelism();
|
|
66
|
-
const {
|
|
67
|
-
maxWorkers = Math.max(1, availableParallelism - 1),
|
|
68
|
-
...minifyHTMLOptions
|
|
69
|
-
} = options;
|
|
70
|
-
|
|
71
|
-
let workerPool: MinifyHTMLFileWorkerPool | undefined;
|
|
72
|
-
if (maxWorkers > 0 && isTransferable(minifyHTMLOptions)) {
|
|
73
|
-
workerPool = new MinifyHTMLFileWorkerPool(
|
|
74
|
-
maxWorkers,
|
|
75
|
-
minifyHTMLOptions,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
34
|
+
const totalTimeStart = performance.now(); // --- TOTAL TIMED BLOCK START ---
|
|
78
35
|
|
|
79
36
|
const tasks: (() => Promise<void>)[] = [];
|
|
80
37
|
let tasksTotal = 0;
|
|
@@ -95,11 +52,29 @@ export default function htmlMinifier(
|
|
|
95
52
|
const relativeAssetPath = getRelativePath(distPath, assetPath);
|
|
96
53
|
const logLineAssetPath = ` ${logLineArrow} /${relativeAssetPath} `;
|
|
97
54
|
tasks.push(async () => {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
55
|
+
const timeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
56
|
+
|
|
57
|
+
const html = await readFile(assetPath, {
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
signal,
|
|
60
|
+
});
|
|
61
|
+
const minifiedHTML = await minifyHTML(html, options);
|
|
62
|
+
|
|
63
|
+
const savings =
|
|
64
|
+
Buffer.byteLength(html) - Buffer.byteLength(minifiedHTML);
|
|
65
|
+
if (savings > 0) {
|
|
66
|
+
// Only write the minified HTML to the file if it's smaller.
|
|
67
|
+
await writeFile(assetPath, minifiedHTML, {
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
signal,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
101
72
|
|
|
102
|
-
|
|
73
|
+
const timeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
74
|
+
const time = timeEnd - timeStart;
|
|
75
|
+
|
|
76
|
+
// Log a nice summary of the minification savings and the time it
|
|
77
|
+
// took.
|
|
103
78
|
const savingsSign = savings > 0 ? "-" : "+";
|
|
104
79
|
const savingsAbs = Math.abs(savings);
|
|
105
80
|
const savingsWithUnit =
|
|
@@ -129,14 +104,17 @@ export default function htmlMinifier(
|
|
|
129
104
|
}
|
|
130
105
|
}
|
|
131
106
|
|
|
132
|
-
// We use a quadruple of the available parallelism here, even if we
|
|
133
|
-
//
|
|
134
|
-
|
|
107
|
+
// We use a quadruple of the available parallelism here, even if we
|
|
108
|
+
// don't actually run the tasks in different threads or anything. The
|
|
109
|
+
// available parallelism is a good indicator of machine capabilities,
|
|
110
|
+
// and the multiplier gives a good balance of speed and resource usage.
|
|
111
|
+
const maxExecutingTasksSize = getAvailableParallelism() * 4;
|
|
135
112
|
|
|
136
113
|
// This holds the current batch of promises that are waiting to fulfill.
|
|
137
114
|
const executingTasks = new Set<Promise<void>>();
|
|
138
115
|
|
|
139
|
-
// Batch the tasks to avoid minifying too many files at once, which
|
|
116
|
+
// Batch the tasks to avoid minifying too many files at once, which
|
|
117
|
+
// could lead to memory and performance issues.
|
|
140
118
|
for (const task of tasks) {
|
|
141
119
|
const taskPromise = task()
|
|
142
120
|
.then(() => {
|
|
@@ -152,8 +130,9 @@ export default function htmlMinifier(
|
|
|
152
130
|
executingTasks.add(taskPromise);
|
|
153
131
|
|
|
154
132
|
if (executingTasks.size >= maxExecutingTasksSize) {
|
|
155
|
-
// If the amount of executing tasks reaches the limit, we wait
|
|
156
|
-
//
|
|
133
|
+
// If the amount of executing tasks reaches the limit, we wait
|
|
134
|
+
// until the one of them finishes, and therefore gets deleted from
|
|
135
|
+
// the list, before continuing with the next task.
|
|
157
136
|
await Promise.race(executingTasks);
|
|
158
137
|
}
|
|
159
138
|
|
|
@@ -165,15 +144,15 @@ export default function htmlMinifier(
|
|
|
165
144
|
// Wait for any remaining tasks to finish.
|
|
166
145
|
await Promise.all(executingTasks);
|
|
167
146
|
|
|
168
|
-
const totalTimeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
147
|
+
const totalTimeEnd = performance.now(); // --- TOTAL TIMED BLOCK END ---
|
|
148
|
+
const totalTime = totalTimeEnd - totalTimeStart;
|
|
169
149
|
|
|
170
150
|
// Log how long processing all assets took.
|
|
171
|
-
const
|
|
172
|
-
const totalTimeStr =
|
|
151
|
+
const totalTimeWithUnit =
|
|
173
152
|
totalTime < 1000
|
|
174
153
|
? `${Math.round(totalTime)}ms`
|
|
175
154
|
: `${(totalTime / 1000).toFixed(2)}s`;
|
|
176
|
-
logger.info(styleText("green", `✓ Completed in ${
|
|
155
|
+
logger.info(styleText("green", `✓ Completed in ${totalTimeWithUnit}.`));
|
|
177
156
|
},
|
|
178
157
|
},
|
|
179
158
|
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Worker } from "node:worker_threads";
|
|
2
|
-
import type { MinifierOptions as MinifyHTMLOptions } from "html-minifier-next";
|
|
3
|
-
import type { MinifyHTMLFileResult } from "./minify-html-file.js";
|
|
4
|
-
interface MinifyHTMLFileWorker extends Worker {
|
|
5
|
-
_currentResolve?: (result: MinifyHTMLFileResult) => void;
|
|
6
|
-
_currentReject?: (error: unknown) => void;
|
|
7
|
-
}
|
|
8
|
-
export type MinifyHTMLWorkerInput = string;
|
|
9
|
-
export type MinifyHTMLWorkerOutput = MinifyHTMLFileResult | {
|
|
10
|
-
error: unknown;
|
|
11
|
-
};
|
|
12
|
-
export declare class MinifyHTMLFileWorkerPool {
|
|
13
|
-
protected maxWorkers: number;
|
|
14
|
-
protected minifyHTMLOptions: MinifyHTMLOptions;
|
|
15
|
-
protected workerUrl: URL;
|
|
16
|
-
protected pool: Set<Worker>;
|
|
17
|
-
protected idle: Worker[];
|
|
18
|
-
protected queue: ((value: Worker) => void)[];
|
|
19
|
-
constructor(maxWorkers: number, minifyHTMLOptions: MinifyHTMLOptions);
|
|
20
|
-
protected getAvailableWorker(): Promise<MinifyHTMLFileWorker>;
|
|
21
|
-
protected releaseWorker(worker: Worker): void;
|
|
22
|
-
protected removeWorker(worker: Worker): void;
|
|
23
|
-
minifyHTMLFile(htmlFile: string): Promise<MinifyHTMLFileResult>;
|
|
24
|
-
}
|
|
25
|
-
export {};
|
|
26
|
-
//# sourceMappingURL=minify-html-file-worker-pool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file-worker-pool.d.ts","sourceRoot":"","sources":["../src/minify-html-file-worker-pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,UAAU,oBAAqB,SAAQ,MAAM;IAC5C,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACzD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAC3C,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/E,qBAAa,wBAAwB;IACpC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAE/C,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC;IACzB,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;gBAEjC,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,iBAAiB;cAUpD,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAqDnE,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAc7C,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU/B,cAAc,CAC1B,QAAQ,EAAE,MAAM,GAEd,OAAO,CAAC,oBAAoB,CAAC;CAWhC"}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { Worker } from "node:worker_threads";
|
|
2
|
-
export class MinifyHTMLFileWorkerPool {
|
|
3
|
-
maxWorkers;
|
|
4
|
-
minifyHTMLOptions;
|
|
5
|
-
workerUrl;
|
|
6
|
-
pool;
|
|
7
|
-
idle;
|
|
8
|
-
queue;
|
|
9
|
-
constructor(maxWorkers, minifyHTMLOptions) {
|
|
10
|
-
this.maxWorkers = maxWorkers;
|
|
11
|
-
this.minifyHTMLOptions = minifyHTMLOptions;
|
|
12
|
-
this.workerUrl = new URL("./minify-html-file-worker.js", import.meta.url);
|
|
13
|
-
this.pool = new Set();
|
|
14
|
-
this.idle = [];
|
|
15
|
-
this.queue = [];
|
|
16
|
-
}
|
|
17
|
-
async getAvailableWorker() {
|
|
18
|
-
// If there is an idle worker, use it.
|
|
19
|
-
if (this.idle.length) {
|
|
20
|
-
const worker = this.idle.shift();
|
|
21
|
-
if (worker !== undefined) {
|
|
22
|
-
return worker;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
// If we can create a new worker, do so.
|
|
26
|
-
if (this.pool.size < this.maxWorkers) {
|
|
27
|
-
const worker = new Worker(this.workerUrl, {
|
|
28
|
-
workerData: this.minifyHTMLOptions,
|
|
29
|
-
});
|
|
30
|
-
worker.on("message", async (message) => {
|
|
31
|
-
if ("error" in message) {
|
|
32
|
-
worker._currentReject?.(message.error);
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
worker._currentResolve?.(message);
|
|
36
|
-
}
|
|
37
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
38
|
-
this.releaseWorker(worker);
|
|
39
|
-
});
|
|
40
|
-
worker.on("error", (error) => {
|
|
41
|
-
worker._currentReject?.(error);
|
|
42
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
43
|
-
});
|
|
44
|
-
worker.on("exit", (exitCode) => {
|
|
45
|
-
this.removeWorker(worker);
|
|
46
|
-
if (exitCode !== 0) {
|
|
47
|
-
worker._currentReject?.(new Error(`Worker failed with exit code ${exitCode}.`));
|
|
48
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
this.pool.add(worker);
|
|
52
|
-
return worker;
|
|
53
|
-
}
|
|
54
|
-
// Otherwise, wait for a worker to free up.
|
|
55
|
-
return new Promise((resolve) => {
|
|
56
|
-
// When a worker frees up, they will check the queue and resolve this promise.
|
|
57
|
-
this.queue.push(resolve);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
releaseWorker(worker) {
|
|
61
|
-
// If there is a queued request for a worker, resolve it.
|
|
62
|
-
if (this.queue.length) {
|
|
63
|
-
const resolve = this.queue.shift();
|
|
64
|
-
if (resolve !== undefined) {
|
|
65
|
-
resolve(worker);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Otherwise, keep the worker as idle.
|
|
70
|
-
this.idle.push(worker);
|
|
71
|
-
}
|
|
72
|
-
removeWorker(worker) {
|
|
73
|
-
this.pool.delete(worker);
|
|
74
|
-
// If a worker is force stopped by the system, it might still be in the idle list.
|
|
75
|
-
const idleIndex = this.idle.indexOf(worker);
|
|
76
|
-
if (idleIndex !== -1) {
|
|
77
|
-
this.idle.splice(idleIndex, 1);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
async minifyHTMLFile(htmlFile) {
|
|
81
|
-
const worker = await this.getAvailableWorker();
|
|
82
|
-
return new Promise((resolve, reject) => {
|
|
83
|
-
worker._currentResolve = resolve;
|
|
84
|
-
worker._currentReject = reject;
|
|
85
|
-
worker.postMessage(htmlFile);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=minify-html-file-worker-pool.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file-worker-pool.js","sourceRoot":"","sources":["../src/minify-html-file-worker-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file-worker.d.ts","sourceRoot":"","sources":["../src/minify-html-file-worker.ts"],"names":[],"mappings":""}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { isMainThread, workerData as minifyHTMLOptions, parentPort, } from "node:worker_threads";
|
|
2
|
-
import { minifyHTMLFile } from "./minify-html-file.js";
|
|
3
|
-
if (isMainThread) {
|
|
4
|
-
throw new Error("Not a worker thread.");
|
|
5
|
-
}
|
|
6
|
-
// biome-ignore-start lint/style/noNonNullAssertion: I can assume `parentPort` is not null.
|
|
7
|
-
parentPort.on("message", async (htmlFile) => {
|
|
8
|
-
try {
|
|
9
|
-
parentPort.postMessage((await minifyHTMLFile(htmlFile, minifyHTMLOptions)));
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
parentPort.postMessage({ error });
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
// biome-ignore-end lint/style/noNonNullAssertion: See start.
|
|
16
|
-
//# sourceMappingURL=minify-html-file-worker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file-worker.js","sourceRoot":"","sources":["../src/minify-html-file-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,YAAY,EACZ,UAAU,IAAI,iBAAiB,EAC/B,UAAU,GACV,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMvD,IAAI,YAAY,EAAE,CAAC;IAClB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;AACzC,CAAC;AAED,2FAA2F;AAC3F,UAAW,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAA+B,EAAE,EAAE;IACnE,IAAI,CAAC;QACJ,UAAW,CAAC,WAAW,CACtB,CAAC,MAAM,cAAc,CACpB,QAAQ,EACR,iBAAiB,CACjB,CAAkC,CACnC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,UAAW,CAAC,WAAW,CAAC,EAAE,KAAK,EAAmC,CAAC,CAAC;IACrE,CAAC;AACF,CAAC,CAAC,CAAC;AACH,6DAA6D"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type MinifierOptions as MinifyHTMLOptions } from "html-minifier-next";
|
|
2
|
-
export interface MinifyHTMLFileResult {
|
|
3
|
-
savings: number;
|
|
4
|
-
time: number;
|
|
5
|
-
}
|
|
6
|
-
export declare function minifyHTMLFile(htmlFile: string, minifyHTMLOptions: MinifyHTMLOptions, signal?: AbortSignal): Promise<MinifyHTMLFileResult>;
|
|
7
|
-
//# sourceMappingURL=minify-html-file.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file.d.ts","sourceRoot":"","sources":["../src/minify-html-file.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,eAAe,IAAI,iBAAiB,EAEzC,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAsB/B"}
|
package/dist/minify-html-file.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { minify as minifyHTML, } from "html-minifier-next";
|
|
3
|
-
export async function minifyHTMLFile(htmlFile, minifyHTMLOptions, signal) {
|
|
4
|
-
const timeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
5
|
-
const html = await readFile(htmlFile, {
|
|
6
|
-
encoding: "utf8",
|
|
7
|
-
signal,
|
|
8
|
-
});
|
|
9
|
-
const minifiedHTML = await minifyHTML(html, minifyHTMLOptions);
|
|
10
|
-
const savings = Buffer.byteLength(html) - Buffer.byteLength(minifiedHTML);
|
|
11
|
-
if (savings > 0) {
|
|
12
|
-
// Only write the minified HTML to the file if it's smaller.
|
|
13
|
-
await writeFile(htmlFile, minifiedHTML, {
|
|
14
|
-
encoding: "utf8",
|
|
15
|
-
signal,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
const timeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
19
|
-
const time = timeEnd - timeStart;
|
|
20
|
-
return { savings, time };
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=minify-html-file.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minify-html-file.js","sourceRoot":"","sources":["../src/minify-html-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAEN,MAAM,IAAI,UAAU,GACpB,MAAM,oBAAoB,CAAC;AAO5B,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,QAAgB,EAChB,iBAAoC,EACpC,MAAoB;IAEpB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,4BAA4B;IAEjE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE;QACrC,QAAQ,EAAE,MAAM;QAChB,MAAM;KACN,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAE/D,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAC1E,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QACjB,4DAA4D;QAC5D,MAAM,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE;YACvC,QAAQ,EAAE,MAAM;YAChB,MAAM;SACN,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,0BAA0B;IAE7D,MAAM,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { Worker } from "node:worker_threads";
|
|
2
|
-
import type { MinifierOptions as MinifyHTMLOptions } from "html-minifier-next";
|
|
3
|
-
import type { MinifyHTMLFileResult } from "./minify-html-file.js";
|
|
4
|
-
|
|
5
|
-
interface MinifyHTMLFileWorker extends Worker {
|
|
6
|
-
_currentResolve?: (result: MinifyHTMLFileResult) => void;
|
|
7
|
-
_currentReject?: (error: unknown) => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type MinifyHTMLWorkerInput = string;
|
|
11
|
-
export type MinifyHTMLWorkerOutput = MinifyHTMLFileResult | { error: unknown };
|
|
12
|
-
|
|
13
|
-
export class MinifyHTMLFileWorkerPool {
|
|
14
|
-
protected maxWorkers: number;
|
|
15
|
-
protected minifyHTMLOptions: MinifyHTMLOptions;
|
|
16
|
-
|
|
17
|
-
protected workerUrl: URL;
|
|
18
|
-
protected pool: Set<Worker>;
|
|
19
|
-
protected idle: Worker[];
|
|
20
|
-
protected queue: ((value: Worker) => void)[];
|
|
21
|
-
|
|
22
|
-
constructor(maxWorkers: number, minifyHTMLOptions: MinifyHTMLOptions) {
|
|
23
|
-
this.maxWorkers = maxWorkers;
|
|
24
|
-
this.minifyHTMLOptions = minifyHTMLOptions;
|
|
25
|
-
|
|
26
|
-
this.workerUrl = new URL("./minify-html-file-worker.js", import.meta.url);
|
|
27
|
-
this.pool = new Set();
|
|
28
|
-
this.idle = [];
|
|
29
|
-
this.queue = [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
protected async getAvailableWorker(): Promise<MinifyHTMLFileWorker> {
|
|
33
|
-
// If there is an idle worker, use it.
|
|
34
|
-
if (this.idle.length) {
|
|
35
|
-
const worker = this.idle.shift();
|
|
36
|
-
if (worker !== undefined) {
|
|
37
|
-
return worker;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// If we can create a new worker, do so.
|
|
42
|
-
if (this.pool.size < this.maxWorkers) {
|
|
43
|
-
const worker = new Worker(this.workerUrl, {
|
|
44
|
-
workerData: this.minifyHTMLOptions,
|
|
45
|
-
}) as MinifyHTMLFileWorker;
|
|
46
|
-
|
|
47
|
-
worker.on("message", async (message: MinifyHTMLWorkerOutput) => {
|
|
48
|
-
if ("error" in message) {
|
|
49
|
-
worker._currentReject?.(message.error);
|
|
50
|
-
} else {
|
|
51
|
-
worker._currentResolve?.(message);
|
|
52
|
-
}
|
|
53
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
54
|
-
|
|
55
|
-
this.releaseWorker(worker);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
worker.on("error", (error) => {
|
|
59
|
-
worker._currentReject?.(error);
|
|
60
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
worker.on("exit", (exitCode) => {
|
|
64
|
-
this.removeWorker(worker);
|
|
65
|
-
|
|
66
|
-
if (exitCode !== 0) {
|
|
67
|
-
worker._currentReject?.(
|
|
68
|
-
new Error(`Worker failed with exit code ${exitCode}.`),
|
|
69
|
-
);
|
|
70
|
-
worker._currentResolve = worker._currentReject = undefined;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
this.pool.add(worker);
|
|
75
|
-
return worker;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Otherwise, wait for a worker to free up.
|
|
79
|
-
return new Promise<Worker>((resolve) => {
|
|
80
|
-
// When a worker frees up, they will check the queue and resolve this promise.
|
|
81
|
-
this.queue.push(resolve);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
protected releaseWorker(worker: Worker): void {
|
|
86
|
-
// If there is a queued request for a worker, resolve it.
|
|
87
|
-
if (this.queue.length) {
|
|
88
|
-
const resolve = this.queue.shift();
|
|
89
|
-
if (resolve !== undefined) {
|
|
90
|
-
resolve(worker);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Otherwise, keep the worker as idle.
|
|
96
|
-
this.idle.push(worker);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
protected removeWorker(worker: Worker): void {
|
|
100
|
-
this.pool.delete(worker);
|
|
101
|
-
|
|
102
|
-
// If a worker is force stopped by the system, it might still be in the idle list.
|
|
103
|
-
const idleIndex = this.idle.indexOf(worker);
|
|
104
|
-
if (idleIndex !== -1) {
|
|
105
|
-
this.idle.splice(idleIndex, 1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public async minifyHTMLFile(
|
|
110
|
-
htmlFile: string,
|
|
111
|
-
// TODO: Signal?
|
|
112
|
-
): Promise<MinifyHTMLFileResult> {
|
|
113
|
-
const worker = await this.getAvailableWorker();
|
|
114
|
-
|
|
115
|
-
return new Promise<MinifyHTMLFileResult>((resolve, reject) => {
|
|
116
|
-
worker._currentResolve = resolve;
|
|
117
|
-
worker._currentReject = reject;
|
|
118
|
-
worker.postMessage(htmlFile satisfies MinifyHTMLWorkerInput);
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// TODO: Destroy function
|
|
123
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isMainThread,
|
|
3
|
-
workerData as minifyHTMLOptions,
|
|
4
|
-
parentPort,
|
|
5
|
-
} from "node:worker_threads";
|
|
6
|
-
import { minifyHTMLFile } from "./minify-html-file.js";
|
|
7
|
-
import type {
|
|
8
|
-
MinifyHTMLWorkerInput,
|
|
9
|
-
MinifyHTMLWorkerOutput,
|
|
10
|
-
} from "./minify-html-file-worker-pool.js";
|
|
11
|
-
|
|
12
|
-
if (isMainThread) {
|
|
13
|
-
throw new Error("Not a worker thread.");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// biome-ignore-start lint/style/noNonNullAssertion: I can assume `parentPort` is not null.
|
|
17
|
-
parentPort!.on("message", async (htmlFile: MinifyHTMLWorkerInput) => {
|
|
18
|
-
try {
|
|
19
|
-
parentPort!.postMessage(
|
|
20
|
-
(await minifyHTMLFile(
|
|
21
|
-
htmlFile,
|
|
22
|
-
minifyHTMLOptions,
|
|
23
|
-
)) satisfies MinifyHTMLWorkerOutput,
|
|
24
|
-
);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
parentPort!.postMessage({ error } satisfies MinifyHTMLWorkerOutput);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
// biome-ignore-end lint/style/noNonNullAssertion: See start.
|
package/src/minify-html-file.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
type MinifierOptions as MinifyHTMLOptions,
|
|
4
|
-
minify as minifyHTML,
|
|
5
|
-
} from "html-minifier-next";
|
|
6
|
-
|
|
7
|
-
export interface MinifyHTMLFileResult {
|
|
8
|
-
savings: number;
|
|
9
|
-
time: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function minifyHTMLFile(
|
|
13
|
-
htmlFile: string,
|
|
14
|
-
minifyHTMLOptions: MinifyHTMLOptions,
|
|
15
|
-
signal?: AbortSignal,
|
|
16
|
-
): Promise<MinifyHTMLFileResult> {
|
|
17
|
-
const timeStart = performance.now(); // --- TIMED BLOCK START ---
|
|
18
|
-
|
|
19
|
-
const html = await readFile(htmlFile, {
|
|
20
|
-
encoding: "utf8",
|
|
21
|
-
signal,
|
|
22
|
-
});
|
|
23
|
-
const minifiedHTML = await minifyHTML(html, minifyHTMLOptions);
|
|
24
|
-
|
|
25
|
-
const savings = Buffer.byteLength(html) - Buffer.byteLength(minifiedHTML);
|
|
26
|
-
if (savings > 0) {
|
|
27
|
-
// Only write the minified HTML to the file if it's smaller.
|
|
28
|
-
await writeFile(htmlFile, minifiedHTML, {
|
|
29
|
-
encoding: "utf8",
|
|
30
|
-
signal,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const timeEnd = performance.now(); // --- TIMED BLOCK END ---
|
|
35
|
-
|
|
36
|
-
const time = timeEnd - timeStart;
|
|
37
|
-
return { savings, time };
|
|
38
|
-
}
|