@fontmin-rs/wasm 1.0.0 → 1.0.1
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/fontmin_wasm_core_bg.wasm +0 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.mjs +158 -90
- package/package.json +1 -1
|
Binary file
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,11 @@ type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Modul
|
|
|
5
5
|
declare function isWasmInitialized(): boolean;
|
|
6
6
|
declare function initWasm(input?: InitInput): Promise<void>;
|
|
7
7
|
//#endregion
|
|
8
|
-
//#region src/diagnostics.d.ts
|
|
8
|
+
//#region ../../packages/fontmin/src/runtime-neutral/diagnostics.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* Normalizes Rust bridge diagnostics without depending on a concrete runtime.
|
|
11
|
+
* Native and WASM adapters use this module to expose the same error contract.
|
|
12
|
+
*/
|
|
9
13
|
type FontminDiagnosticCode = 'fontmin::config' | 'fontmin::convert_failed' | 'fontmin::invalid_font' | 'fontmin::io' | 'fontmin::missing_glyph' | 'fontmin::napi_bridge_failed' | 'fontmin::plugin_failed' | 'fontmin::unsupported_format';
|
|
10
14
|
declare class FontminDiagnosticError extends Error {
|
|
11
15
|
readonly name = "FontminDiagnosticError";
|
package/dist/index.mjs
CHANGED
|
@@ -14,8 +14,13 @@ async function getWasmModule() {
|
|
|
14
14
|
return module;
|
|
15
15
|
}
|
|
16
16
|
async function initWasm(input) {
|
|
17
|
-
initialization ??= initializeWasm(input);
|
|
18
|
-
|
|
17
|
+
const attempt = initialization ??= initializeWasm(input);
|
|
18
|
+
try {
|
|
19
|
+
await attempt;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (initialization === attempt) initialization = void 0;
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
async function initializeWasm(input) {
|
|
21
26
|
const module = await import("./fontmin_wasm_core-C2e3XISU.mjs");
|
|
@@ -25,7 +30,7 @@ async function initializeWasm(input) {
|
|
|
25
30
|
return module;
|
|
26
31
|
}
|
|
27
32
|
//#endregion
|
|
28
|
-
//#region src/diagnostics.ts
|
|
33
|
+
//#region ../../packages/fontmin/src/runtime-neutral/diagnostics.ts
|
|
29
34
|
const bridgeDiagnosticPattern = /^\[(?<code>fontmin::[a-z_]+)\] (?<message>[\s\S]+)$/u;
|
|
30
35
|
var FontminDiagnosticError = class extends Error {
|
|
31
36
|
name = "FontminDiagnosticError";
|
|
@@ -35,27 +40,129 @@ var FontminDiagnosticError = class extends Error {
|
|
|
35
40
|
this.code = code;
|
|
36
41
|
}
|
|
37
42
|
};
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
function createFontminDiagnostics(operationName) {
|
|
44
|
+
function normalizeFontminDiagnostic(error) {
|
|
45
|
+
let message;
|
|
46
|
+
if (error instanceof Error) message = error.message;
|
|
47
|
+
else if (typeof error === "string") message = error;
|
|
48
|
+
const match = message?.match(bridgeDiagnosticPattern);
|
|
49
|
+
if (match === null || match === void 0) {
|
|
50
|
+
if (error instanceof Error) return error;
|
|
51
|
+
if (typeof error === "string") return new Error(error, { cause: error });
|
|
52
|
+
return new Error(`${operationName} failed`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
const code = match.groups?.["code"];
|
|
55
|
+
const diagnosticMessage = match.groups?.["message"];
|
|
56
|
+
if (code === void 0 || diagnosticMessage === void 0) return new Error(`${operationName} returned an invalid diagnostic`, { cause: error });
|
|
57
|
+
return new FontminDiagnosticError(code, diagnosticMessage, { cause: error });
|
|
47
58
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
function withFontminDiagnostics(operation) {
|
|
60
|
+
try {
|
|
61
|
+
return operation();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw normalizeFontminDiagnostic(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
normalizeFontminDiagnostic,
|
|
68
|
+
withFontminDiagnostics
|
|
69
|
+
};
|
|
52
70
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/diagnostics.ts
|
|
73
|
+
const { normalizeFontminDiagnostic, withFontminDiagnostics } = createFontminDiagnostics("fontmin-rs WASM operation");
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region ../../packages/fontmin/src/runtime-neutral/optimize-policy.ts
|
|
76
|
+
const FONT_CONVERSIONS = [
|
|
77
|
+
{
|
|
78
|
+
inputFormat: "otf",
|
|
79
|
+
name: "otf2ttf",
|
|
80
|
+
outputFormat: "ttf"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
inputFormat: "svg",
|
|
84
|
+
name: "svg2ttf",
|
|
85
|
+
outputFormat: "ttf"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
inputFormat: "ttf",
|
|
89
|
+
name: "ttf2eot",
|
|
90
|
+
outputFormat: "eot"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
inputFormat: "ttf",
|
|
94
|
+
name: "ttf2svg",
|
|
95
|
+
outputFormat: "svg"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
inputFormat: "ttf",
|
|
99
|
+
name: "ttf2woff",
|
|
100
|
+
outputFormat: "woff"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
inputFormat: "ttf",
|
|
104
|
+
name: "ttf2woff2",
|
|
105
|
+
outputFormat: "woff2"
|
|
58
106
|
}
|
|
107
|
+
];
|
|
108
|
+
async function applyAssetTransform(assets, transform, context, normalize) {
|
|
109
|
+
const transformedAssets = [];
|
|
110
|
+
for (const asset of assets) {
|
|
111
|
+
const result = await transform(asset, context);
|
|
112
|
+
if (result === void 0) transformedAssets.push(asset);
|
|
113
|
+
else if (Array.isArray(result)) transformedAssets.push(...result.map((asset) => normalize(asset)));
|
|
114
|
+
else if (result !== null) transformedAssets.push(normalize(result));
|
|
115
|
+
}
|
|
116
|
+
return transformedAssets;
|
|
117
|
+
}
|
|
118
|
+
async function applyAssetConversion(assets, clone, convert) {
|
|
119
|
+
const primaryAssets = [];
|
|
120
|
+
const clonedAssets = [];
|
|
121
|
+
for (const asset of assets) {
|
|
122
|
+
const convertedAsset = await convert(asset);
|
|
123
|
+
if (convertedAsset === void 0) primaryAssets.push(asset);
|
|
124
|
+
else if (clone) {
|
|
125
|
+
primaryAssets.push(asset);
|
|
126
|
+
clonedAssets.push(convertedAsset);
|
|
127
|
+
} else primaryAssets.push(convertedAsset);
|
|
128
|
+
}
|
|
129
|
+
return clone ? [...primaryAssets, ...clonedAssets] : primaryAssets;
|
|
130
|
+
}
|
|
131
|
+
async function applyFontConversion(assets, pluginName, clone, formatOf, convert) {
|
|
132
|
+
const conversion = FONT_CONVERSIONS.find((candidate) => candidate.name === pluginName);
|
|
133
|
+
if (conversion === void 0) return;
|
|
134
|
+
return applyAssetConversion(assets, clone, (asset) => formatOf(asset) === conversion.inputFormat ? convert(asset, conversion) : void 0);
|
|
135
|
+
}
|
|
136
|
+
async function flatMapAssets(assets, transform) {
|
|
137
|
+
const transformedAssets = [];
|
|
138
|
+
for (const asset of assets) transformedAssets.push(...await transform(asset));
|
|
139
|
+
return transformedAssets;
|
|
140
|
+
}
|
|
141
|
+
function missingGlyphWarning(report) {
|
|
142
|
+
if (report.missing.length === 0) return;
|
|
143
|
+
const visible = report.missing.slice(0, 16).map((codePoint) => `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`).join(", ");
|
|
144
|
+
const remaining = report.missing.length - 16;
|
|
145
|
+
return `missing glyphs for requested Unicode code points: ${visible}${remaining > 0 ? `, and ${remaining} more` : ""}`;
|
|
146
|
+
}
|
|
147
|
+
function normalizeDeliverySlices$1(values, options = {}) {
|
|
148
|
+
if (!Array.isArray(values)) throw new TypeError("unicode delivery slices must be an array");
|
|
149
|
+
if (values.length === 0) {
|
|
150
|
+
if (options.allowEmpty === true) return [];
|
|
151
|
+
throw new Error("unicode delivery slices must not be empty");
|
|
152
|
+
}
|
|
153
|
+
const names = /* @__PURE__ */ new Set();
|
|
154
|
+
return values.map((value, index) => {
|
|
155
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`unicode delivery slice ${index + 1} must be an object`);
|
|
156
|
+
const { name, unicodeRanges } = value;
|
|
157
|
+
if (typeof name !== "string" || name.length === 0 || !/^[A-Za-z0-9_-]+$/u.test(name)) throw new Error(`unicode delivery slice ${index + 1} must have a name containing only letters, digits, hyphens, or underscores`);
|
|
158
|
+
if (names.has(name)) throw new Error(`unicode delivery slice name is duplicated: ${name}`);
|
|
159
|
+
if (!Array.isArray(unicodeRanges) || unicodeRanges.length === 0 || unicodeRanges.some((range) => typeof range !== "string" || range.length === 0)) throw new Error(`unicode delivery slice ${name} must include at least one Unicode range`);
|
|
160
|
+
names.add(name);
|
|
161
|
+
return {
|
|
162
|
+
name,
|
|
163
|
+
unicodeRanges: [...unicodeRanges]
|
|
164
|
+
};
|
|
165
|
+
});
|
|
59
166
|
}
|
|
60
167
|
//#endregion
|
|
61
168
|
//#region src/native.ts
|
|
@@ -130,12 +237,6 @@ function coverageOptions(options) {
|
|
|
130
237
|
if (options.unicodes !== void 0) coverage.unicodes = options.unicodes;
|
|
131
238
|
return coverage;
|
|
132
239
|
}
|
|
133
|
-
function missingGlyphWarning(report) {
|
|
134
|
-
if (report.missing.length === 0) return;
|
|
135
|
-
const visible = report.missing.slice(0, 16).map((codepoint) => `U+${codepoint.toString(16).toUpperCase().padStart(4, "0")}`).join(", ");
|
|
136
|
-
const remaining = report.missing.length - 16;
|
|
137
|
-
return `missing glyphs for requested Unicode code points: ${visible}${remaining > 0 ? `, and ${remaining} more` : ""}`;
|
|
138
|
-
}
|
|
139
240
|
//#endregion
|
|
140
241
|
//#region src/plugins.ts
|
|
141
242
|
function plugin(name, options) {
|
|
@@ -163,21 +264,7 @@ function deliverySlices(slices) {
|
|
|
163
264
|
})) });
|
|
164
265
|
}
|
|
165
266
|
function normalizeDeliverySlices(options) {
|
|
166
|
-
|
|
167
|
-
if (!Array.isArray(values) || values.length === 0) throw new Error("unicode delivery slices must not be empty");
|
|
168
|
-
const names = /* @__PURE__ */ new Set();
|
|
169
|
-
return values.map((value, index) => {
|
|
170
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`unicode delivery slice ${index + 1} must be an object`);
|
|
171
|
-
const { name, unicodeRanges } = value;
|
|
172
|
-
if (typeof name !== "string" || name.length === 0 || !/^[A-Za-z0-9_-]+$/u.test(name)) throw new Error(`unicode delivery slice ${index + 1} must have a name containing only letters, digits, hyphens, or underscores`);
|
|
173
|
-
if (names.has(name)) throw new Error(`unicode delivery slice name is duplicated: ${name}`);
|
|
174
|
-
if (!Array.isArray(unicodeRanges) || unicodeRanges.length === 0 || unicodeRanges.some((range) => typeof range !== "string" || range.length === 0)) throw new Error(`unicode delivery slice ${name} must include at least one Unicode range`);
|
|
175
|
-
names.add(name);
|
|
176
|
-
return {
|
|
177
|
-
name,
|
|
178
|
-
unicodeRanges: [...unicodeRanges]
|
|
179
|
-
};
|
|
180
|
-
});
|
|
267
|
+
return normalizeDeliverySlices$1(options.slices);
|
|
181
268
|
}
|
|
182
269
|
function ttf2woff(options = {}) {
|
|
183
270
|
return plugin("ttf2woff", options);
|
|
@@ -256,30 +343,21 @@ async function optimizeBrowser(config) {
|
|
|
256
343
|
for (const plugin of config.plugins ?? []) {
|
|
257
344
|
if (plugin.name === "glyph") {
|
|
258
345
|
const options = optionsOf(plugin);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (asset.format !== "ttf") {
|
|
262
|
-
subsetAssets.push(asset);
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
346
|
+
assets = await flatMapAssets(assets, async (asset) => {
|
|
347
|
+
if (asset.format !== "ttf") return [asset];
|
|
265
348
|
const subsetAsset = {
|
|
266
349
|
...asset,
|
|
267
350
|
contents: await subsetTtf(asset.contents, options)
|
|
268
351
|
};
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
assets = subsetAssets;
|
|
352
|
+
return options.clone === true ? [asset, subsetAsset] : [subsetAsset];
|
|
353
|
+
});
|
|
272
354
|
continue;
|
|
273
355
|
}
|
|
274
356
|
if (plugin.name === "unicodeSlices") {
|
|
275
357
|
const slices = normalizeDeliverySlices(optionsOf(plugin));
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
slicedAssets.push(asset);
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
for (const slice of slices) slicedAssets.push({
|
|
358
|
+
assets = await flatMapAssets(assets, async (asset) => {
|
|
359
|
+
if (asset.format !== "ttf") return [asset];
|
|
360
|
+
return Promise.all(slices.map(async (slice) => ({
|
|
283
361
|
...asset,
|
|
284
362
|
contents: await subsetTtf(asset.contents, {
|
|
285
363
|
missingGlyphs: "ignore",
|
|
@@ -287,12 +365,12 @@ async function optimizeBrowser(config) {
|
|
|
287
365
|
}),
|
|
288
366
|
fileName: appendFileNameSuffix(asset.fileName, slice.name),
|
|
289
367
|
unicodeRanges: slice.unicodeRanges
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
assets = slicedAssets;
|
|
368
|
+
})));
|
|
369
|
+
});
|
|
293
370
|
continue;
|
|
294
371
|
}
|
|
295
372
|
if (plugin.name === "css") {
|
|
373
|
+
const options = optionsOf(plugin);
|
|
296
374
|
const css = await generateFontFaceCss(assets.filter((asset) => [
|
|
297
375
|
"eot",
|
|
298
376
|
"svg",
|
|
@@ -309,11 +387,17 @@ async function optimizeBrowser(config) {
|
|
|
309
387
|
...source,
|
|
310
388
|
unicodeRanges: asset.unicodeRanges
|
|
311
389
|
};
|
|
312
|
-
}),
|
|
313
|
-
const firstFont = assets.find((asset) =>
|
|
390
|
+
}), options);
|
|
391
|
+
const firstFont = assets.find((asset) => [
|
|
392
|
+
"eot",
|
|
393
|
+
"svg",
|
|
394
|
+
"ttf",
|
|
395
|
+
"woff",
|
|
396
|
+
"woff2"
|
|
397
|
+
].includes(asset.format ?? ""));
|
|
314
398
|
if (firstFont !== void 0) assets.push({
|
|
315
399
|
contents: new TextEncoder().encode(css),
|
|
316
|
-
fileName: replaceExtension(firstFont.fileName, "css"),
|
|
400
|
+
fileName: replaceExtension(firstFont.fileName, options.target ?? "css"),
|
|
317
401
|
format: "css"
|
|
318
402
|
});
|
|
319
403
|
continue;
|
|
@@ -349,38 +433,22 @@ async function optimizeBrowser(config) {
|
|
|
349
433
|
});
|
|
350
434
|
}
|
|
351
435
|
};
|
|
352
|
-
|
|
353
|
-
for (const asset of assets) {
|
|
354
|
-
const result = await plugin.transform(asset, context);
|
|
355
|
-
if (result === null) continue;
|
|
356
|
-
if (result === void 0) transformed.push(asset);
|
|
357
|
-
else transformed.push(...(Array.isArray(result) ? result : [result]).map((asset) => formatAsset(asset)));
|
|
358
|
-
}
|
|
359
|
-
assets = [...transformed, ...emitted];
|
|
436
|
+
assets = [...await applyAssetTransform(assets, plugin.transform, context, formatAsset), ...emitted];
|
|
360
437
|
continue;
|
|
361
438
|
}
|
|
362
439
|
const clone = optionsOf(plugin).clone !== false;
|
|
363
|
-
const convertedAssets =
|
|
364
|
-
|
|
365
|
-
for (const asset of assets) {
|
|
366
|
-
const convertedAsset = await convert(asset, plugin);
|
|
367
|
-
if (convertedAsset === void 0) convertedAssets.push(asset);
|
|
368
|
-
else if (clone) {
|
|
369
|
-
convertedAssets.push(asset);
|
|
370
|
-
additions.push(convertedAsset);
|
|
371
|
-
} else convertedAssets.push(convertedAsset);
|
|
372
|
-
}
|
|
373
|
-
assets = [...convertedAssets, ...additions];
|
|
440
|
+
const convertedAssets = await applyFontConversion(assets, plugin.name, clone, (asset) => asset.format, (asset, conversion) => convert(asset, plugin, conversion));
|
|
441
|
+
if (convertedAssets !== void 0) assets = convertedAssets;
|
|
374
442
|
}
|
|
375
443
|
return assets;
|
|
376
444
|
}
|
|
377
|
-
async function convert(asset, plugin) {
|
|
378
|
-
if (
|
|
379
|
-
if (
|
|
380
|
-
if (
|
|
381
|
-
if (
|
|
382
|
-
if (
|
|
383
|
-
|
|
445
|
+
async function convert(asset, plugin, conversion) {
|
|
446
|
+
if (conversion.name === "ttf2woff") return converted(asset, conversion.outputFormat, await ttfToWoff(asset.contents, optionsOf(plugin)));
|
|
447
|
+
if (conversion.name === "ttf2woff2") return converted(asset, conversion.outputFormat, await ttfToWoff2(asset.contents, optionsOf(plugin)));
|
|
448
|
+
if (conversion.name === "ttf2eot") return converted(asset, conversion.outputFormat, await ttfToEot(asset.contents, optionsOf(plugin)));
|
|
449
|
+
if (conversion.name === "ttf2svg") return converted(asset, conversion.outputFormat, new TextEncoder().encode(await ttfToSvg(asset.contents, optionsOf(plugin))));
|
|
450
|
+
if (conversion.name === "otf2ttf") return converted(asset, conversion.outputFormat, await otfToTtf(asset.contents, optionsOf(plugin)));
|
|
451
|
+
return converted(asset, conversion.outputFormat, await svgFontToTtf(new TextDecoder().decode(asset.contents), optionsOf(plugin)));
|
|
384
452
|
}
|
|
385
453
|
function optionsOf(plugin) {
|
|
386
454
|
return plugin.options ?? {};
|