UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

327 lines 12.2 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.NestiaSdkWatcher = void 0; const fs_1 = __importDefault(require("fs")); const glob_1 = require("glob"); const path_1 = __importDefault(require("path")); var NestiaSdkWatcher; (function (NestiaSdkWatcher) { NestiaSdkWatcher.watch = (props) => __awaiter(this, void 0, void 0, function* () { const session = new WatchSession(props); session.registerSignals(); yield session.regenerate("initial generation"); yield new Promise(() => { }); }); })(NestiaSdkWatcher || (exports.NestiaSdkWatcher = NestiaSdkWatcher = {})); class WatchSession { constructor(props) { this.props = props; this.watchers = new Map(); this.debounce = null; this.ignored = []; this.pending = null; this.running = false; this.stopped = false; } registerSignals() { const stop = () => { this.dispose(); process.exit(0); }; process.once("SIGINT", stop); process.once("SIGTERM", stop); } regenerate(reason) { return __awaiter(this, void 0, void 0, function* () { if (this.stopped) return; if (this.running) { this.pending = reason; return; } this.running = true; let nextReason = reason; do { const current = nextReason; nextReason = null; this.pending = null; let configurations = []; try { if (current === "initial generation") console.log("Starting nestia swagger watch mode"); else console.log(`Change detected (${current}); regenerating swagger`); configurations = yield this.props.configurations(); // Watchers must be live before artifacts are written: consumers react // to the artifact appearing, and an edit arriving between the write // and a later watcher installation would be lost forever. Changes // during generation queue through `pending`. yield this.refresh(configurations); yield this.props.generate(configurations); yield this.refresh(configurations); console.log(`Watching ${this.watchers.size} directories. Press Ctrl+C to stop.`); } catch (error) { yield this.refresh(configurations); console.error("Nestia swagger watch generation failed:"); printError(error); } nextReason = this.pending; } while (nextReason !== null && !this.stopped); this.running = false; }); } schedule(reason) { if (this.stopped) return; if (this.debounce !== null) clearTimeout(this.debounce); this.debounce = setTimeout(() => { this.debounce = null; void this.regenerate(reason); }, 250); } refresh(configurations) { return __awaiter(this, void 0, void 0, function* () { const next = yield collectWatchPlan({ configurations, configFile: this.props.configFile, projectFile: this.props.projectFile, }); this.ignored = next.ignored; const directories = new Set(); for (const target of next.targets) yield collectDirectories({ directories, ignored: next.ignored, target, }); this.sync(directories); }); } sync(next) { for (const [directory, watcher] of this.watchers) if (next.has(directory) === false) { watcher.close(); this.watchers.delete(directory); } for (const directory of next) { if (this.watchers.has(directory)) continue; try { const watcher = fs_1.default.watch(directory, (event, filename) => { const target = filename === null || filename === undefined ? directory : path_1.default.resolve(directory, String(filename)); if (shouldIgnore(target, this.ignored)) return; if (shouldReact({ event, target }) === false) return; this.schedule(relative(target)); }); watcher.on("error", () => { watcher.close(); this.watchers.delete(directory); this.schedule(relative(directory)); }); this.watchers.set(directory, watcher); } catch (_a) { continue; } } } dispose() { this.stopped = true; if (this.debounce !== null) clearTimeout(this.debounce); for (const watcher of this.watchers.values()) watcher.close(); this.watchers.clear(); } } const collectWatchPlan = (props) => __awaiter(void 0, void 0, void 0, function* () { const targets = new Set([ path_1.default.resolve(props.configFile), path_1.default.resolve(props.projectFile), ]); const ignored = new Set([ path_1.default.resolve("node_modules"), path_1.default.resolve(".git"), ]); for (const config of props.configurations) { for (const target of yield inputTargets(config.input)) targets.add(target); for (const target of outputTargets(config)) ignored.add(target); } return { ignored: [...ignored], targets: [...targets], }; }); const inputTargets = (input) => __awaiter(void 0, void 0, void 0, function* () { if (typeof input === "function") { const fallback = path_1.default.resolve("src"); return [fs_1.default.existsSync(fallback) ? fallback : process.cwd()]; } const patterns = Array.isArray(input) ? input : typeof input === "object" ? input.include : [input]; const output = new Set(); for (const pattern of patterns) { for (const target of yield patternTargets(pattern)) output.add(target); } return [...output]; }); const patternTargets = (pattern) => __awaiter(void 0, void 0, void 0, function* () { const output = new Set(); const direct = path_1.default.resolve(pattern); if (fs_1.default.existsSync(direct)) output.add(direct); if (hasGlobMagic(pattern)) { const root = staticRoot(pattern); if (fs_1.default.existsSync(root)) output.add(root); for (const match of yield (0, glob_1.glob)(pattern)) output.add(path_1.default.resolve(match)); } return [...output]; }); const outputTargets = (config) => { var _a; const output = []; for (const value of [config.output, config.e2e, config.distribute]) if (typeof value === "string") output.push(path_1.default.resolve(value)); if (((_a = config.swagger) === null || _a === void 0 ? void 0 : _a.output) !== undefined) { const parsed = path_1.default.parse(config.swagger.output); output.push(parsed.ext ? path_1.default.resolve(config.swagger.output) : path_1.default.resolve(config.swagger.output, "swagger.json")); } return output; }; const collectDirectories = (props) => __awaiter(void 0, void 0, void 0, function* () { const stats = yield safeStat(props.target); if (stats === null) return; const root = stats.isDirectory() ? props.target : path_1.default.dirname(props.target); yield iterateDirectories({ directories: props.directories, ignored: props.ignored, root, }); }); const iterateDirectories = (props) => __awaiter(void 0, void 0, void 0, function* () { const location = path_1.default.resolve(props.root); if (shouldIgnore(location, props.ignored)) return; props.directories.add(location); let entries; try { entries = yield fs_1.default.promises.readdir(location, { withFileTypes: true }); } catch (_a) { return; } for (const entry of entries) { if (entry.isDirectory() === false) continue; if (SKIPPED_DIRECTORIES.has(entry.name)) continue; yield iterateDirectories({ directories: props.directories, ignored: props.ignored, root: path_1.default.join(location, entry.name), }); } }); const safeStat = (location) => __awaiter(void 0, void 0, void 0, function* () { try { return yield fs_1.default.promises.stat(location); } catch (_a) { return null; } }); const shouldReact = (props) => { const ext = path_1.default.extname(props.target).toLowerCase(); if (SOURCE_EXTENSIONS.has(ext)) return true; return props.event === "rename" && ext.length === 0; }; const shouldIgnore = (location, ignored) => ignored.some((elem) => isSameOrInside(location, elem)); const isSameOrInside = (child, parent) => { const left = comparable(child); const right = comparable(parent); return left === right || left.startsWith(`${right}${path_1.default.sep}`); }; const comparable = (location) => { const resolved = path_1.default.resolve(location); return process.platform === "win32" ? resolved.toLowerCase() : resolved; }; const staticRoot = (pattern) => { const absolute = path_1.default.resolve(pattern); const parsed = path_1.default.parse(absolute); const segments = absolute .slice(parsed.root.length) .split(/[\\/]+/) .filter((segment) => segment.length !== 0); const stable = []; for (const segment of segments) { if (hasGlobMagic(segment)) break; stable.push(segment); } return stable.length === 0 ? parsed.root : path_1.default.join(parsed.root, ...stable); }; const hasGlobMagic = (pattern) => /[*?[\]{}]/.test(pattern); const relative = (location) => { const output = path_1.default.relative(process.cwd(), location); return output.length === 0 ? "." : output.split(path_1.default.sep).join("/"); }; const printError = (error) => { let current = error; let depth = 0; while (current !== undefined && current !== null && depth < 10) { const message = current instanceof Error ? current.message : String(current); console.error(`${" ".repeat(depth)}${depth === 0 ? "" : "caused by: "}${message}`); if (current instanceof Error && current.cause !== undefined) { current = current.cause; ++depth; } else break; } }; const SOURCE_EXTENSIONS = new Set([ ".cts", ".json", ".mts", ".ts", ".tsx", ]); const SKIPPED_DIRECTORIES = new Set([ ".git", ".nestia", "node_modules", ]); //# sourceMappingURL=NestiaSdkWatcher.js.map