Author: Björn Theart [bjorntheart@users.noreply.github.com]
Committer: GitHub [noreply@github.com] Mon, 26 Feb 2024 22:55:50 +0000
Hash: b4f75ba9d6c8b22cb91fa073752ab8058e359ca2
Timestamp: Mon, 26 Feb 2024 22:55:50 +0000 (6 months ago)

+170 -2 +/-3 browse
Add script to generate bundle sizes (#474)
Add script to generate bundle sizes (#474)

Add script to generate bundle sizes
1diff --git a/build/bundle-sizes.js b/build/bundle-sizes.js
2new file mode 100644
3index 0000000..0e621d8
4--- /dev/null
5+++ b/build/bundle-sizes.js
6 @@ -0,0 +1,92 @@
7+ import path from 'node:path';
8+ import postcss from 'postcss';
9+ import fs from 'node:fs/promises';
10+ import pkg from '../postcss.config.cjs';
11+ import { createRequire } from 'node:module';
12+ import { sync as brotli } from 'brotli-size';
13+ import { gzipSizeSync as gzip } from 'gzip-size';
14+
15+ // Comes in handy later when we run postcss
16+ const { plugins } = pkg;
17+
18+ // @ts-ignore
19+ const require = createRequire(import.meta.url);
20+ const { scripts } = require('../package.json');
21+ /**
22+ * We build up an object with script:command pairs from package.json
23+ *
24+ * @type {Object.<string, string>}
25+ */
26+ const filtered = Object.keys(scripts)
27+ .filter((key) => key.startsWith('lib:'))
28+ .reduce((obj, key) => {
29+ obj[key] = scripts[key];
30+ return obj;
31+ }, {});
32+
33+ /**
34+ * The regex captures a filepath and filename group from an npm command.
35+ *
36+ * Captures for the command `postcss src/extra/normalize.light.css -o normalize.light.min.css` yields
37+ * {
38+ * groups: {
39+ * filepath: 'src/extra/normalize.light.css',
40+ * filename: 'normalize.light.min.css'
41+ * }
42+ * }
43+ */
44+ const regex = /postcss\s(?<filepath>\S+)\s\-[o]\s(?<filename>.*\.css)(?:.*$)/;
45+
46+ /**
47+ * @typedef {Object} Size
48+ * @propety {number} raw - Unminified size in bytes
49+ * @property {string} size - Unminified size in KiB
50+ * @property {string} minified - Minified size in KiB
51+ * @property {string} brotli - Brotli compressed minified size in KiB
52+ * @property {string} gzip - Gzip compressed minified size in KiB
53+ */
54+ /** @type {Object.<string, Size>} sizes */
55+ let sizes = {}
56+
57+ for (const [_, script] of Object.entries(filtered)) {
58+ const found = script.match(regex);
59+
60+ if (!found) continue;
61+
62+ /**
63+ * @typedef {object} CaptureGroup
64+ * @property {string} filepath
65+ * @property {string} filename
66+ */
67+ /** @type {CaptureGroup} */
68+ const { filepath, filename } = found.groups;
69+
70+ /**
71+ * @type {import('postcss').ProcessOptions}
72+ */
73+ const options = { from: path.resolve(`../${filepath}`), to: undefined };
74+ const css = await fs.readFile(path.resolve(`../${filepath}`), 'utf-8');
75+ /**
76+ * Run the css through PostCSS (just like Open-Props).
77+ * plugins.slice(0, -1) remove `cssnano` plugin so we can get the size of the unminified code
78+ */
79+ const code = await postcss(plugins.slice(0, -1)).process(css, options);
80+ /**
81+ * This time we want to get the minified size.
82+ */
83+ const minified = await postcss(plugins).process(css, options);
84+
85+ /**
86+ * Build the sizes object.
87+ * Strip `.min` from the filename
88+ */
89+ sizes[filename.replace('.min', '')] = {
90+ raw: code.css.length, // in bytes
91+ size: (code.css.length / 1024).toFixed(2), // in KiB
92+ minified: (minified.css.length / 1024).toFixed(2), // KiB
93+ brotli: (brotli(minified.css) / 1024).toFixed(2), // in KiB
94+ gzip: (gzip(minified.css) / 1024).toFixed(2), // in KiB
95+ }
96+ }
97+
98+ await fs.writeFile('bundle-sizes.json', JSON.stringify(sizes, null, 2), { encoding: 'utf8' });
99 diff --git a/package-lock.json b/package-lock.json
100index 1efa6ab..02ffb78 100644
101--- a/package-lock.json
102+++ b/package-lock.json
103 @@ -1,18 +1,20 @@
104 {
105 "name": "open-props",
106- "version": "1.6.8",
107+ "version": "1.6.19",
108 "lockfileVersion": 2,
109 "requires": true,
110 "packages": {
111 "": {
112 "name": "open-props",
113- "version": "1.6.8",
114+ "version": "1.6.19",
115 "license": "MIT",
116 "devDependencies": {
117 "ava": "^3.15.0",
118+ "brotli-size": "^4.0.0",
119 "colorjs.io": "^0.4.1-patch.1",
120 "concurrently": "^7.2.2",
121 "cssnano": "^5.1.10",
122+ "gzip-size": "^7.0.0",
123 "json": "^11.0.0",
124 "open-color": "^1.9.1",
125 "postcss": "^8.3.9",
126 @@ -695,6 +697,18 @@
127 "node": ">=8"
128 }
129 },
130+ "node_modules/brotli-size": {
131+ "version": "4.0.0",
132+ "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz",
133+ "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==",
134+ "dev": true,
135+ "dependencies": {
136+ "duplexer": "0.1.1"
137+ },
138+ "engines": {
139+ "node": ">= 10.16.0"
140+ }
141+ },
142 "node_modules/browserslist": {
143 "version": "4.20.3",
144 "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
145 @@ -1766,6 +1780,12 @@
146 "node": ">=8"
147 }
148 },
149+ "node_modules/duplexer": {
150+ "version": "0.1.1",
151+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
152+ "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==",
153+ "dev": true
154+ },
155 "node_modules/duplexer3": {
156 "version": "0.1.4",
157 "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
158 @@ -2113,6 +2133,27 @@
159 "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
160 "dev": true
161 },
162+ "node_modules/gzip-size": {
163+ "version": "7.0.0",
164+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
165+ "integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==",
166+ "dev": true,
167+ "dependencies": {
168+ "duplexer": "^0.1.2"
169+ },
170+ "engines": {
171+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
172+ },
173+ "funding": {
174+ "url": "https://github.com/sponsors/sindresorhus"
175+ }
176+ },
177+ "node_modules/gzip-size/node_modules/duplexer": {
178+ "version": "0.1.2",
179+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
180+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
181+ "dev": true
182+ },
183 "node_modules/has": {
184 "version": "1.0.3",
185 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
186 @@ -6810,6 +6851,15 @@
187 "fill-range": "^7.0.1"
188 }
189 },
190+ "brotli-size": {
191+ "version": "4.0.0",
192+ "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz",
193+ "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==",
194+ "dev": true,
195+ "requires": {
196+ "duplexer": "0.1.1"
197+ }
198+ },
199 "browserslist": {
200 "version": "4.20.3",
201 "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
202 @@ -7581,6 +7631,12 @@
203 "is-obj": "^2.0.0"
204 }
205 },
206+ "duplexer": {
207+ "version": "0.1.1",
208+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
209+ "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==",
210+ "dev": true
211+ },
212 "duplexer3": {
213 "version": "0.1.4",
214 "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
215 @@ -7841,6 +7897,23 @@
216 "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
217 "dev": true
218 },
219+ "gzip-size": {
220+ "version": "7.0.0",
221+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
222+ "integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==",
223+ "dev": true,
224+ "requires": {
225+ "duplexer": "^0.1.2"
226+ },
227+ "dependencies": {
228+ "duplexer": {
229+ "version": "0.1.2",
230+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
231+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
232+ "dev": true
233+ }
234+ }
235+ },
236 "has": {
237 "version": "1.0.3",
238 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
239 diff --git a/package.json b/package.json
240index 7490fe8..8d50179 100644
241--- a/package.json
242+++ b/package.json
243 @@ -186,6 +186,7 @@
244 "build": "concurrently npm:gen:op npm:gen:shadowdom && npm run gen:types",
245 "test": "ava test/basic.test.cjs",
246 "bundle": "concurrently npm:lib:* -m 25 && concurrently npm:shadow:* -m 25",
247+ "bundle:sizes": "cd build && node bundle-sizes.js",
248 "gen:op": "cd build && node props.js \"\" true",
249 "gen:nowhere": "cd build && node props \"\" false",
250 "gen:shadowdom": "cd build && node props \"\" false \":host\" \"shadow\"",
251 @@ -313,9 +314,11 @@
252 },
253 "devDependencies": {
254 "ava": "^3.15.0",
255+ "brotli-size": "^4.0.0",
256 "colorjs.io": "^0.4.1-patch.1",
257 "concurrently": "^7.2.2",
258 "cssnano": "^5.1.10",
259+ "gzip-size": "^7.0.0",
260 "json": "^11.0.0",
261 "open-color": "^1.9.1",
262 "postcss": "^8.3.9",