swagger-auto-generate
Version:
Automatically generate Swagger JSDoc documentation for Express applications
234 lines ⢠9.05 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SwaggerAuto = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const fileScanner_1 = require("./utils/fileScanner");
const astParser_1 = require("./utils/astParser");
const jsdocGenerator_1 = require("./utils/jsdocGenerator");
class SwaggerAuto {
constructor(config) {
this.config = config;
this.fileScanner = new fileScanner_1.FileScanner(config);
}
/**
* Generate Swagger documentation
*/
async generate() {
try {
console.log('š Scanning files...');
const files = await this.fileScanner.scanFiles();
if (files.length === 0) {
console.warn('ā ļø No files found to process');
return;
}
console.log(`š Found ${files.length} files to process`);
const allRoutes = [];
for (const file of files) {
if (this.config.verbose) {
console.log(`š Processing: ${path.relative(process.cwd(), file)}`);
}
try {
const content = await fileScanner_1.FileScanner.readFileContent(file);
const routes = astParser_1.ASTParser.parseFile(content, file);
if (routes.length > 0) {
allRoutes.push(...routes);
if (this.config.verbose) {
console.log(` ā
Found ${routes.length} routes`);
}
}
}
catch (error) {
console.warn(`ā ļø Error processing file ${file}:`, error);
}
}
if (allRoutes.length === 0) {
console.warn('ā ļø No routes found in scanned files');
return;
}
console.log(`š Found ${allRoutes.length} routes total`);
// Generate Swagger specification
const swaggerSpec = jsdocGenerator_1.JSDocGenerator.generateSwaggerSpec(allRoutes, this.config);
// Save to output file
await this.saveOutput(swaggerSpec);
console.log(`ā
Swagger documentation generated successfully!`);
console.log(`š Output saved to: ${this.getOutputFilePath()}`);
}
catch (error) {
console.error('ā Error generating Swagger documentation:', error);
throw error;
}
}
/**
* Get the output file path, always inside 'swagger-docs' unless absolute
*/
getOutputFilePath() {
let outputFile = this.config.outputFile || 'swagger-output.json';
if (!path.isAbsolute(outputFile)) {
// Always write to swagger-docs folder in project root
outputFile = path.join(process.cwd(), 'swagger-docs', path.basename(outputFile));
}
return outputFile;
}
/**
* Save output to file
*/
async saveOutput(swaggerSpec) {
const outputFile = this.getOutputFilePath();
const outputDir = path.dirname(outputFile);
// Create output directory if it doesn't exist
try {
await fs.mkdir(outputDir, { recursive: true });
}
catch (error) {
// Directory might already exist
}
// Save as JSON
await fs.writeFile(outputFile, JSON.stringify(swaggerSpec, null, 2), 'utf-8');
// Also save as YAML if requested
if (outputFile.endsWith('.yaml') || outputFile.endsWith('.yml')) {
const yaml = await this.convertToYaml(swaggerSpec);
await fs.writeFile(outputFile, yaml, 'utf-8');
}
}
/**
* Convert JSON to YAML
*/
async convertToYaml(obj) {
try {
const yaml = await Promise.resolve().then(() => __importStar(require('js-yaml')));
return yaml.dump(obj);
}
catch (error) {
console.warn('ā ļø js-yaml not available, saving as JSON instead');
return JSON.stringify(obj, null, 2);
}
}
/**
* Watch for file changes and regenerate documentation
*/
async watch() {
if (!this.config.watchMode) {
return;
}
console.log('š Watching for file changes...');
const chokidar = require('chokidar');
const watcher = chokidar.watch(this.config.inputFolders, {
ignored: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/.git/**',
'**/coverage/**',
'**/__mocks__/**',
'**/*.test.js',
'**/*.test.ts',
'**/*.spec.js',
'**/*.spec.ts',
],
persistent: true,
});
let debounceTimer;
const regenerate = () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
console.log('š File change detected, regenerating documentation...');
try {
await this.generate();
}
catch (error) {
console.error('ā Error during regeneration:', error);
}
}, 1000); // Debounce for 1 second
};
watcher
.on('add', (path) => {
console.log(`š File added: ${path}`);
regenerate();
})
.on('change', (path) => {
console.log(`š File changed: ${path}`);
regenerate();
})
.on('unlink', (path) => {
console.log(`š File removed: ${path}`);
regenerate();
})
.on('error', (error) => {
console.error('ā Watcher error:', error);
});
// Keep the process running
process.on('SIGINT', () => {
console.log('\nš Stopping file watcher...');
watcher.close();
process.exit(0);
});
}
/**
* Generate JSDoc comments for routes
*/
async generateJSDocComments() {
console.log('š Generating JSDoc comments...');
const files = await this.fileScanner.scanFiles();
const processedFiles = new Set();
for (const file of files) {
try {
const content = await fileScanner_1.FileScanner.readFileContent(file);
const routes = astParser_1.ASTParser.parseFile(content, file);
if (routes.length > 0) {
let updatedContent = content;
let lineOffset = 0;
for (const route of routes) {
const jsdoc = jsdocGenerator_1.JSDocGenerator.generateJSDoc(route);
updatedContent = jsdocGenerator_1.JSDocGenerator.injectJSDoc(updatedContent, route, jsdoc);
lineOffset++;
}
if (updatedContent !== content) {
await fs.writeFile(file, updatedContent, 'utf-8');
processedFiles.add(file);
console.log(`ā
Updated: ${path.relative(process.cwd(), file)}`);
}
}
}
catch (error) {
console.warn(`ā ļø Error processing file ${file}:`, error);
}
}
console.log(`ā
JSDoc comments generated for ${processedFiles.size} files`);
}
}
exports.SwaggerAuto = SwaggerAuto;
//# sourceMappingURL=SwaggerAuto.js.map