fontmin-rs 0.2.0 → 0.3.0
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/bin/cache-lock.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto'
|
|
2
|
-
import { mkdir, open, readFile, rm, stat } from 'node:fs/promises'
|
|
2
|
+
import { mkdir, open, readFile, readdir, rm, stat } from 'node:fs/promises'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
|
|
5
5
|
const CACHE_LOCK_RETRY_COUNT = 200
|
|
@@ -8,6 +8,7 @@ const CACHE_LOCK_STALE_MS = 5 * 60_000
|
|
|
8
8
|
export async function withCacheLock(cacheRoot, operation) {
|
|
9
9
|
const lockPath = join(cacheRoot, '.write.lock')
|
|
10
10
|
const owner = `${process.pid}:${randomUUID()}`
|
|
11
|
+
let recoveredLock = false
|
|
11
12
|
|
|
12
13
|
await mkdir(cacheRoot, { recursive: true })
|
|
13
14
|
|
|
@@ -24,7 +25,8 @@ export async function withCacheLock(cacheRoot, operation) {
|
|
|
24
25
|
const staleOwner = await readStaleLockOwner(lockPath)
|
|
25
26
|
|
|
26
27
|
if (staleOwner !== undefined) {
|
|
27
|
-
|
|
28
|
+
recoveredLock =
|
|
29
|
+
(await removeLockIfOwned(lockPath, staleOwner)) || recoveredLock
|
|
28
30
|
continue
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -40,6 +42,16 @@ export async function withCacheLock(cacheRoot, operation) {
|
|
|
40
42
|
throw error
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
if (recoveredLock) {
|
|
46
|
+
try {
|
|
47
|
+
await cleanupCacheTemporaryFiles(cacheRoot)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
await lock.close()
|
|
50
|
+
await removeLockIfOwned(lockPath, owner)
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
try {
|
|
44
56
|
return await operation()
|
|
45
57
|
} finally {
|
|
@@ -58,7 +70,10 @@ async function readStaleLockOwner(path) {
|
|
|
58
70
|
stat(path),
|
|
59
71
|
])
|
|
60
72
|
|
|
61
|
-
|
|
73
|
+
const ownerPid = parseOwnerPid(owner)
|
|
74
|
+
|
|
75
|
+
return (ownerPid !== undefined && !isProcessAlive(ownerPid)) ||
|
|
76
|
+
Date.now() - lockStat.mtimeMs > CACHE_LOCK_STALE_MS
|
|
62
77
|
? owner
|
|
63
78
|
: undefined
|
|
64
79
|
} catch (error) {
|
|
@@ -70,6 +85,53 @@ async function readStaleLockOwner(path) {
|
|
|
70
85
|
}
|
|
71
86
|
}
|
|
72
87
|
|
|
88
|
+
async function cleanupCacheTemporaryFiles(cacheRoot) {
|
|
89
|
+
await cleanupTemporaryFilesInTree(cacheRoot)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function cleanupTemporaryFilesInTree(path) {
|
|
93
|
+
let entries
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
entries = await readdir(path, { withFileTypes: true })
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (hasErrorCode(error, 'ENOENT')) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw error
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await Promise.all(
|
|
106
|
+
entries.map(entry => {
|
|
107
|
+
const entryPath = join(path, entry.name)
|
|
108
|
+
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
return cleanupTemporaryFilesInTree(entryPath)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return entry.isFile() && entry.name.endsWith('.tmp')
|
|
114
|
+
? rm(entryPath, { force: true })
|
|
115
|
+
: Promise.resolve()
|
|
116
|
+
}),
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseOwnerPid(owner) {
|
|
121
|
+
const pid = Math.trunc(Number(owner.split(':', 1)[0] ?? ''))
|
|
122
|
+
|
|
123
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isProcessAlive(pid) {
|
|
127
|
+
try {
|
|
128
|
+
process.kill(pid, 0)
|
|
129
|
+
return true
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return !hasErrorCode(error, 'ESRCH')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
73
135
|
async function removeLockIfOwned(path, owner) {
|
|
74
136
|
try {
|
|
75
137
|
if ((await readFile(path, 'utf8')) !== owner) {
|
package/dist/cli.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { _ as woffToTtf, a as inspect, b as findConfig, d as ttfToSvg, f as ttfToWoff, g as woff2ToTtf, n as analyzeCoverage, o as otfToTtf, p as ttfToWoff2, r as eotToTtf, s as subsetTtf, t as optimize, u as ttfToEot, x as loadConfig } from "./optimize-pipeline-
|
|
1
|
+
import { _ as woffToTtf, a as inspect, b as findConfig, d as ttfToSvg, f as ttfToWoff, g as woff2ToTtf, n as analyzeCoverage, o as otfToTtf, p as ttfToWoff2, r as eotToTtf, s as subsetTtf, t as optimize, u as ttfToEot, x as loadConfig } from "./optimize-pipeline-DuJ2ilJf.mjs";
|
|
2
2
|
import { deliverySlices, glyph, svgs2ttf } from "./plugins.mjs";
|
|
3
3
|
import { mkdir, readFile, realpath, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, isAbsolute, parse, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { glob } from "tinyglobby";
|
|
6
6
|
//#region src/cli.mjs
|
|
7
7
|
const INIT_CONFIG_FILE = "fontmin.config.jsonc";
|
|
8
|
-
const FONTMIN_VERSION = "0.
|
|
8
|
+
const FONTMIN_VERSION = "0.3.0";
|
|
9
9
|
const DEFAULT_CACHE_DIR = "node_modules/.cache/fontmin-rs";
|
|
10
10
|
let emitWarnings = true;
|
|
11
11
|
const DEFAULT_INIT_CONFIG = `{
|
package/dist/compat.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as optimize, y as defineConfig } from "./optimize-pipeline-
|
|
1
|
+
import { t as optimize, y as defineConfig } from "./optimize-pipeline-DuJ2ilJf.mjs";
|
|
2
2
|
import { css, deliverySlices, glyph, otf2ttf, svg2ttf, svgs2ttf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2 } from "./plugins.mjs";
|
|
3
3
|
//#region src/compat.ts
|
|
4
4
|
var FontminCompat = class {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as woffToTtf, a as inspect, c as svgFontToTtf, d as ttfToSvg, f as ttfToWoff, g as woff2ToTtf, h as validateWoff2, i as generateFontFaceCss, l as svgsToTtf, m as ttfToWoff2Async, n as analyzeCoverage, o as otfToTtf, p as ttfToWoff2, r as eotToTtf, s as subsetTtf, t as optimize, u as ttfToEot, v as FontminDiagnosticError, x as loadConfig, y as defineConfig } from "./optimize-pipeline-
|
|
1
|
+
import { _ as woffToTtf, a as inspect, c as svgFontToTtf, d as ttfToSvg, f as ttfToWoff, g as woff2ToTtf, h as validateWoff2, i as generateFontFaceCss, l as svgsToTtf, m as ttfToWoff2Async, n as analyzeCoverage, o as otfToTtf, p as ttfToWoff2, r as eotToTtf, s as subsetTtf, t as optimize, u as ttfToEot, v as FontminDiagnosticError, x as loadConfig, y as defineConfig } from "./optimize-pipeline-DuJ2ilJf.mjs";
|
|
2
2
|
import { css, definePlugin, deliverySlices, glyph, modernWeb, otf2ttf, svg2ttf, svgs2ttf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2 } from "./plugins.mjs";
|
|
3
3
|
import FontminCompat from "./compat.mjs";
|
|
4
4
|
import { fontminCompatPreset } from "./presets.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { lstat, mkdir, open, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, extname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { parse as parse$1 } from "jsonc-parser";
|
|
@@ -462,6 +462,7 @@ const CACHE_LOCK_STALE_MS = 5 * 6e4;
|
|
|
462
462
|
async function withCacheLock(cacheRoot, operation) {
|
|
463
463
|
const lockPath = join(cacheRoot, ".write.lock");
|
|
464
464
|
const owner = `${process.pid}:${randomUUID()}`;
|
|
465
|
+
let recoveredLock = false;
|
|
465
466
|
await mkdir(cacheRoot, { recursive: true });
|
|
466
467
|
for (let attempt = 0; attempt < CACHE_LOCK_RETRY_COUNT; attempt += 1) {
|
|
467
468
|
let lock;
|
|
@@ -471,7 +472,7 @@ async function withCacheLock(cacheRoot, operation) {
|
|
|
471
472
|
if (!isNodeErrorWithCode$1(error, "EEXIST")) throw error;
|
|
472
473
|
const staleOwner = await readStaleLockOwner(lockPath);
|
|
473
474
|
if (staleOwner !== void 0) {
|
|
474
|
-
await removeLockIfOwned(lockPath, staleOwner);
|
|
475
|
+
recoveredLock = await removeLockIfOwned(lockPath, staleOwner) || recoveredLock;
|
|
475
476
|
continue;
|
|
476
477
|
}
|
|
477
478
|
await delay(CACHE_LOCK_RETRY_MS);
|
|
@@ -484,6 +485,13 @@ async function withCacheLock(cacheRoot, operation) {
|
|
|
484
485
|
await rm(lockPath, { force: true });
|
|
485
486
|
throw error;
|
|
486
487
|
}
|
|
488
|
+
if (recoveredLock) try {
|
|
489
|
+
await cleanupCacheTemporaryFiles(cacheRoot);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
await lock.close();
|
|
492
|
+
await removeLockIfOwned(lockPath, owner);
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
487
495
|
try {
|
|
488
496
|
return await operation();
|
|
489
497
|
} finally {
|
|
@@ -496,12 +504,42 @@ async function withCacheLock(cacheRoot, operation) {
|
|
|
496
504
|
async function readStaleLockOwner(path) {
|
|
497
505
|
try {
|
|
498
506
|
const [owner, lockStat] = await Promise.all([readFile(path, "utf8"), stat(path)]);
|
|
499
|
-
|
|
507
|
+
const ownerPid = parseOwnerPid(owner);
|
|
508
|
+
return ownerPid !== void 0 && !isProcessAlive(ownerPid) || Date.now() - lockStat.mtimeMs > CACHE_LOCK_STALE_MS ? owner : void 0;
|
|
500
509
|
} catch (error) {
|
|
501
510
|
if (isNodeErrorWithCode$1(error, "ENOENT")) return;
|
|
502
511
|
throw error;
|
|
503
512
|
}
|
|
504
513
|
}
|
|
514
|
+
async function cleanupCacheTemporaryFiles(cacheRoot) {
|
|
515
|
+
await cleanupTemporaryFilesInTree(cacheRoot);
|
|
516
|
+
}
|
|
517
|
+
async function cleanupTemporaryFilesInTree(path) {
|
|
518
|
+
let entries;
|
|
519
|
+
try {
|
|
520
|
+
entries = await readdir(path, { withFileTypes: true });
|
|
521
|
+
} catch (error) {
|
|
522
|
+
if (isNodeErrorWithCode$1(error, "ENOENT")) return;
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
await Promise.all(entries.map((entry) => {
|
|
526
|
+
const entryPath = join(path, entry.name);
|
|
527
|
+
if (entry.isDirectory()) return cleanupTemporaryFilesInTree(entryPath);
|
|
528
|
+
return entry.isFile() && entry.name.endsWith(".tmp") ? rm(entryPath, { force: true }) : Promise.resolve();
|
|
529
|
+
}));
|
|
530
|
+
}
|
|
531
|
+
function parseOwnerPid(owner) {
|
|
532
|
+
const pid = Math.trunc(Number(owner.split(":", 1)[0] ?? ""));
|
|
533
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
534
|
+
}
|
|
535
|
+
function isProcessAlive(pid) {
|
|
536
|
+
try {
|
|
537
|
+
process.kill(pid, 0);
|
|
538
|
+
return true;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
return !isNodeErrorWithCode$1(error, "ESRCH");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
505
543
|
async function removeLockIfOwned(path, owner) {
|
|
506
544
|
try {
|
|
507
545
|
if (await readFile(path, "utf8") !== owner) return false;
|
|
@@ -1101,7 +1139,7 @@ function internalCacheKey(plugin) {
|
|
|
1101
1139
|
//#endregion
|
|
1102
1140
|
//#region src/optimize-storage.ts
|
|
1103
1141
|
const CACHE_SCHEMA_VERSION = "v1";
|
|
1104
|
-
const FONTMIN_VERSION = "0.
|
|
1142
|
+
const FONTMIN_VERSION = "0.3.0";
|
|
1105
1143
|
const DEFAULT_CACHE_DIR = "node_modules/.cache/fontmin-rs";
|
|
1106
1144
|
let temporaryFileCounter = 0;
|
|
1107
1145
|
function createPluginContext(cwd, emittedAssets) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fontmin-rs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Fast font subsetter and converter written in Rust with Node.js bindings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"font",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"jsonc-parser": "^3.3.1",
|
|
60
60
|
"tinyglobby": "^0.2.17",
|
|
61
|
-
"@fontmin-rs/wasm": "0.
|
|
61
|
+
"@fontmin-rs/wasm": "0.3.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@ntnyq/tsconfig": "^3.1.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"vitest": "^4.1.10"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"@fontmin-rs/binding": "0.
|
|
73
|
+
"@fontmin-rs/binding": "0.3.0"
|
|
74
74
|
},
|
|
75
75
|
"engines": {
|
|
76
76
|
"node": ">=22.0.0"
|