svgo-mbt 0.1.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/index.mjs ADDED
@@ -0,0 +1,141 @@
1
+ // Loader for the wasm-gc build of svgo.mbt. Works in Node >= 24 (V8 13.6, JS String
2
+ // Builtins with imported string constants; Node 22 and 23 cannot load it) and in
3
+ // browsers with the JS String Builtins proposal (Chrome 130+, Firefox 134+,
4
+ // Safari 18.4+).
5
+ let instancePromise;
6
+
7
+ const hostImports = () => ({
8
+ // Transcendentals and the slow path of number parsing come from the host,
9
+ // which keeps their MoonBit implementations out of the wasm.
10
+ Math: {
11
+ sin: Math.sin,
12
+ cos: Math.cos,
13
+ tan: Math.tan,
14
+ asin: Math.asin,
15
+ acos: Math.acos,
16
+ atan: Math.atan,
17
+ },
18
+ Number: { parseFloat: Number.parseFloat },
19
+ // `println` and friends are never called by the optimizer, but the module
20
+ // declares the imports; provide inert implementations.
21
+ spectest: {
22
+ print_char: () => {},
23
+ },
24
+ __moonbit_fs_unstable: new Proxy({}, { get: () => () => 0 }),
25
+ __moonbit_time_unstable: new Proxy({}, { get: () => () => 0 }),
26
+ __moonbit_rand_unstable: new Proxy({}, { get: () => () => 0 }),
27
+ __moonbit_io_unstable: new Proxy({}, { get: () => () => 0 }),
28
+ __moonbit_sys_unstable: new Proxy({}, { get: () => () => 0 }),
29
+ });
30
+
31
+ async function loadBytes(url) {
32
+ if (typeof fetch === "function" && !url.startsWith("file:")) {
33
+ const res = await fetch(url);
34
+ return new Uint8Array(await res.arrayBuffer());
35
+ }
36
+ const { readFile } = await import("node:fs/promises");
37
+ const { fileURLToPath } = await import("node:url");
38
+ return readFile(fileURLToPath(url));
39
+ }
40
+
41
+ /** Instantiate the module once. `wasmUrl` defaults to svgo.wasm next to this file. */
42
+ export async function init(wasmUrl = new URL("./svgo.wasm", import.meta.url)) {
43
+ if (!instancePromise) {
44
+ instancePromise = (async () => {
45
+ const bytes = await loadBytes(wasmUrl.toString());
46
+ const { instance } = await WebAssembly.instantiate(bytes, hostImports(), {
47
+ builtins: ["js-string"],
48
+ importedStringConstants: "_",
49
+ });
50
+ return instance.exports;
51
+ })();
52
+ }
53
+ return instancePromise;
54
+ }
55
+
56
+ // Strings cross the wasm boundary as length-prefixed tokens, `<length>:<text>`
57
+ // with the length in UTF-16 code units; svgo/wasm/wasm.mbt reads and writes
58
+ // the same layout. This replaces JSON on both sides of the call.
59
+
60
+ const token = (s) => `${s.length}:${s}`;
61
+
62
+ function encodeValue(v) {
63
+ if (v === null || v === undefined) return token("z");
64
+ if (typeof v === "boolean") return token(v ? "b1" : "b0");
65
+ if (typeof v === "number") return Number.isFinite(v) ? token(`n${v}`) : token("z");
66
+ if (typeof v === "string") return token(`s${v}`);
67
+ if (Array.isArray(v)) return token(`a${v.length}`) + v.map(encodeValue).join("");
68
+ const keys = Object.keys(v);
69
+ return token(`o${keys.length}`) + keys.map((k) => token(k) + encodeValue(v[k])).join("");
70
+ }
71
+
72
+ function encodeConfig(options) {
73
+ const params = { ...(options.params ?? {}) };
74
+ let plugins = "";
75
+ if (Array.isArray(options.plugins)) {
76
+ const names = [];
77
+ for (const entry of options.plugins) {
78
+ if (typeof entry === "string") {
79
+ names.push(entry);
80
+ } else if (entry && typeof entry.name === "string") {
81
+ if (entry.params !== undefined) params[entry.name] = entry.params;
82
+ names.push(entry.name);
83
+ }
84
+ }
85
+ plugins = token(String(names.length)) + names.map(token).join("");
86
+ } else {
87
+ plugins = token("");
88
+ }
89
+ const keys = Object.keys(params);
90
+ return (
91
+ token(typeof options.precision === "number" ? String(Math.trunc(options.precision)) : "") +
92
+ token(options.multipass === false ? "0" : "1") +
93
+ token(options.pretty === true ? "1" : "0") +
94
+ plugins +
95
+ token(String(keys.length)) +
96
+ keys.map((k) => token(k) + encodeValue(params[k])).join("")
97
+ );
98
+ }
99
+
100
+ function* tokens(s) {
101
+ let i = 0;
102
+ while (i < s.length) {
103
+ const colon = s.indexOf(":", i);
104
+ const length = Number(s.slice(i, colon));
105
+ yield s.slice(colon + 1, colon + 1 + length);
106
+ i = colon + 1 + length;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Optimize an SVG string. Options: { plugins?: (string | {name: string, params?: object})[],
112
+ * params?: Record<string, object>, precision?: number,
113
+ * multipass?: boolean, pretty?: boolean }. Returns { data, originalSize, size, passes, applied }.
114
+ */
115
+ export async function optimize(svg, options = {}) {
116
+ const exports = await init();
117
+ const it = tokens(exports.optimize(svg, encodeConfig(options)));
118
+ const status = it.next().value;
119
+ if (status !== "ok") throw new Error(it.next().value);
120
+ const originalSize = Number(it.next().value);
121
+ const size = Number(it.next().value);
122
+ const passes = Number(it.next().value);
123
+ const applied = [];
124
+ for (let n = Number(it.next().value); n > 0; n--) applied.push(it.next().value);
125
+ const data = it.next().value;
126
+ return { data, originalSize, size, passes, applied };
127
+ }
128
+
129
+ /** List available plugins: [{ name, description, enabled }]. */
130
+ export async function plugins() {
131
+ const exports = await init();
132
+ const it = tokens(exports.plugins());
133
+ const out = [];
134
+ for (let n = Number(it.next().value); n > 0; n--) {
135
+ const name = it.next().value;
136
+ const description = it.next().value;
137
+ const enabled = it.next().value === "1";
138
+ out.push({ name, description, enabled });
139
+ }
140
+ return out;
141
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "svgo-mbt",
3
+ "version": "0.1.2",
4
+ "description": "SVG optimizer written in MoonBit: a wasm-gc library and the svgo-mbt command line tool",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/PerfectPan/svgo.mbt.git",
8
+ "directory": "packages/svgo-mbt"
9
+ },
10
+ "homepage": "https://perfectpan.github.io/svgo.mbt/",
11
+ "bugs": "https://github.com/PerfectPan/svgo.mbt/issues",
12
+ "type": "module",
13
+ "main": "index.mjs",
14
+ "bin": {
15
+ "svgo-mbt": "./cli.mjs"
16
+ },
17
+ "exports": {
18
+ ".": "./index.mjs"
19
+ },
20
+ "files": [
21
+ "index.mjs",
22
+ "cli.mjs",
23
+ "svgo.wasm",
24
+ "README.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=24"
28
+ },
29
+ "license": "MIT",
30
+ "keywords": [
31
+ "svg",
32
+ "svgo",
33
+ "optimizer",
34
+ "minifier",
35
+ "wasm",
36
+ "moonbit"
37
+ ],
38
+ "scripts": {
39
+ "test": "node --test"
40
+ }
41
+ }
package/svgo.wasm ADDED
Binary file