UNPKG

hfs

Version:
103 lines (102 loc) 5.08 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DESCRIPT_ION = void 0; exports.usingDescriptIon = usingDescriptIon; exports.getCommentFor = getCommentFor; exports.setCommentFor = setCommentFor; exports.areCommentsEnabled = areCommentsEnabled; const config_1 = require("./config"); const path_1 = require("path"); const cross_1 = require("./cross"); const util_files_1 = require("./util-files"); const misc_1 = require("./misc"); const lodash_1 = __importDefault(require("lodash")); const iconv_lite_1 = __importDefault(require("iconv-lite")); const promises_1 = require("node:fs/promises"); exports.DESCRIPT_ION = 'descript.ion'; const commentsStorage = (0, config_1.defineConfig)(cross_1.CFG.comments_storage, ''); (0, config_1.defineConfig)('descript_ion', true, (v, more) => { var _a; if (!v && ((_a = more.version) === null || _a === void 0 ? void 0 : _a.olderThan('0.57.0-alpha1'))) commentsStorage.set('attr'); }); const descriptIonEncoding = (0, config_1.defineConfig)('descript_ion_encoding', 'utf8'); function readFromDescriptIon(path) { return usingDescriptIon() && readDescriptIon((0, path_1.dirname)(path)).then(x => x.get((0, path_1.basename)(path)), () => undefined); } function usingDescriptIon() { return ['', 'attr+ion'].includes(commentsStorage.get()); } const COMMENT_ATTR = 'comment'; async function getCommentFor(path) { return !path ? undefined : Promise.all([ commentsStorage.get() ? (0, misc_1.loadFileAttr)(path, COMMENT_ATTR) : undefined, readFromDescriptIon(path) ]).then(([fromAttr, fromIon]) => fromAttr || fromIon); } async function setCommentFor(path, comment) { if (commentsStorage.get()) { await (0, misc_1.storeFileAttr)(path, COMMENT_ATTR, comment || undefined); return setCommentDescriptIon(path, ''); } return setCommentDescriptIon(path, comment); // should we also remove from file-attr? not sure, but for the time we won't because #1 storeFileAttr is not really deleting, and we would store a lot of empty attributes, #2 more people will switch from descript.ion to attr (because introduced later) than the opposite } const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(async (jobs) => { const byFolder = lodash_1.default.groupBy(jobs, job => (0, path_1.dirname)(job[0])); return Promise.allSettled(lodash_1.default.map(byFolder, async (jobs, folder) => { const comments = await readDescriptIon(folder).catch(() => new Map()); for (const [path, comment] of jobs) { const file = path.slice(folder.length + 1); if (!comment) comments.delete(file); else comments.set(file, comment); } const path = (0, path_1.join)(folder, exports.DESCRIPT_ION); if (!comments.size) return (0, promises_1.unlink)(path); // encode comments in descript.ion format const ws = await (0, util_files_1.safeWriteStream)(path); comments.forEach((comment, filename) => { const multiline = comment.includes('\n'); const line = (filename.includes(' ') ? `"${filename}"` : filename) + ' ' + (multiline ? comment.replaceAll('\n', '\\n') : comment); ws.write(iconv_lite_1.default.encode(line, descriptIonEncoding.get())); if (multiline) ws.write(MULTILINE_SUFFIX, 'binary'); ws.write('\n'); }); await new Promise(res => ws.end(res)); })); }); function areCommentsEnabled() { return true; // true since we introduced comments in file-attr } const MULTILINE_SUFFIX = Buffer.from([4, 0xC2]); function readDescriptIon(path) { // decoding could also be done with native TextDecoder.decode, but we need iconv for the encoding anyway return (0, util_files_1.parseFileContent)((0, path_1.join)(path, exports.DESCRIPT_ION), raw => { // for simplicity we "remove" the sequence MULTILINE_SUFFIX before iconv.decode messes it up for (let i = 0; i < raw.length; i++) if (raw[i] === MULTILINE_SUFFIX[0] && raw[i + 1] === MULTILINE_SUFFIX[1] && [undefined, 13, 10].includes(raw[i + 2])) raw[i] = raw[i + 1] = 10; const decoded = iconv_lite_1.default.decode(raw, descriptIonEncoding.get()); const ret = new Map(decoded.split('\n').map(line => { const quoted = line[0] === '"' ? 1 : 0; const i = quoted ? line.indexOf('"', 2) + 1 : line.indexOf(' '); const fn = line.slice(quoted, i - quoted); const comment = line.slice(i + 1).replaceAll('\\n', '\n'); return [fn, comment]; })); ret.delete(''); return ret; }); } descriptIonEncoding.sub(() => { for (const k of util_files_1.parseFileCache.keys()) if (k.endsWith(exports.DESCRIPT_ION)) util_files_1.parseFileCache.delete(k); });