diff options
author | Kristóf Marussy <kristof@marussy.com> | 2024-03-21 20:59:50 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-03-21 20:59:50 +0100 |
commit | cad97041b39570e78b38352e60b4dae6870be3c2 (patch) | |
tree | a122f5c1155d85c6931ad0ec7e75706d8cf17066 | |
parent | Also compress Docusaurus-generated files (diff) | |
download | blog-cad97041b39570e78b38352e60b4dae6870be3c2.tar.gz blog-cad97041b39570e78b38352e60b4dae6870be3c2.tar.zst blog-cad97041b39570e78b38352e60b4dae6870be3c2.zip |
Simplify compression
Compress Docusaurus-generated files in a separate script instead of a post-build
hook to avoid post-build hook ordering and concurrency issues.
-rw-r--r-- | docusaurus.config.ts | 1 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | scripts/compressSite.mjs | 45 | ||||
-rw-r--r-- | scripts/compressionOptions.mjs | 21 | ||||
-rw-r--r-- | src/plugins/compressionPlugin.ts | 49 |
5 files changed, 79 insertions, 39 deletions
diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 9468812..f19513a 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts | |||
@@ -23,6 +23,7 @@ export default { | |||
23 | remarkPlugins: [[smartypants, { dashes: 'oldschool' }]], | 23 | remarkPlugins: [[smartypants, { dashes: 'oldschool' }]], |
24 | }, | 24 | }, |
25 | ], | 25 | ], |
26 | '@docusaurus/plugin-sitemap', | ||
26 | './src/plugins/compressionPlugin.ts', | 27 | './src/plugins/compressionPlugin.ts', |
27 | './src/plugins/responsiveLoaderPlugin.ts', | 28 | './src/plugins/responsiveLoaderPlugin.ts', |
28 | './src/plugins/swcMinifyPlugin.ts', | 29 | './src/plugins/swcMinifyPlugin.ts', |
diff --git a/package.json b/package.json index 57e8f60..fc56b75 100644 --- a/package.json +++ b/package.json | |||
@@ -14,7 +14,7 @@ | |||
14 | "private": true, | 14 | "private": true, |
15 | "scripts": { | 15 | "scripts": { |
16 | "docusaurus": "docusaurus", | 16 | "docusaurus": "docusaurus", |
17 | "build": "cross-env WEBPACK_URL_LOADER_LIMIT=0 docusaurus build", | 17 | "build": "cross-env WEBPACK_URL_LOADER_LIMIT=0 docusaurus build && node scripts/compressSite.mjs", |
18 | "swizzle": "docusaurus swizzle", | 18 | "swizzle": "docusaurus swizzle", |
19 | "clear": "docusaurus clear", | 19 | "clear": "docusaurus clear", |
20 | "serve": "docusaurus serve -p 1313 --no-open", | 20 | "serve": "docusaurus serve -p 1313 --no-open", |
diff --git a/scripts/compressSite.mjs b/scripts/compressSite.mjs new file mode 100644 index 0000000..bd98c55 --- /dev/null +++ b/scripts/compressSite.mjs | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * SPDX-License-Identifier: MIT | ||
5 | */ | ||
6 | |||
7 | import { readFile, writeFile } from 'node:fs/promises'; | ||
8 | import path from 'node:path'; | ||
9 | import { promisify } from 'node:util'; | ||
10 | import zlib from 'node:zlib'; | ||
11 | |||
12 | import { Globby } from '@docusaurus/utils'; | ||
13 | |||
14 | import { brotliOptions, gzipOptions, minRatio } from './compressionOptions.mjs'; | ||
15 | |||
16 | const patterns = [ | ||
17 | '**/*.html', | ||
18 | 'atom.xml', | ||
19 | 'feed.json', | ||
20 | 'robots.txt', | ||
21 | 'rss.xml', | ||
22 | 'sitemap.xml', | ||
23 | ]; | ||
24 | const brotliCompress = promisify(zlib.brotliCompress); | ||
25 | const gzip = promisify(zlib.gzip); | ||
26 | |||
27 | async function compressSite() { | ||
28 | const files = await Globby(patterns.map((s) => path.join('build', s))); | ||
29 | const promises = files.map(async (file) => { | ||
30 | const contents = await readFile(file); | ||
31 | const maxLength = contents.length * minRatio; | ||
32 | const gzipContents = await gzip(contents, gzipOptions); | ||
33 | if (gzipContents.length <= maxLength) { | ||
34 | await writeFile(`${file}.gz`, gzipContents); | ||
35 | } | ||
36 | const brotliContents = await brotliCompress(contents, brotliOptions); | ||
37 | if (brotliContents.length <= maxLength) { | ||
38 | await writeFile(`${file}.br`, brotliContents); | ||
39 | } | ||
40 | }); | ||
41 | await Promise.all(promises); | ||
42 | } | ||
43 | |||
44 | // @ts-expect-error This file is run directly by nodejs, so top-level await is fine here. | ||
45 | await compressSite(); | ||
diff --git a/scripts/compressionOptions.mjs b/scripts/compressionOptions.mjs new file mode 100644 index 0000000..3f2d226 --- /dev/null +++ b/scripts/compressionOptions.mjs | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * SPDX-License-Identifier: MIT | ||
5 | */ | ||
6 | |||
7 | import zlib from 'node:zlib'; | ||
8 | |||
9 | export const minRatio = 0.8; | ||
10 | |||
11 | /** @type {import('node:zlib').ZlibOptions} */ | ||
12 | export const gzipOptions = { | ||
13 | level: 9, | ||
14 | }; | ||
15 | |||
16 | /** @type {import('node:zlib').BrotliOptions} */ | ||
17 | export const brotliOptions = { | ||
18 | params: { | ||
19 | [zlib.constants.BROTLI_PARAM_QUALITY]: 11, | ||
20 | }, | ||
21 | }; | ||
diff --git a/src/plugins/compressionPlugin.ts b/src/plugins/compressionPlugin.ts index 8b347cf..7b9feeb 100644 --- a/src/plugins/compressionPlugin.ts +++ b/src/plugins/compressionPlugin.ts | |||
@@ -4,35 +4,20 @@ | |||
4 | * SPDX-License-Identifier: MIT | 4 | * SPDX-License-Identifier: MIT |
5 | */ | 5 | */ |
6 | 6 | ||
7 | import { readFile, writeFile } from 'node:fs/promises'; | 7 | import type { BrotliOptions } from 'node:zlib'; |
8 | import path from 'node:path'; | ||
9 | import { promisify } from 'node:util'; | ||
10 | import zlib, { type BrotliOptions, type ZlibOptions } from 'node:zlib'; | ||
11 | 8 | ||
12 | import pluginSitemap, { type PluginOptions } from '@docusaurus/plugin-sitemap'; | 9 | import type { Plugin } from '@docusaurus/types'; |
13 | import type { LoadContext, Plugin } from '@docusaurus/types'; | ||
14 | import { Globby } from '@docusaurus/utils'; | ||
15 | import CompressionPlugin from 'compression-webpack-plugin'; | 10 | import CompressionPlugin from 'compression-webpack-plugin'; |
16 | 11 | ||
17 | const gzipOptions = { | 12 | import { |
18 | level: 9, | 13 | brotliOptions, |
19 | } satisfies ZlibOptions; | 14 | gzipOptions, |
20 | const brotliOptions = { | 15 | minRatio, |
21 | params: { | 16 | } from '../../scripts/compressionOptions.mjs'; |
22 | [zlib.constants.BROTLI_PARAM_QUALITY]: 11, | ||
23 | }, | ||
24 | }; | ||
25 | const test = /\.(js|css|svg|txt)$/; | ||
26 | const paths = ['**/*.html', 'robots.txt', 'sitemap.xml']; | ||
27 | 17 | ||
28 | const gzip = promisify(zlib.gzip); | 18 | const test = /\.(js|css|svg|txt)$/; |
29 | const brotliCompress = promisify(zlib.brotliCompress); | ||
30 | 19 | ||
31 | export default function compressionPlugin( | 20 | export default function compressionPlugin(): Plugin<void> { |
32 | context: LoadContext, | ||
33 | options: PluginOptions, | ||
34 | ): Plugin<void> { | ||
35 | const sitemap = pluginSitemap(context, options); | ||
36 | return { | 21 | return { |
37 | name: 'marussy-compression-plugin', | 22 | name: 'marussy-compression-plugin', |
38 | configureWebpack: (_config, isServer) => | 23 | configureWebpack: (_config, isServer) => |
@@ -44,28 +29,16 @@ export default function compressionPlugin( | |||
44 | test, | 29 | test, |
45 | filename: '[path][base].gz', | 30 | filename: '[path][base].gz', |
46 | compressionOptions: gzipOptions, | 31 | compressionOptions: gzipOptions, |
32 | minRatio, | ||
47 | }), | 33 | }), |
48 | new CompressionPlugin<BrotliOptions>({ | 34 | new CompressionPlugin<BrotliOptions>({ |
49 | test, | 35 | test, |
50 | filename: '[path][base].br', | 36 | filename: '[path][base].br', |
51 | algorithm: 'brotliCompress', | 37 | algorithm: 'brotliCompress', |
52 | compressionOptions: brotliOptions, | 38 | compressionOptions: brotliOptions, |
39 | minRatio, | ||
53 | }), | 40 | }), |
54 | ], | 41 | ], |
55 | }, | 42 | }, |
56 | async postBuild(props) { | ||
57 | await sitemap?.postBuild(props); | ||
58 | const files = await Globby(paths.map((s) => path.join(props.outDir, s))); | ||
59 | for (const file of files) { | ||
60 | console.log(file); | ||
61 | const contents = await readFile(file); | ||
62 | const gzipContents = await gzip(contents, gzipOptions); | ||
63 | await writeFile(`${file}.gz`, gzipContents); | ||
64 | const brotliContents = await brotliCompress(contents, brotliOptions); | ||
65 | await writeFile(`${file}.br`, brotliContents); | ||
66 | } | ||
67 | }, | ||
68 | }; | 43 | }; |
69 | } | 44 | } |
70 | |||
71 | export { validateOptions } from '@docusaurus/plugin-sitemap'; | ||