Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 58x 1x 57x 2x 11x 11x 11x 11x 11x 9x 11x 12x 11x 10x 13x 10x 13x 13x 9x 9x 11x 11x 11x 10x 10x 8x 8x 12x 8x 2x 2x 2x | import { ensureDir, remove } from "fs-extra";
import glob from "glob";
import { promisify } from "util";
import { writeFileWithBackupAsync } from "lib/helpers/fs/writeFileWithBackup";
import Path from "path";
import { WriteResourcesPatch } from "shared/lib/resources/types";
import promiseLimit from "lib/helpers/promiseLimit";
import { uniq, throttle } from "lodash";
import { pathToPosix } from "shared/lib/helpers/path";
import { encodeResource } from "shared/lib/resources/save";
const CONCURRENT_RESOURCE_SAVE_COUNT = 8;
const globAsync = promisify(glob);
// Normalize paths to ensure consistent comparison and file operations with unicode across different OSes
// Note: Only use on relative paths (never full OS paths)
const normalizeResourcePath = (p: string): string => {
if (Path.isAbsolute(p)) {
throw new Error(
"normalizeResourcePath must only be used with project-relative paths",
);
}
return pathToPosix(p).normalize("NFC");
};
interface SaveProjectDataOptions {
progress?: (completed: number, total: number) => void;
}
const saveProjectData = async (
projectPath: string,
patch: WriteResourcesPatch,
options?: SaveProjectDataOptions,
) => {
const writeBuffer = patch.data;
const metadata = patch.metadata;
const projectFolder = Path.dirname(projectPath);
let completedCount = 0;
const notifyProgress = throttle(() => {
options?.progress?.(completedCount, writeBuffer.length);
}, 50);
const existingResourcePaths = new Set(
(
await globAsync(
Path.join(projectFolder, "{project,assets,plugins}", "**/*.gbsres"),
)
).map((absolutePath) =>
normalizeResourcePath(Path.relative(projectFolder, absolutePath)),
),
);
const expectedResourcePaths: Set<string> = new Set(
patch.paths.map(normalizeResourcePath),
);
const resourceDirPaths = uniq(
writeBuffer.map(({ path }) => normalizeResourcePath(Path.dirname(path))),
);
await promiseLimit(
CONCURRENT_RESOURCE_SAVE_COUNT,
resourceDirPaths.map((relativeDir) => async () => {
await ensureDir(Path.join(projectFolder, relativeDir));
}),
);
notifyProgress();
// Write files using normalized resource paths
await promiseLimit(
CONCURRENT_RESOURCE_SAVE_COUNT,
writeBuffer.map(({ path, data }) => async () => {
const normalizedPath = normalizeResourcePath(path);
await writeFileWithBackupAsync(
Path.join(projectFolder, normalizedPath),
data,
);
completedCount++;
notifyProgress();
}),
);
await writeFileWithBackupAsync(
projectPath,
encodeResource("project", metadata),
);
const resourceDiff = Array.from(existingResourcePaths).filter(
(path) => !expectedResourcePaths.has(path),
);
// Remove previous project files that are no longer needed
for (const relativePath of resourceDiff) {
const removePath = Path.join(projectFolder, relativePath);
await remove(removePath);
}
};
export default saveProjectData;
|