@opencraw/studio 0.0.13 → 0.0.15
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/index.esm.js
CHANGED
|
@@ -8,7 +8,7 @@ import { randomBytes, createHash } from 'node:crypto';
|
|
|
8
8
|
import { execFile } from 'node:child_process';
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
10
|
import { load } from 'cheerio';
|
|
11
|
-
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
11
|
+
import { readFile, writeFile, rm, rename, mkdir } from 'node:fs/promises';
|
|
12
12
|
import { describeJson } from '@opencraw/probe';
|
|
13
13
|
import { tmpdir } from 'node:os';
|
|
14
14
|
import { existsSync, createReadStream } from 'node:fs';
|
|
@@ -2877,11 +2877,40 @@ function parseOne(file, content, kind, parse) {
|
|
|
2877
2877
|
* round-trip for a hand-formatted file is future work; see this file's test
|
|
2878
2878
|
* for what is guaranteed today.
|
|
2879
2879
|
*
|
|
2880
|
+
* The text goes to a sibling temporary file that is then renamed over the
|
|
2881
|
+
* recipe, so a reader (or a crash) never sees a half-written file. Windows
|
|
2882
|
+
* refuses a rename over a file another process has open (`EPERM`, `EBUSY`),
|
|
2883
|
+
* so the rename is retried briefly and then gives way to a plain write.
|
|
2884
|
+
*
|
|
2880
2885
|
* @param path - The recipe file to write.
|
|
2881
2886
|
* @param recipe - The whole recipe object.
|
|
2882
2887
|
*/
|
|
2883
2888
|
async function saveRecipe(path, recipe) {
|
|
2884
|
-
|
|
2889
|
+
const temporary = `${path}.${String(process.pid)}.tmp`;
|
|
2890
|
+
const text = `${JSON.stringify(recipe, null, 2)}\n`;
|
|
2891
|
+
await writeFile(temporary, text, 'utf8');
|
|
2892
|
+
try {
|
|
2893
|
+
await renameWithRetry(temporary, path);
|
|
2894
|
+
} catch {
|
|
2895
|
+
await writeFile(path, text, 'utf8');
|
|
2896
|
+
} finally {
|
|
2897
|
+
await rm(temporary, {
|
|
2898
|
+
force: true
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
const RENAME_ATTEMPTS = 5;
|
|
2903
|
+
const RENAME_PAUSE_MS = 20;
|
|
2904
|
+
async function renameWithRetry(from, to) {
|
|
2905
|
+
for (let attempt = 1;; attempt += 1) {
|
|
2906
|
+
try {
|
|
2907
|
+
await rename(from, to);
|
|
2908
|
+
return;
|
|
2909
|
+
} catch (error) {
|
|
2910
|
+
if (attempt >= RENAME_ATTEMPTS) throw error;
|
|
2911
|
+
await new Promise(resolve => setTimeout(resolve, RENAME_PAUSE_MS));
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2885
2914
|
}
|
|
2886
2915
|
|
|
2887
2916
|
/** How many `name-2.ext`, `name-3.ext`… are tried before giving up on a folder full of same-named copies. */
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* round-trip for a hand-formatted file is future work; see this file's test
|
|
10
10
|
* for what is guaranteed today.
|
|
11
11
|
*
|
|
12
|
+
* The text goes to a sibling temporary file that is then renamed over the
|
|
13
|
+
* recipe, so a reader (or a crash) never sees a half-written file. Windows
|
|
14
|
+
* refuses a rename over a file another process has open (`EPERM`, `EBUSY`),
|
|
15
|
+
* so the rename is retried briefly and then gives way to a plain write.
|
|
16
|
+
*
|
|
12
17
|
* @param path - The recipe file to write.
|
|
13
18
|
* @param recipe - The whole recipe object.
|
|
14
19
|
*/
|