UNPKG

typia

Version:

Superfast runtime validators with only one line

213 lines 9.73 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileSystemIdentity = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); var FileSystemIdentity; (function (FileSystemIdentity) { function create(caseSensitive, pathApi = path_1.default) { const fold = (value) => caseSensitive ? value : value.toLowerCase(); const filesystemKey = (file) => fold(pathApi.normalize(file)); return { caseSensitive, contains: (file, directory) => { const fileKey = filesystemKey(pathApi.resolve(file)); const directoryKey = filesystemKey(pathApi.resolve(directory)); const prefix = directoryKey.endsWith(pathApi.sep) ? directoryKey : `${directoryKey}${pathApi.sep}`; return fileKey === directoryKey || fileKey.startsWith(prefix); }, filesystemKey, isDeclarationFile: (file) => DTS_PATTERN.test(fold(pathApi.basename(file))), isSamePath: (x, y) => filesystemKey(x) === filesystemKey(y), isSupportedExtension: (file) => { const filename = fold(pathApi.basename(file)); return (TS_PATTERN.test(filename) && DTS_PATTERN.test(filename) === false); }, projectFileKey: fold, }; } FileSystemIdentity.create = create; class Policy { observe(caseSensitive, location) { if (caseSensitive === undefined) return; if (this.caseSensitive_ === undefined) { this.caseSensitive_ = caseSensitive; this.location_ = location; } else if (this.caseSensitive_ !== caseSensitive) { throw new URIError(`Error on TypiaGenerateWizard.generate(): inconsistent filesystem case behavior between ${this.location_} and ${location}.`); } } get() { if (this.caseSensitive_ === undefined) { throw new URIError("Error on TypiaGenerateWizard.generate(): unable to determine filesystem case behavior for this run."); } return create(this.caseSensitive_); } } FileSystemIdentity.Policy = Policy; function inspectDirectory(directory) { return __awaiter(this, void 0, void 0, function* () { const names = yield fs_1.default.promises.readdir(directory); const entries = new Set(names); for (const name of names) { const alternate = alternateCase(name); if (alternate === undefined) continue; if (entries.has(alternate)) return true; try { yield fs_1.default.promises.lstat(path_1.default.join(directory, alternate)); return false; } catch (error) { if (isMissingFileError(error)) return true; throw error; } } return undefined; }); } FileSystemIdentity.inspectDirectory = inspectDirectory; function probeDirectory(directory) { return __awaiter(this, void 0, void 0, function* () { const inspected = yield inspectDirectory(directory); if (inspected !== undefined) return inspected; for (let attempt = 0; attempt < 10; ++attempt) { const name = `.typia-case-probe-${process.pid}-${Date.now()}-${Math.random() .toString(36) .slice(2)}`.toLowerCase(); const original = path_1.default.join(directory, name); const alternate = path_1.default.join(directory, name.toUpperCase()); let handle; try { handle = yield fs_1.default.promises.open(original, "wx"); } catch (error) { if (isAlreadyExistsError(error)) continue; throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to probe filesystem case behavior at ${directory}: ${formatUnknownError(error)}`); } let closed = false; try { yield handle.close(); closed = true; try { yield fs_1.default.promises.lstat(alternate); return false; } catch (error) { if (isMissingFileError(error)) return true; throw error; } } finally { let cleanupError; if (closed === false) { try { yield handle.close(); } catch (error) { cleanupError = error; } } try { yield fs_1.default.promises.unlink(original); } catch (error) { if (isMissingFileError(error) === false) cleanupError !== null && cleanupError !== void 0 ? cleanupError : (cleanupError = error); } if (cleanupError !== undefined) { throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to clean filesystem case probe ${original}: ${formatUnknownError(cleanupError)}`); } } } throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to reserve a filesystem case probe at ${directory}.`); }); } FileSystemIdentity.probeDirectory = probeDirectory; /** * Builds the key that decides whether two paths are the same filesystem * object. * * The identity is read from `fs.BigIntStats` rather than `fs.Stats`, because * `fs.Stats.ino` is a JavaScript number. An NTFS file ID is `(sequenceNumber * << 48) | mftRecordIndex` and routinely exceeds `Number.MAX_SAFE_INTEGER` — * in a 4000-directory probe, 1879 of them did — where the spacing between * representable doubles is 8 or more. Two entries sharing a sequence number * with MFT record indices within that spacing round to one double. The * traversal callers would then treat a distinct directory or file as already * visited and skip it with no diagnostic, and the overwrite guard would * refuse a legitimate output (samchon/typia#2269). * * A filesystem that reports no inode at all keeps the canonical path * fallback, which is the only identity available there. * * @param stat Stats read with `{ bigint: true }`. * @param realpath Canonical path of the same object. * @returns Key that is equal for two paths only when they are one object. */ function identityKey(stat, realpath) { // `BigInt(0)` rather than `0n`: this package compiles at ES2016, where a // BigInt literal is a syntax error, while the constructor and the `bigint` // type are fine. return stat.ino === BigInt(0) ? `path:${path_1.default.normalize(realpath)}` : `inode:${stat.dev}:${stat.ino}`; } FileSystemIdentity.identityKey = identityKey; function alternateCase(name) { let changed = false; let output = ""; for (const character of name) { if (character >= "a" && character <= "z") { output += character.toUpperCase(); changed = true; } else if (character >= "A" && character <= "Z") { output += character.toLowerCase(); changed = true; } else output += character; } return changed ? output : undefined; } function isAlreadyExistsError(error) { return (typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST"); } function isMissingFileError(error) { return (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"); } function formatUnknownError(error) { return error instanceof Error ? error.message : String(error); } })(FileSystemIdentity || (exports.FileSystemIdentity = FileSystemIdentity = {})); const TS_PATTERN = /\.[cm]?tsx?$/; const DTS_PATTERN = /\.d\.[cm]?tsx?$/; //# sourceMappingURL=FileSystemIdentity.js.map