UNPKG

@idlebox/ignore-edit

Version:

api for modify .*ignore files

235 lines 7.1 kB
import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { readFile, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { arrayUniqueReference } from '@idlebox/common'; export const unscoped = Symbol('unscoped'); const filePath = Symbol('file-path'); const originalContent = Symbol('original-content'); const EMPTYLINE = '@idebox/ignore/space'; export function emptyLine() { const s = Symbol(EMPTYLINE); return s; } export function stringify(data) { let ret = ''; // console.log('stringify:', data); for (const [item, arr] of data) { if (!arr || arr.length === 0) continue; if (item !== unscoped) { ret += `\n### ${item}\n`; } ret += `${arr .map((e) => { return isEmptyLine(e) ? '' : e; }) .join('\n')}\n`; } return `${ret.trim()}\n`; } /** @deprecated */ export function saveFileSync(data, saveAs = data[filePath]) { if (!saveAs) { throw new Error('not opened by loadFile(), use saveAs'); } const result = stringify(data); if (result !== data[originalContent]) { writeFileSync(saveAs, result, 'utf-8'); return true; } return false; } export async function saveFile(data, saveAs = data[filePath]) { if (!saveAs) { throw new Error('not opened by loadFile(), use saveAs'); } const result = stringify(data); if (result !== data[originalContent]) { await writeFile(saveAs, result, 'utf-8'); return true; } return false; } function isEmptyLine(line) { if (typeof line === 'symbol') { if (line.description === EMPTYLINE) { return true; } } return false; } function trimLastEmptyLines(lines) { while (isEmptyLine(lines.at(-1))) { lines.pop(); } } function wrapProxy(instance, content) { const lines = content.split('\n').map((l) => l.trim()); const sections = []; let current = instance[unscoped]; for (const line of lines) { if (line.startsWith('###')) { trimLastEmptyLines(current); current = new WrappedArray(); const section = line.slice(3).trim(); sections.push(section); instance[section] = current; } else if (!line) { if (current.length > 0 && !isEmptyLine(current.at(-1))) { current.push(emptyLine()); } } else { current.push(line); } } return new Proxy(instance, { get(_target, name) { if (typeof name === 'symbol') { if (name === Symbol.iterator) { const list = [unscoped, ...sections]; return function* () { for (const i of list) yield [i, instance[i]]; }; } return instance[name]; } if (!instance[name]) { instance[name] = new WrappedArray(); sections.push(name); } return instance[name]; }, set(_target, name, value) { if (!Array.isArray(value)) { throw new TypeError('invalid value (must be array)'); } if (!(value instanceof WrappedArray)) value = new WrappedArray(...value); if (typeof name === 'symbol') { if (unscoped === name) { instance[unscoped] = value; return true; } throw new Error('do not support symbol index (except "unscoped")'); } instance[unscoped] = value; return true; }, }); } export function parse(content) { const struct = { [unscoped]: new WrappedArray(), [originalContent]: content, }; return wrapProxy(struct, content); } export async function loadFile(file, create = false) { file = resolve(process.cwd(), file); if (create && !existsSync(file)) { await writeFile(file, ''); } const content = await readFile(file, 'utf-8'); const struct = { [unscoped]: new WrappedArray(), [originalContent]: content, [filePath]: file, }; return wrapProxy(struct, content); } /** @deprecated */ export function loadFileSync(file, create = false) { file = resolve(process.cwd(), file); if (create && !existsSync(file)) { writeFileSync(file, ''); } const content = readFileSync(file, 'utf-8'); const struct = { [unscoped]: new WrappedArray(), [originalContent]: content, [filePath]: file, }; return wrapProxy(struct, content); } export class WrappedArray extends Array { push(...items) { if (items.length === 0) { return super.length; } arrayUniqueReference(items); let lastIndex = -1; for (const item of items) { const index = super.indexOf(item); if (index >= 0) { if (index < lastIndex) { super.splice(index, 1); super.splice(lastIndex + 1, 0, item); } else { lastIndex = index; } } else if (lastIndex === -1) { lastIndex = super.push(item) - 1; } else { lastIndex++; super.splice(lastIndex, 0, item); } } return super.length; } unshift(...items) { if (items.length === 0) { return super.length; } arrayUniqueReference(items); let lastIndex = 0; for (const item of items) { const index = super.indexOf(item); if (index >= 0) { if (index < lastIndex) { super.splice(index, 1); super.splice(lastIndex + 1, 0, item); } else { lastIndex = index; } } else { lastIndex++; super.splice(lastIndex, 0, item); } } return super.length; } splice(start, deleteCount, ...items) { const ret = super.splice(start, deleteCount); if (items.length === 0) { return ret; } arrayUniqueReference(items); let lastIndex = start; for (const item of items) { const index = super.indexOf(item); if (index >= 0) { if (index < lastIndex) { super.splice(index, 1); super.splice(lastIndex + 1, 0, item); } else { lastIndex = index; } } else { lastIndex++; super.splice(lastIndex, 0, item); } } return ret; } } //# sourceMappingURL=api.js.map