tsoa
This commit is contained in:
356
node_modules/@tsoa/cli/dist/cli.js
generated
vendored
Executable file
356
node_modules/@tsoa/cli/dist/cli.js
generated
vendored
Executable file
@@ -0,0 +1,356 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
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;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.validateSpecConfig = void 0;
|
||||
exports.runCLI = runCLI;
|
||||
exports.generateSpecAndRoutes = generateSpecAndRoutes;
|
||||
const yaml_1 = __importDefault(require("yaml"));
|
||||
const yargs_1 = __importDefault(require("yargs"));
|
||||
const helpers_1 = require("yargs/helpers");
|
||||
const metadataGenerator_1 = require("./metadataGeneration/metadataGenerator");
|
||||
const generate_routes_1 = require("./module/generate-routes");
|
||||
const generate_spec_1 = require("./module/generate-spec");
|
||||
const fs_1 = require("./utils/fs");
|
||||
const node_path_1 = require("node:path");
|
||||
const workingDir = process.cwd();
|
||||
let packageJson;
|
||||
const getPackageJsonValue = async (key, defaultValue = '') => {
|
||||
if (!packageJson) {
|
||||
try {
|
||||
const packageJsonRaw = await (0, fs_1.fsReadFile)(`${workingDir}/package.json`);
|
||||
packageJson = JSON.parse(packageJsonRaw.toString('utf8'));
|
||||
}
|
||||
catch (err) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return packageJson[key] || '';
|
||||
};
|
||||
const nameDefault = () => getPackageJsonValue('name', 'TSOA');
|
||||
const versionDefault = () => getPackageJsonValue('version', '1.0.0');
|
||||
const descriptionDefault = () => getPackageJsonValue('description', 'Build swagger-compliant REST APIs using TypeScript and Node');
|
||||
const licenseDefault = () => getPackageJsonValue('license', 'MIT');
|
||||
const determineNoImplicitAdditionalSetting = (noImplicitAdditionalProperties) => {
|
||||
if (noImplicitAdditionalProperties === 'silently-remove-extras' || noImplicitAdditionalProperties === 'throw-on-extras' || noImplicitAdditionalProperties === 'ignore') {
|
||||
return noImplicitAdditionalProperties;
|
||||
}
|
||||
else {
|
||||
return 'ignore';
|
||||
}
|
||||
};
|
||||
const authorInformation = getPackageJsonValue('author', 'unknown');
|
||||
const isYamlExtension = (extension) => extension === '.yaml' || extension === '.yml';
|
||||
const isJsExtension = (extension) => extension === '.js' || extension === '.cjs';
|
||||
const getConfig = async (configPath = 'tsoa.json') => {
|
||||
let config;
|
||||
const ext = (0, node_path_1.extname)(configPath);
|
||||
const configFullPath = (0, node_path_1.isAbsolute)(configPath) ? configPath : `${workingDir}/${configPath}`;
|
||||
try {
|
||||
if (isYamlExtension(ext)) {
|
||||
const configRaw = await (0, fs_1.fsReadFile)(configFullPath);
|
||||
config = yaml_1.default.parse(configRaw.toString('utf8'));
|
||||
}
|
||||
else if (isJsExtension(ext)) {
|
||||
config = await Promise.resolve(`${configFullPath}`).then(s => __importStar(require(s)));
|
||||
}
|
||||
else {
|
||||
const configRaw = await (0, fs_1.fsReadFile)(configFullPath);
|
||||
config = JSON.parse(configRaw.toString('utf8'));
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
console.error(err);
|
||||
throw Error(`Unhandled error encountered loading '${configPath}': ${String(err)}`);
|
||||
}
|
||||
else if ('code' in err && err.code === 'MODULE_NOT_FOUND') {
|
||||
throw Error(`No config file found at '${configPath}'`);
|
||||
}
|
||||
else if (err.name === 'SyntaxError') {
|
||||
console.error(err);
|
||||
const errorType = isJsExtension(ext) ? 'JS' : 'JSON';
|
||||
throw Error(`Invalid ${errorType} syntax in config at '${configPath}': ${err.message}`);
|
||||
}
|
||||
else {
|
||||
console.error(err);
|
||||
throw Error(`Unhandled error encountered loading '${configPath}': ${err.message}`);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
};
|
||||
const resolveConfig = async (config) => {
|
||||
return typeof config === 'object' ? config : getConfig(config);
|
||||
};
|
||||
const validateCompilerOptions = (config) => {
|
||||
return (config || {});
|
||||
};
|
||||
const validateSpecConfig = async (config) => {
|
||||
if (!config.spec) {
|
||||
throw new Error('Missing spec: configuration must contain spec. Spec used to be called swagger in previous versions of tsoa.');
|
||||
}
|
||||
if (!config.spec.outputDirectory) {
|
||||
throw new Error('Missing outputDirectory: configuration must contain output directory.');
|
||||
}
|
||||
if (!config.entryFile && (!config.controllerPathGlobs || !config.controllerPathGlobs.length)) {
|
||||
throw new Error('Missing entryFile and controllerPathGlobs: Configuration must contain an entry point file or controller path globals.');
|
||||
}
|
||||
if (!!config.entryFile && !(await (0, fs_1.fsExists)(config.entryFile))) {
|
||||
throw new Error(`EntryFile not found: ${config.entryFile} - please check your tsoa config.`);
|
||||
}
|
||||
config.spec.version = config.spec.version || (await versionDefault());
|
||||
config.spec.specVersion = config.spec.specVersion || 2;
|
||||
const supportedVersions = [2, 3, 3.1];
|
||||
if (!supportedVersions.includes(config.spec.specVersion)) {
|
||||
throw new Error(`Unsupported Spec version: ${config.spec.specVersion}.`);
|
||||
}
|
||||
if (config.spec.spec && !['immediate', 'recursive', 'deepmerge', undefined].includes(config.spec.specMerging)) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Invalid specMerging config: ${config.spec.specMerging}`);
|
||||
}
|
||||
const noImplicitAdditionalProperties = determineNoImplicitAdditionalSetting(config.noImplicitAdditionalProperties);
|
||||
config.spec.name = config.spec.name || (await nameDefault());
|
||||
config.spec.description = config.spec.description || (await descriptionDefault());
|
||||
config.spec.license = config.spec.license || (await licenseDefault());
|
||||
config.spec.basePath = config.spec.basePath || '/';
|
||||
// defaults to template that may generate non-unique operation ids.
|
||||
// @see https://github.com/lukeautry/tsoa/issues/1005
|
||||
config.spec.operationIdTemplate = config.spec.operationIdTemplate || '{{titleCase method.name}}';
|
||||
if (!config.spec.contact) {
|
||||
config.spec.contact = {};
|
||||
}
|
||||
const author = await authorInformation;
|
||||
if (typeof author === 'string') {
|
||||
const contact = /^([^<(]*)?\s*(?:<([^>(]*)>)?\s*(?:\(([^)]*)\)|$)/m.exec(author);
|
||||
config.spec.contact.name = config.spec.contact.name || contact?.[1];
|
||||
config.spec.contact.email = config.spec.contact.email || contact?.[2];
|
||||
config.spec.contact.url = config.spec.contact.url || contact?.[3];
|
||||
}
|
||||
else if (typeof author === 'object') {
|
||||
config.spec.contact.name = config.spec.contact.name || author?.name;
|
||||
config.spec.contact.email = config.spec.contact.email || author?.email;
|
||||
config.spec.contact.url = config.spec.contact.url || author?.url;
|
||||
}
|
||||
if (!config.defaultNumberType) {
|
||||
config.defaultNumberType = 'double';
|
||||
}
|
||||
if (config.spec.rootSecurity) {
|
||||
if (!Array.isArray(config.spec.rootSecurity)) {
|
||||
throw new Error('spec.rootSecurity must be an array');
|
||||
}
|
||||
if (config.spec.rootSecurity) {
|
||||
const ok = config.spec.rootSecurity.every(security => typeof security === 'object' && security !== null && Object.values(security).every(scope => Array.isArray(scope)));
|
||||
if (!ok) {
|
||||
throw new Error('spec.rootSecurity must be an array of objects whose keys are security scheme names and values are arrays of scopes');
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
...config.spec,
|
||||
noImplicitAdditionalProperties,
|
||||
entryFile: config.entryFile,
|
||||
controllerPathGlobs: config.controllerPathGlobs,
|
||||
};
|
||||
};
|
||||
exports.validateSpecConfig = validateSpecConfig;
|
||||
const validateRoutesConfig = async (config) => {
|
||||
if (!config.entryFile && (!config.controllerPathGlobs || !config.controllerPathGlobs.length)) {
|
||||
throw new Error('Missing entryFile and controllerPathGlobs: Configuration must contain an entry point file or controller path globals.');
|
||||
}
|
||||
if (!!config.entryFile && !(await (0, fs_1.fsExists)(config.entryFile))) {
|
||||
throw new Error(`EntryFile not found: ${config.entryFile} - Please check your tsoa config.`);
|
||||
}
|
||||
if (!config.routes.routesDir) {
|
||||
throw new Error('Missing routesDir: Configuration must contain a routes file output directory.');
|
||||
}
|
||||
if (config.routes.authenticationModule && !((await (0, fs_1.fsExists)(config.routes.authenticationModule)) || (await (0, fs_1.fsExists)(config.routes.authenticationModule + '.ts')))) {
|
||||
throw new Error(`No authenticationModule file found at '${config.routes.authenticationModule}'`);
|
||||
}
|
||||
if (config.routes.iocModule && !((await (0, fs_1.fsExists)(config.routes.iocModule)) || (await (0, fs_1.fsExists)(config.routes.iocModule + '.ts')))) {
|
||||
throw new Error(`No iocModule file found at '${config.routes.iocModule}'`);
|
||||
}
|
||||
const noImplicitAdditionalProperties = determineNoImplicitAdditionalSetting(config.noImplicitAdditionalProperties);
|
||||
const bodyCoercion = config.routes.bodyCoercion ?? true;
|
||||
config.routes.basePath = config.routes.basePath || '/';
|
||||
return {
|
||||
...config.routes,
|
||||
entryFile: config.entryFile,
|
||||
noImplicitAdditionalProperties,
|
||||
bodyCoercion,
|
||||
controllerPathGlobs: config.controllerPathGlobs,
|
||||
multerOpts: config.multerOpts,
|
||||
rootSecurity: config.spec.rootSecurity,
|
||||
};
|
||||
};
|
||||
const configurationArgs = {
|
||||
alias: 'c',
|
||||
describe: 'tsoa configuration file; default is tsoa.json in the working directory',
|
||||
required: false,
|
||||
string: true,
|
||||
};
|
||||
const hostArgs = {
|
||||
describe: 'API host',
|
||||
required: false,
|
||||
string: true,
|
||||
};
|
||||
const basePathArgs = {
|
||||
describe: 'Base API path',
|
||||
required: false,
|
||||
string: true,
|
||||
};
|
||||
const yarmlArgs = {
|
||||
describe: 'Swagger spec yaml format',
|
||||
required: false,
|
||||
boolean: true,
|
||||
};
|
||||
const jsonArgs = {
|
||||
describe: 'Swagger spec json format',
|
||||
required: false,
|
||||
boolean: true,
|
||||
};
|
||||
function runCLI() {
|
||||
return (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
||||
.usage('Usage: $0 <command> [options]')
|
||||
.command('spec', 'Generate OpenAPI spec', {
|
||||
basePath: basePathArgs,
|
||||
configuration: configurationArgs,
|
||||
host: hostArgs,
|
||||
json: jsonArgs,
|
||||
yaml: yarmlArgs,
|
||||
}, args => SpecGenerator(args))
|
||||
.command('routes', 'Generate routes', {
|
||||
basePath: basePathArgs,
|
||||
configuration: configurationArgs,
|
||||
}, args => routeGenerator(args))
|
||||
.command('spec-and-routes', 'Generate OpenAPI spec and routes', {
|
||||
basePath: basePathArgs,
|
||||
configuration: configurationArgs,
|
||||
host: hostArgs,
|
||||
json: jsonArgs,
|
||||
yaml: yarmlArgs,
|
||||
}, args => void generateSpecAndRoutes(args))
|
||||
.demandCommand(1, 1, 'Must provide a valid command.')
|
||||
.help('help')
|
||||
.alias('help', 'h')
|
||||
.parse();
|
||||
}
|
||||
if (require.main === module) {
|
||||
void (async () => {
|
||||
try {
|
||||
await runCLI();
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('tsoa cli error:\n', err);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
}
|
||||
async function SpecGenerator(args) {
|
||||
try {
|
||||
const config = await resolveConfig(args.configuration);
|
||||
if (args.basePath) {
|
||||
config.spec.basePath = args.basePath;
|
||||
}
|
||||
if (args.host) {
|
||||
config.spec.host = args.host;
|
||||
}
|
||||
if (args.yaml) {
|
||||
config.spec.yaml = args.yaml;
|
||||
}
|
||||
if (args.json) {
|
||||
config.spec.yaml = false;
|
||||
}
|
||||
const compilerOptions = validateCompilerOptions(config.compilerOptions);
|
||||
const swaggerConfig = await (0, exports.validateSpecConfig)(config);
|
||||
await (0, generate_spec_1.generateSpec)(swaggerConfig, compilerOptions, config.ignore);
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Generate swagger error.\n', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
async function routeGenerator(args) {
|
||||
try {
|
||||
const config = await resolveConfig(args.configuration);
|
||||
if (args.basePath) {
|
||||
config.routes.basePath = args.basePath;
|
||||
}
|
||||
const compilerOptions = validateCompilerOptions(config.compilerOptions);
|
||||
const routesConfig = await validateRoutesConfig(config);
|
||||
await (0, generate_routes_1.generateRoutes)(routesConfig, compilerOptions, config.ignore);
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Generate routes error.\n', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
async function generateSpecAndRoutes(args, metadata) {
|
||||
try {
|
||||
const config = await resolveConfig(args.configuration);
|
||||
if (args.basePath) {
|
||||
config.spec.basePath = args.basePath;
|
||||
}
|
||||
if (args.host) {
|
||||
config.spec.host = args.host;
|
||||
}
|
||||
if (args.yaml) {
|
||||
config.spec.yaml = args.yaml;
|
||||
}
|
||||
if (args.json) {
|
||||
config.spec.yaml = false;
|
||||
}
|
||||
const compilerOptions = validateCompilerOptions(config.compilerOptions);
|
||||
const routesConfig = await validateRoutesConfig(config);
|
||||
const swaggerConfig = await (0, exports.validateSpecConfig)(config);
|
||||
if (!metadata) {
|
||||
metadata = new metadataGenerator_1.MetadataGenerator(config.entryFile, compilerOptions, config.ignore, config.controllerPathGlobs, config.spec.rootSecurity, config.defaultNumberType, config.routes.esm).Generate();
|
||||
}
|
||||
await Promise.all([(0, generate_routes_1.generateRoutes)(routesConfig, compilerOptions, config.ignore, metadata), (0, generate_spec_1.generateSpec)(swaggerConfig, compilerOptions, config.ignore, metadata)]);
|
||||
return metadata;
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Generate routes error.\n', err);
|
||||
process.exit(1);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=cli.js.map
|
||||
Reference in New Issue
Block a user