@nestia/sdk
Version:
Nestia SDK and Swagger generator
327 lines • 12.2 kB
JavaScript
;
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