@prairielearn/compiled-assets 3.3.1 → 3.3.2
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/CHANGELOG.md +6 -0
- package/dist/cli.js +41 -7
- package/dist/cli.js.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +57 -8
package/CHANGELOG.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,19 @@ import prettyBytes from 'pretty-bytes';
|
|
|
8
8
|
import { build } from './index.js';
|
|
9
9
|
const gzip = promisify(zlib.gzip);
|
|
10
10
|
const brotli = promisify(zlib.brotliCompress);
|
|
11
|
+
/**
|
|
12
|
+
* Collects all unique asset paths from the manifest, including entry points and their preloads.
|
|
13
|
+
*/
|
|
14
|
+
function getAllAssetPaths(manifest) {
|
|
15
|
+
const allPaths = new Set();
|
|
16
|
+
for (const asset of Object.values(manifest)) {
|
|
17
|
+
allPaths.add(asset.assetPath);
|
|
18
|
+
for (const preload of asset.preloads) {
|
|
19
|
+
allPaths.add(preload);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return allPaths;
|
|
23
|
+
}
|
|
11
24
|
/**
|
|
12
25
|
* Writes gzip and brotli compressed versions of the assets in the specified directory.
|
|
13
26
|
* It reads each asset file, compresses it using gzip and brotli algorithms, and writes the compressed files
|
|
@@ -19,14 +32,15 @@ const brotli = promisify(zlib.brotliCompress);
|
|
|
19
32
|
*/
|
|
20
33
|
async function writeCompressedAssets(destination, manifest) {
|
|
21
34
|
const compressedSizes = {};
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
const allAssetPaths = getAllAssetPaths(manifest);
|
|
36
|
+
await Promise.all([...allAssetPaths].map(async (assetPath) => {
|
|
37
|
+
const destinationFilePath = path.resolve(destination, assetPath);
|
|
24
38
|
const contents = await fs.readFile(destinationFilePath);
|
|
25
39
|
const gzipCompressed = await gzip(contents);
|
|
26
40
|
const brotliCompressed = await brotli(contents);
|
|
27
41
|
await fs.writeFile(`${destinationFilePath}.gz`, gzipCompressed);
|
|
28
42
|
await fs.writeFile(`${destinationFilePath}.br`, brotliCompressed);
|
|
29
|
-
compressedSizes[
|
|
43
|
+
compressedSizes[assetPath] = {
|
|
30
44
|
raw: contents.length,
|
|
31
45
|
gzip: gzipCompressed.length,
|
|
32
46
|
brotli: brotliCompressed.length,
|
|
@@ -34,6 +48,26 @@ async function writeCompressedAssets(destination, manifest) {
|
|
|
34
48
|
}));
|
|
35
49
|
return compressedSizes;
|
|
36
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Calculates the total size of an entry point including all its preloaded chunks.
|
|
53
|
+
*/
|
|
54
|
+
function calculateTotalSizes(asset, compressedSizes) {
|
|
55
|
+
const entrySizes = compressedSizes[asset.assetPath];
|
|
56
|
+
const total = {
|
|
57
|
+
raw: entrySizes.raw,
|
|
58
|
+
gzip: entrySizes.gzip,
|
|
59
|
+
brotli: entrySizes.brotli,
|
|
60
|
+
};
|
|
61
|
+
for (const preload of asset.preloads) {
|
|
62
|
+
const preloadSizes = compressedSizes[preload];
|
|
63
|
+
if (preloadSizes) {
|
|
64
|
+
total.raw += preloadSizes.raw;
|
|
65
|
+
total.gzip += preloadSizes.gzip;
|
|
66
|
+
total.brotli += preloadSizes.brotli;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return total;
|
|
70
|
+
}
|
|
37
71
|
program.command('build <source> <destination>').action(async (source, destination) => {
|
|
38
72
|
const manifest = await build(source, destination);
|
|
39
73
|
// Write gzip and brotli versions of the output files. Record size information
|
|
@@ -42,12 +76,12 @@ program.command('build <source> <destination>').action(async (source, destinatio
|
|
|
42
76
|
// Format the output into an object that we can pass to `console.table`.
|
|
43
77
|
const results = {};
|
|
44
78
|
Object.entries(manifest).forEach(([entryPoint, asset]) => {
|
|
45
|
-
const
|
|
79
|
+
const totalSizes = calculateTotalSizes(asset, compressedSizes);
|
|
46
80
|
results[entryPoint] = {
|
|
47
81
|
'Output file': asset.assetPath,
|
|
48
|
-
Size: prettyBytes(
|
|
49
|
-
'Size (gzip)': prettyBytes(
|
|
50
|
-
'Size (brotli)': prettyBytes(
|
|
82
|
+
Size: prettyBytes(totalSizes.raw),
|
|
83
|
+
'Size (gzip)': prettyBytes(totalSizes.gzip),
|
|
84
|
+
'Size (brotli)': prettyBytes(totalSizes.brotli),
|
|
51
85
|
};
|
|
52
86
|
});
|
|
53
87
|
console.table(results);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAuB,KAAK,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport path from 'path';\nimport { promisify } from 'util';\nimport zlib from 'zlib';\n\nimport { program } from 'commander';\nimport fs from 'fs-extra';\nimport prettyBytes from 'pretty-bytes';\n\nimport { type AssetsManifest, build } from './index.js';\n\nconst gzip = promisify(zlib.gzip);\nconst brotli = promisify(zlib.brotliCompress);\n\ninterface AssetSizes {\n raw: number;\n gzip: number;\n brotli: number;\n}\n\ntype CompressedSizes = Record<string, AssetSizes>;\n\n/**\n * Collects all unique asset paths from the manifest, including entry points and their preloads.\n */\nfunction getAllAssetPaths(manifest: AssetsManifest): Set<string> {\n const allPaths = new Set<string>();\n for (const asset of Object.values(manifest)) {\n allPaths.add(asset.assetPath);\n for (const preload of asset.preloads) {\n allPaths.add(preload);\n }\n }\n return allPaths;\n}\n\n/**\n * Writes gzip and brotli compressed versions of the assets in the specified directory.\n * It reads each asset file, compresses it using gzip and brotli algorithms, and writes the compressed files\n * with appropriate extensions (.gz and .br) in the same directory.\n *\n * @param destination Directory where the compressed assets will be written.\n * @param manifest The assets manifest containing information about the assets.\n * @returns A promise that resolves to an object containing the sizes of the original, gzip, and brotli compressed assets.\n */\nasync function writeCompressedAssets(\n destination: string,\n manifest: AssetsManifest,\n): Promise<CompressedSizes> {\n const compressedSizes: CompressedSizes = {};\n const allAssetPaths = getAllAssetPaths(manifest);\n\n await Promise.all(\n [...allAssetPaths].map(async (assetPath) => {\n const destinationFilePath = path.resolve(destination, assetPath);\n const contents = await fs.readFile(destinationFilePath);\n const gzipCompressed = await gzip(contents);\n const brotliCompressed = await brotli(contents);\n await fs.writeFile(`${destinationFilePath}.gz`, gzipCompressed);\n await fs.writeFile(`${destinationFilePath}.br`, brotliCompressed);\n compressedSizes[assetPath] = {\n raw: contents.length,\n gzip: gzipCompressed.length,\n brotli: brotliCompressed.length,\n };\n }),\n );\n return compressedSizes;\n}\n\n/**\n * Calculates the total size of an entry point including all its preloaded chunks.\n */\nfunction calculateTotalSizes(\n asset: AssetsManifest[string],\n compressedSizes: CompressedSizes,\n): AssetSizes {\n const entrySizes = compressedSizes[asset.assetPath];\n const total: AssetSizes = {\n raw: entrySizes.raw,\n gzip: entrySizes.gzip,\n brotli: entrySizes.brotli,\n };\n\n for (const preload of asset.preloads) {\n const preloadSizes = compressedSizes[preload];\n if (preloadSizes) {\n total.raw += preloadSizes.raw;\n total.gzip += preloadSizes.gzip;\n total.brotli += preloadSizes.brotli;\n }\n }\n\n return total;\n}\n\nprogram.command('build <source> <destination>').action(async (source, destination) => {\n const manifest = await build(source, destination);\n\n // Write gzip and brotli versions of the output files. Record size information\n // so we can show it to the user.\n const compressedSizes = await writeCompressedAssets(destination, manifest);\n\n // Format the output into an object that we can pass to `console.table`.\n const results: Record<string, any> = {};\n Object.entries(manifest).forEach(([entryPoint, asset]) => {\n const totalSizes = calculateTotalSizes(asset, compressedSizes);\n\n results[entryPoint] = {\n 'Output file': asset.assetPath,\n Size: prettyBytes(totalSizes.raw),\n 'Size (gzip)': prettyBytes(totalSizes.gzip),\n 'Size (brotli)': prettyBytes(totalSizes.brotli),\n };\n });\n console.table(results);\n});\n\nprogram.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/compiled-assets",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
29
|
-
"@types/node": "^22.19.
|
|
30
|
-
"@vitest/coverage-v8": "^4.0.
|
|
29
|
+
"@types/node": "^22.19.5",
|
|
30
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
31
31
|
"express": "^4.22.1",
|
|
32
32
|
"get-port": "^7.1.0",
|
|
33
33
|
"node-fetch": "^3.3.2",
|
|
34
34
|
"tsx": "^4.21.0",
|
|
35
35
|
"typescript": "^5.9.3",
|
|
36
|
-
"vitest": "^4.0.
|
|
36
|
+
"vitest": "^4.0.17"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/cli.ts
CHANGED
|
@@ -12,7 +12,27 @@ import { type AssetsManifest, build } from './index.js';
|
|
|
12
12
|
const gzip = promisify(zlib.gzip);
|
|
13
13
|
const brotli = promisify(zlib.brotliCompress);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
interface AssetSizes {
|
|
16
|
+
raw: number;
|
|
17
|
+
gzip: number;
|
|
18
|
+
brotli: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type CompressedSizes = Record<string, AssetSizes>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Collects all unique asset paths from the manifest, including entry points and their preloads.
|
|
25
|
+
*/
|
|
26
|
+
function getAllAssetPaths(manifest: AssetsManifest): Set<string> {
|
|
27
|
+
const allPaths = new Set<string>();
|
|
28
|
+
for (const asset of Object.values(manifest)) {
|
|
29
|
+
allPaths.add(asset.assetPath);
|
|
30
|
+
for (const preload of asset.preloads) {
|
|
31
|
+
allPaths.add(preload);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return allPaths;
|
|
35
|
+
}
|
|
16
36
|
|
|
17
37
|
/**
|
|
18
38
|
* Writes gzip and brotli compressed versions of the assets in the specified directory.
|
|
@@ -28,15 +48,17 @@ async function writeCompressedAssets(
|
|
|
28
48
|
manifest: AssetsManifest,
|
|
29
49
|
): Promise<CompressedSizes> {
|
|
30
50
|
const compressedSizes: CompressedSizes = {};
|
|
51
|
+
const allAssetPaths = getAllAssetPaths(manifest);
|
|
52
|
+
|
|
31
53
|
await Promise.all(
|
|
32
|
-
|
|
33
|
-
const destinationFilePath = path.resolve(destination,
|
|
54
|
+
[...allAssetPaths].map(async (assetPath) => {
|
|
55
|
+
const destinationFilePath = path.resolve(destination, assetPath);
|
|
34
56
|
const contents = await fs.readFile(destinationFilePath);
|
|
35
57
|
const gzipCompressed = await gzip(contents);
|
|
36
58
|
const brotliCompressed = await brotli(contents);
|
|
37
59
|
await fs.writeFile(`${destinationFilePath}.gz`, gzipCompressed);
|
|
38
60
|
await fs.writeFile(`${destinationFilePath}.br`, brotliCompressed);
|
|
39
|
-
compressedSizes[
|
|
61
|
+
compressedSizes[assetPath] = {
|
|
40
62
|
raw: contents.length,
|
|
41
63
|
gzip: gzipCompressed.length,
|
|
42
64
|
brotli: brotliCompressed.length,
|
|
@@ -46,6 +68,32 @@ async function writeCompressedAssets(
|
|
|
46
68
|
return compressedSizes;
|
|
47
69
|
}
|
|
48
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Calculates the total size of an entry point including all its preloaded chunks.
|
|
73
|
+
*/
|
|
74
|
+
function calculateTotalSizes(
|
|
75
|
+
asset: AssetsManifest[string],
|
|
76
|
+
compressedSizes: CompressedSizes,
|
|
77
|
+
): AssetSizes {
|
|
78
|
+
const entrySizes = compressedSizes[asset.assetPath];
|
|
79
|
+
const total: AssetSizes = {
|
|
80
|
+
raw: entrySizes.raw,
|
|
81
|
+
gzip: entrySizes.gzip,
|
|
82
|
+
brotli: entrySizes.brotli,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
for (const preload of asset.preloads) {
|
|
86
|
+
const preloadSizes = compressedSizes[preload];
|
|
87
|
+
if (preloadSizes) {
|
|
88
|
+
total.raw += preloadSizes.raw;
|
|
89
|
+
total.gzip += preloadSizes.gzip;
|
|
90
|
+
total.brotli += preloadSizes.brotli;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return total;
|
|
95
|
+
}
|
|
96
|
+
|
|
49
97
|
program.command('build <source> <destination>').action(async (source, destination) => {
|
|
50
98
|
const manifest = await build(source, destination);
|
|
51
99
|
|
|
@@ -56,12 +104,13 @@ program.command('build <source> <destination>').action(async (source, destinatio
|
|
|
56
104
|
// Format the output into an object that we can pass to `console.table`.
|
|
57
105
|
const results: Record<string, any> = {};
|
|
58
106
|
Object.entries(manifest).forEach(([entryPoint, asset]) => {
|
|
59
|
-
const
|
|
107
|
+
const totalSizes = calculateTotalSizes(asset, compressedSizes);
|
|
108
|
+
|
|
60
109
|
results[entryPoint] = {
|
|
61
110
|
'Output file': asset.assetPath,
|
|
62
|
-
Size: prettyBytes(
|
|
63
|
-
'Size (gzip)': prettyBytes(
|
|
64
|
-
'Size (brotli)': prettyBytes(
|
|
111
|
+
Size: prettyBytes(totalSizes.raw),
|
|
112
|
+
'Size (gzip)': prettyBytes(totalSizes.gzip),
|
|
113
|
+
'Size (brotli)': prettyBytes(totalSizes.brotli),
|
|
65
114
|
};
|
|
66
115
|
});
|
|
67
116
|
console.table(results);
|