This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

44
node_modules/@tsoa/cli/dist/cli.d.ts generated vendored Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env node
import { Config, RoutesConfig, SpecConfig, Tsoa } from '@tsoa/runtime';
import { AbstractRouteGenerator } from './routeGeneration/routeGenerator';
export interface ExtendedSpecConfig extends SpecConfig {
entryFile: Config['entryFile'];
noImplicitAdditionalProperties: Exclude<Config['noImplicitAdditionalProperties'], undefined>;
controllerPathGlobs?: Config['controllerPathGlobs'];
}
export declare const validateSpecConfig: (config: Config) => Promise<ExtendedSpecConfig>;
type RouteGeneratorImpl = new (metadata: Tsoa.Metadata, options: ExtendedRoutesConfig) => AbstractRouteGenerator<any>;
export interface ExtendedRoutesConfig extends RoutesConfig {
entryFile: Config['entryFile'];
noImplicitAdditionalProperties: Exclude<Config['noImplicitAdditionalProperties'], undefined>;
bodyCoercion: Exclude<RoutesConfig['bodyCoercion'], undefined>;
controllerPathGlobs?: Config['controllerPathGlobs'];
multerOpts?: Config['multerOpts'];
rootSecurity?: Config['spec']['rootSecurity'];
routeGenerator?: string | RouteGeneratorImpl;
}
export interface ConfigArgs {
basePath?: string;
configuration?: string | Config;
}
export interface SwaggerArgs extends ConfigArgs {
host?: string;
json?: boolean;
yaml?: boolean;
}
export declare function runCLI(): {
[x: string]: unknown;
_: (string | number)[];
$0: string;
} | Promise<{
[x: string]: unknown;
_: (string | number)[];
$0: string;
}>;
export declare function generateSpecAndRoutes(args: SwaggerArgs, metadata?: Tsoa.Metadata): Promise<Tsoa.Metadata>;
export type RouteGeneratorModule<Config extends ExtendedRoutesConfig> = {
default: new (metadata: Tsoa.Metadata, routesConfig: Config) => {
GenerateCustomRoutes: () => Promise<void>;
};
};
export {};

356
node_modules/@tsoa/cli/dist/cli.js generated vendored Executable file
View 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

1
node_modules/@tsoa/cli/dist/cli.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

6
node_modules/@tsoa/cli/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
export * from './module/generate-spec';
export * from './module/generate-routes';
export { AbstractRouteGenerator } from './routeGeneration/routeGenerator';
export { DefaultRouteGenerator } from './routeGeneration/defaultRouteGenerator';
export { Config } from '@tsoa/runtime';
export * from './cli';

25
node_modules/@tsoa/cli/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultRouteGenerator = exports.AbstractRouteGenerator = void 0;
__exportStar(require("./module/generate-spec"), exports);
__exportStar(require("./module/generate-routes"), exports);
var routeGenerator_1 = require("./routeGeneration/routeGenerator");
Object.defineProperty(exports, "AbstractRouteGenerator", { enumerable: true, get: function () { return routeGenerator_1.AbstractRouteGenerator; } });
var defaultRouteGenerator_1 = require("./routeGeneration/defaultRouteGenerator");
Object.defineProperty(exports, "DefaultRouteGenerator", { enumerable: true, get: function () { return defaultRouteGenerator_1.DefaultRouteGenerator; } });
__exportStar(require("./cli"), exports);
//# sourceMappingURL=index.js.map

1
node_modules/@tsoa/cli/dist/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,yDAAuC;AACvC,2DAAyC;AACzC,mEAA0E;AAAjE,wHAAA,sBAAsB,OAAA;AAC/B,iFAAgF;AAAvE,8HAAA,qBAAqB,OAAA;AAE9B,wCAAsB"}

View File

@@ -0,0 +1,24 @@
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
import { type ClassDeclaration } from 'typescript';
export declare class ControllerGenerator {
private readonly node;
private readonly current;
private readonly parentSecurity;
private readonly path?;
private readonly tags?;
private readonly security?;
private readonly isHidden?;
private readonly commonResponses;
private readonly produces?;
constructor(node: ClassDeclaration, current: MetadataGenerator, parentSecurity?: Tsoa.Security[]);
IsValid(): boolean;
Generate(): Tsoa.Controller;
private buildMethods;
private getPath;
private getCommonResponses;
private getTags;
private getSecurity;
private getIsHidden;
private getProduces;
}

View File

@@ -0,0 +1,123 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ControllerGenerator = void 0;
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const exceptions_1 = require("./exceptions");
const methodGenerator_1 = require("./methodGenerator");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
const typescript_1 = require("typescript");
class ControllerGenerator {
constructor(node, current, parentSecurity = []) {
this.node = node;
this.current = current;
this.parentSecurity = parentSecurity;
this.path = this.getPath();
this.tags = this.getTags();
this.security = this.getSecurity();
this.isHidden = this.getIsHidden();
this.commonResponses = this.getCommonResponses();
this.produces = this.getProduces();
}
IsValid() {
return !!this.path || this.path === '';
}
Generate() {
if (!this.node.parent) {
throw new exceptions_1.GenerateMetadataError("Controller node doesn't have a valid parent source file.");
}
if (!this.node.name) {
throw new exceptions_1.GenerateMetadataError("Controller node doesn't have a valid name.");
}
const sourceFile = this.node.parent.getSourceFile();
return {
location: sourceFile.fileName,
methods: this.buildMethods(),
name: this.node.name.text,
path: this.path || '',
produces: this.produces,
};
}
buildMethods() {
return this.node.members
.filter(typescript_1.isMethodDeclaration)
.map(m => new methodGenerator_1.MethodGenerator(m, this.current, this.commonResponses, this.path, this.tags, this.security, this.isHidden))
.filter(generator => generator.IsValid())
.map(generator => generator.Generate());
}
getPath() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Route');
if (!decorators || !decorators.length) {
return;
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Route decorator allowed in '${this.node.name.text}' class.`);
}
const decorator = decorators[0];
const expression = decorator.parent;
const decoratorArgument = expression.arguments[0];
return decoratorArgument ? `${decoratorArgument.text}` : '';
}
getCommonResponses() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Response');
if (!decorators || !decorators.length) {
return [];
}
return decorators.map(decorator => {
const expression = decorator.parent;
const [name, description, example] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
if (!name) {
throw new exceptions_1.GenerateMetadataError(`Controller's responses should have an explicit name.`);
}
return {
description: description || '',
examples: example === undefined ? undefined : [example],
name,
schema: expression.typeArguments && expression.typeArguments.length > 0 && !this.isHidden ? new typeResolver_1.TypeResolver(expression.typeArguments[0], this.current).resolve() : undefined,
headers: (0, headerTypeHelpers_1.getHeaderType)(expression.typeArguments, 1, this.current),
};
});
}
getTags() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Tags');
if (!decorators || !decorators.length) {
return;
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Tags decorator allowed in '${this.node.name.text}' class.`);
}
const decorator = decorators[0];
const expression = decorator.parent;
return expression.arguments.map((a) => a.text);
}
getSecurity() {
const noSecurityDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'NoSecurity');
const securityDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Security');
if (noSecurityDecorators?.length && securityDecorators?.length) {
throw new exceptions_1.GenerateMetadataError(`NoSecurity decorator cannot be used in conjunction with Security decorator in '${this.node.name.text}' class.`);
}
if (noSecurityDecorators?.length) {
return [];
}
if (!securityDecorators || !securityDecorators.length) {
return this.parentSecurity;
}
return securityDecorators.map(d => (0, decoratorUtils_1.getSecurites)(d, this.current.typeChecker));
}
getIsHidden() {
const hiddenDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return false;
}
if (hiddenDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed in '${this.node.name.text}' class.`);
}
return true;
}
getProduces() {
const produces = (0, decoratorUtils_1.getProduces)(this.node, this.current.typeChecker);
return produces.length ? produces : undefined;
}
}
exports.ControllerGenerator = ControllerGenerator;
//# sourceMappingURL=controllerGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
import { Node, TypeNode } from 'typescript';
export declare class GenerateMetadataError extends Error {
constructor(message?: string, node?: Node | TypeNode, onlyCurrent?: boolean);
}
export declare class GenerateMetaDataWarning {
private message;
private node;
private onlyCurrent;
constructor(message: string, node: Node | TypeNode, onlyCurrent?: boolean);
toString(): string;
}
export declare function prettyLocationOfNode(node: Node | TypeNode): string;
export declare function prettyTroubleCause(node: Node | TypeNode, onlyCurrent?: boolean): string;

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenerateMetaDataWarning = exports.GenerateMetadataError = void 0;
exports.prettyLocationOfNode = prettyLocationOfNode;
exports.prettyTroubleCause = prettyTroubleCause;
const path_1 = require("path");
class GenerateMetadataError extends Error {
constructor(message, node, onlyCurrent = false) {
super(message);
if (node) {
this.message = `${message}\n${prettyLocationOfNode(node)}\n${prettyTroubleCause(node, onlyCurrent)}`;
}
}
}
exports.GenerateMetadataError = GenerateMetadataError;
class GenerateMetaDataWarning {
constructor(message, node, onlyCurrent = false) {
this.message = message;
this.node = node;
this.onlyCurrent = onlyCurrent;
}
toString() {
return `Warning: ${this.message}\n${prettyLocationOfNode(this.node)}\n${prettyTroubleCause(this.node, this.onlyCurrent)}`;
}
}
exports.GenerateMetaDataWarning = GenerateMetaDataWarning;
function prettyLocationOfNode(node) {
const sourceFile = node.getSourceFile();
if (sourceFile) {
const token = node.getFirstToken() || node.parent.getFirstToken();
const start = token ? `:${sourceFile.getLineAndCharacterOfPosition(token.getStart()).line + 1}` : '';
const end = token ? `:${sourceFile.getLineAndCharacterOfPosition(token.getEnd()).line + 1}` : '';
const normalizedPath = (0, path_1.normalize)(`${sourceFile.fileName}${start}${end}`);
return `At: ${normalizedPath}.`;
}
else {
return `At unknown position...`;
}
}
function prettyTroubleCause(node, onlyCurrent = false) {
let name;
if (onlyCurrent || !node.parent) {
name = node.pos !== -1 && node.parent ? node.getText() : node.name?.text || '<unknown name>';
}
else {
name = node.parent.pos !== -1 ? node.parent.getText() : node.parent.name?.text || '<unknown name>';
}
return `This was caused by '${name}'`;
}
//# sourceMappingURL=exceptions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../../src/metadataGeneration/exceptions.ts"],"names":[],"mappings":";;;AAwBA,oDAWC;AAED,gDAQC;AA7CD,+BAAiC;AAGjC,MAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAgB,EAAE,IAAsB,EAAE,WAAW,GAAG,KAAK;QACvE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,GAAG,OAAQ,KAAK,oBAAoB,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;QACxG,CAAC;IACH,CAAC;CACF;AAPD,sDAOC;AAED,MAAa,uBAAuB;IAClC,YACU,OAAe,EACf,IAAqB,EACrB,cAAc,KAAK;QAFnB,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAiB;QACrB,gBAAW,GAAX,WAAW,CAAQ;IAC1B,CAAC;IAEJ,QAAQ;QACN,OAAO,YAAY,IAAI,CAAC,OAAO,KAAK,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IAC5H,CAAC;CACF;AAVD,0DAUC;AAED,SAAgB,oBAAoB,CAAC,IAAqB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,6BAA6B,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrG,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,6BAA6B,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,MAAM,cAAc,GAAG,IAAA,gBAAS,EAAC,GAAG,UAAU,CAAC,QAAQ,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC;QACzE,OAAO,OAAO,cAAc,GAAG,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,IAAqB,EAAE,WAAW,GAAG,KAAK;IAC3E,IAAI,IAAY,CAAC;IACjB,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAE,IAAY,CAAC,IAAI,EAAE,IAAI,IAAI,gBAAgB,CAAC;IACxG,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAE,IAAY,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,gBAAgB,CAAC;IAC9G,CAAC;IACD,OAAO,uBAAuB,IAAI,GAAG,CAAC;AACxC,CAAC"}

View File

@@ -0,0 +1,5 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
export declare function getExtensions(decorators: ts.Identifier[], metadataGenerator: MetadataGenerator): Tsoa.Extension[];
export declare function getExtensionsFromJSDocComments(comments: string[]): Tsoa.Extension[];

View File

@@ -0,0 +1,85 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExtensions = getExtensions;
exports.getExtensionsFromJSDocComments = getExtensionsFromJSDocComments;
const ts = __importStar(require("typescript"));
const initializer_value_1 = require("./initializer-value");
const jsonUtils_1 = require("../utils/jsonUtils");
function getExtensions(decorators, metadataGenerator) {
const extensions = decorators.map(extensionDecorator => {
if (!ts.isCallExpression(extensionDecorator.parent)) {
throw new Error('The parent of the @Extension is not a CallExpression. Are you using it in the right place?');
}
const [decoratorKeyArg, decoratorValueArg] = extensionDecorator.parent.arguments;
if (!ts.isStringLiteral(decoratorKeyArg) && !ts.isIdentifier(decoratorKeyArg)) {
throw new Error('The first argument of @Extension must be a string');
}
const attributeKey = ts.isIdentifier(decoratorKeyArg) ? (0, initializer_value_1.getInitializerValue)(decoratorKeyArg, metadataGenerator.typeChecker) : decoratorKeyArg.text;
if (typeof attributeKey !== 'string') {
throw new Error('The first argument of @Extension must be a string');
}
if (!decoratorValueArg) {
throw new Error(`Extension '${attributeKey}' must contain a value`);
}
assertValidExtensionKey(attributeKey);
const attributeValue = (0, initializer_value_1.getInitializerValue)(decoratorValueArg, metadataGenerator.typeChecker);
if (!(0, initializer_value_1.isNonUndefinedInitializerValue)(attributeValue)) {
throw new Error(`Extension '${attributeKey}' cannot have an undefined initializer value`);
}
return { key: attributeKey, value: attributeValue };
});
return extensions;
}
function getExtensionsFromJSDocComments(comments) {
const extensions = [];
comments.forEach(comment => {
const extensionData = (0, jsonUtils_1.safeFromJson)(comment);
if (extensionData) {
const keys = Object.keys(extensionData);
keys.forEach(key => {
assertValidExtensionKey(key);
extensions.push({ key: key, value: extensionData[key] });
});
}
});
return extensions;
}
function assertValidExtensionKey(key) {
if (!key.startsWith('x-')) {
throw new Error('Extensions must begin with "x-" to be valid. Please see the following link for more information: https://swagger.io/docs/specification/openapi-extensions/');
}
}
//# sourceMappingURL=extension.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/metadataGeneration/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,sCAgCC;AAED,wEAcC;AAtDD,+CAAiC;AACjC,2DAA0F;AAG1F,kDAAkD;AAElD,SAAgB,aAAa,CAAC,UAA2B,EAAE,iBAAoC;IAC7F,MAAM,UAAU,GAAqB,UAAU,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE;QACvE,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;QAChH,CAAC;QAED,MAAM,CAAC,eAAe,EAAE,iBAAiB,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC;QAEjF,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAA,uCAAmB,EAAC,eAAe,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC;QAEnJ,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,cAAc,YAAY,wBAAwB,CAAC,CAAC;QACtE,CAAC;QAED,uBAAuB,CAAC,YAAY,CAAC,CAAC;QAEtC,MAAM,cAAc,GAAG,IAAA,uCAAmB,EAAC,iBAAiB,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAC7F,IAAI,CAAC,IAAA,kDAA8B,EAAC,cAAc,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,cAAc,YAAY,8CAA8C,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,8BAA8B,CAAC,QAAkB;IAC/D,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QACzB,MAAM,aAAa,GAAG,IAAA,wBAAY,EAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBACjB,uBAAuB,CAAC,GAAG,CAAC,CAAC;gBAC7B,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,4JAA4J,CAAC,CAAC;IAChL,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,6 @@
import * as ts from 'typescript';
import { Tsoa } from '@tsoa/runtime';
export type InitializerValue = string | number | boolean | undefined | null | InitializerValue[];
export type DefinedInitializerValue = string | number | boolean | null | DefinedInitializerValue[];
export declare function isNonUndefinedInitializerValue(value: InitializerValue): value is DefinedInitializerValue;
export declare function getInitializerValue(initializer?: ts.Expression | ts.ImportSpecifier, typeChecker?: ts.TypeChecker, type?: Tsoa.Type): InitializerValue | DefinedInitializerValue;

View File

@@ -0,0 +1,154 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNonUndefinedInitializerValue = isNonUndefinedInitializerValue;
exports.getInitializerValue = getInitializerValue;
const ts = __importStar(require("typescript"));
const hasInitializer = (node) => Object.prototype.hasOwnProperty.call(node, 'initializer');
const extractInitializer = (decl) => (decl && hasInitializer(decl) && decl.initializer) || undefined;
const extractImportSpecifier = (symbol) => (symbol?.declarations && symbol.declarations.length > 0 && ts.isImportSpecifier(symbol.declarations[0]) && symbol.declarations[0]) || undefined;
const isIterable = (obj) => obj != null && typeof obj[Symbol.iterator] === 'function';
function isNonUndefinedInitializerValue(value) {
if (Array.isArray(value)) {
return value.every(isNonUndefinedInitializerValue);
}
else {
return value !== undefined;
}
}
function getInitializerValue(initializer, typeChecker, type) {
if (!initializer || !typeChecker) {
return;
}
switch (initializer.kind) {
case ts.SyntaxKind.ArrayLiteralExpression: {
const arrayLiteral = initializer;
return arrayLiteral.elements.reduce((acc, element) => {
if (ts.isSpreadElement(element)) {
const spreadValue = getInitializerValue(element.expression, typeChecker);
if (spreadValue && isIterable(spreadValue)) {
return acc.concat(...spreadValue);
}
else {
throw new Error(`${typeof spreadValue} is not iterable`);
}
}
else {
acc.push(getInitializerValue(element, typeChecker));
}
return acc;
}, []);
}
case ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
return initializer.text;
case ts.SyntaxKind.TrueKeyword:
return true;
case ts.SyntaxKind.FalseKeyword:
return false;
case ts.SyntaxKind.PrefixUnaryExpression: {
const prefixUnary = initializer;
switch (prefixUnary.operator) {
case ts.SyntaxKind.PlusToken:
return Number(prefixUnary.operand.text);
case ts.SyntaxKind.MinusToken:
return Number(`-${prefixUnary.operand.text}`);
default:
throw new Error(`Unsupport prefix operator token: ${prefixUnary.operator}`);
}
}
case ts.SyntaxKind.NumberKeyword:
case ts.SyntaxKind.FirstLiteralToken:
return Number(initializer.text);
case ts.SyntaxKind.NewExpression: {
const newExpression = initializer;
const ident = newExpression.expression;
if (ident.text === 'Date') {
let date = new Date();
if (newExpression.arguments) {
const newArguments = newExpression.arguments.filter(args => args.kind !== undefined);
const argsValue = newArguments.map(args => getInitializerValue(args, typeChecker));
if (argsValue.length > 0) {
date = new Date(argsValue);
}
}
const dateString = date.toISOString();
if (type && type.dataType === 'date') {
return dateString.split('T')[0];
}
return dateString;
}
return;
}
case ts.SyntaxKind.NullKeyword:
return null;
case ts.SyntaxKind.ObjectLiteralExpression: {
const objectLiteral = initializer;
const nestedObject = {};
objectLiteral.properties.forEach((p) => {
if (ts.isSpreadAssignment(p)) {
const spreadValue = getInitializerValue(p.expression, typeChecker);
if (spreadValue) {
if (typeof spreadValue === 'object') {
Object.assign(nestedObject, spreadValue);
}
else {
throw new Error(`Spread types may only be created from object types.`);
}
}
}
else {
nestedObject[p.name.text] = getInitializerValue(p.initializer, typeChecker);
}
});
return nestedObject;
}
case ts.SyntaxKind.ImportSpecifier: {
const importSpecifier = initializer;
const importSymbol = typeChecker.getSymbolAtLocation(importSpecifier.name);
if (!importSymbol)
return;
const aliasedSymbol = typeChecker.getAliasedSymbol(importSymbol);
const declarations = aliasedSymbol.getDeclarations();
const declaration = declarations && declarations.length > 0 ? declarations[0] : undefined;
return getInitializerValue(extractInitializer(declaration), typeChecker);
}
default: {
const symbol = typeChecker.getSymbolAtLocation(initializer);
return getInitializerValue(extractInitializer(symbol?.valueDeclaration) || extractImportSpecifier(symbol), typeChecker);
}
}
}
//# sourceMappingURL=initializer-value.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"initializer-value.js","sourceRoot":"","sources":["../../src/metadataGeneration/initializer-value.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,29 @@
import { Config, Tsoa } from '@tsoa/runtime';
import { type ClassDeclaration, type CompilerOptions, type TypeChecker } from 'typescript';
export declare class MetadataGenerator {
private readonly compilerOptions?;
private readonly ignorePaths?;
private readonly rootSecurity;
readonly defaultNumberType: NonNullable<Config['defaultNumberType']>;
readonly controllerNodes: ClassDeclaration[];
readonly typeChecker: TypeChecker;
private readonly program;
private referenceTypeMap;
private modelDefinitionPosMap;
private expressionOrigNameMap;
constructor(entryFile: string, compilerOptions?: CompilerOptions | undefined, ignorePaths?: string[] | undefined, controllers?: string[], rootSecurity?: Tsoa.Security[], defaultNumberType?: NonNullable<Config['defaultNumberType']>, esm?: boolean);
Generate(): Tsoa.Metadata;
private setProgramToDynamicControllersFiles;
private extractNodeFromProgramSourceFiles;
private checkForMethodSignatureDuplicates;
private checkForPathParamSignatureDuplicates;
TypeChecker(): TypeChecker;
AddReferenceType(referenceType: Tsoa.ReferenceType): void;
GetReferenceType(refName: string): Tsoa.ReferenceType;
CheckModelUnicity(refName: string, positions: Array<{
fileName: string;
pos: number;
}>): void;
CheckExpressionUnicity(formattedRefName: string, refName: string): void;
private buildControllers;
}

View File

@@ -0,0 +1,214 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetadataGenerator = void 0;
const minimatch_1 = require("minimatch");
const typescript_1 = require("typescript");
const decoratorUtils_1 = require("../utils/decoratorUtils");
const importClassesFromDirectories_1 = require("../utils/importClassesFromDirectories");
const controllerGenerator_1 = require("./controllerGenerator");
const exceptions_1 = require("./exceptions");
const typeResolver_1 = require("./typeResolver");
class MetadataGenerator {
constructor(entryFile, compilerOptions, ignorePaths, controllers, rootSecurity = [], defaultNumberType = 'double', esm = false) {
this.compilerOptions = compilerOptions;
this.ignorePaths = ignorePaths;
this.rootSecurity = rootSecurity;
this.defaultNumberType = defaultNumberType;
this.controllerNodes = new Array();
this.referenceTypeMap = {};
this.modelDefinitionPosMap = {};
this.expressionOrigNameMap = {};
this.checkForMethodSignatureDuplicates = (controllers) => {
const map = {};
controllers.forEach(controller => {
controller.methods.forEach(method => {
const signature = method.path ? `@${method.method}(${controller.path}/${method.path})` : `@${method.method}(${controller.path})`;
const methodDescription = `${controller.name}#${method.name}`;
if (map[signature]) {
map[signature].push(methodDescription);
}
else {
map[signature] = [methodDescription];
}
});
});
let message = '';
Object.keys(map).forEach(signature => {
const controllers = map[signature];
if (controllers.length > 1) {
message += `Duplicate method signature ${signature} found in controllers: ${controllers.join(', ')}\n`;
}
});
if (message) {
throw new exceptions_1.GenerateMetadataError(message);
}
};
this.checkForPathParamSignatureDuplicates = (controllers) => {
const paramRegExp = new RegExp('{(\\w*)}|:(\\w+)', 'g');
let PathDuplicationType;
(function (PathDuplicationType) {
PathDuplicationType[PathDuplicationType["FULL"] = 0] = "FULL";
PathDuplicationType[PathDuplicationType["PARTIAL"] = 1] = "PARTIAL";
})(PathDuplicationType || (PathDuplicationType = {}));
const collisions = [];
function addCollision(type, method, controller, collidesWith) {
let existingCollision = collisions.find(collision => collision.type === type && collision.method === method && collision.controller === controller);
if (!existingCollision) {
existingCollision = {
type,
method,
controller,
collidesWith: [],
};
collisions.push(existingCollision);
}
existingCollision.collidesWith.push(collidesWith);
}
controllers.forEach(controller => {
const methodRouteGroup = {};
// Group all ts methods with HTTP method decorator into same object in same controller.
controller.methods.forEach(method => {
if (methodRouteGroup[method.method] === undefined) {
methodRouteGroup[method.method] = [];
}
const params = method.path.match(paramRegExp);
methodRouteGroup[method.method].push({
method, // method.name + ": " + method.path) as any,
path: params?.reduce((s, a) => {
// replace all params with {} placeholder for comparison
return s.replace(a, '{}');
}, method.path) || method.path,
});
});
Object.keys(methodRouteGroup).forEach((key) => {
const methodRoutes = methodRouteGroup[key];
// check each route with the routes that are defined before it
for (let i = 0; i < methodRoutes.length; i += 1) {
for (let j = 0; j < i; j += 1) {
if (methodRoutes[i].path === methodRoutes[j].path) {
// full match
addCollision(PathDuplicationType.FULL, methodRoutes[i].method, controller, methodRoutes[j].method);
}
else if (methodRoutes[i].path.split('/').length === methodRoutes[j].path.split('/').length &&
methodRoutes[j].path
.substr(methodRoutes[j].path.lastIndexOf('/')) // compare only the "last" part of the path
.split('/')
.some(v => !!v) && // ensure the comparison path has a value
methodRoutes[i].path.split('/').every((v, index) => {
const comparisonPathPart = methodRoutes[j].path.split('/')[index];
// if no params, compare values
if (!v.includes('{}')) {
return v === comparisonPathPart;
}
// otherwise check if route starts with comparison route
return v.startsWith(methodRoutes[j].path.split('/')[index]);
})) {
// partial match - reorder routes!
addCollision(PathDuplicationType.PARTIAL, methodRoutes[i].method, controller, methodRoutes[j].method);
}
}
}
});
});
// print warnings for each collision (grouped by route)
collisions.forEach(collision => {
let message = '';
if (collision.type === PathDuplicationType.FULL) {
message = `Duplicate path parameter definition signature found in controller `;
}
else if (collision.type === PathDuplicationType.PARTIAL) {
message = `Overlapping path parameter definition signature found in controller `;
}
message += collision.controller.name;
message += ` [ method ${collision.method.method.toUpperCase()} ${collision.method.name} route: ${collision.method.path} ] collides with `;
message += collision.collidesWith
.map((method) => {
return `[ method ${method.method.toUpperCase()} ${method.name} route: ${method.path} ]`;
})
.join(', ');
message += '\n';
console.warn(message);
});
};
typeResolver_1.TypeResolver.clearCache();
this.program = controllers ? this.setProgramToDynamicControllersFiles(controllers, esm) : (0, typescript_1.createProgram)([entryFile], compilerOptions || {});
this.typeChecker = this.program.getTypeChecker();
}
Generate() {
this.extractNodeFromProgramSourceFiles();
const controllers = this.buildControllers();
this.checkForMethodSignatureDuplicates(controllers);
this.checkForPathParamSignatureDuplicates(controllers);
return {
controllers,
referenceTypeMap: this.referenceTypeMap,
};
}
setProgramToDynamicControllersFiles(controllers, esm) {
const allGlobFiles = (0, importClassesFromDirectories_1.importClassesFromDirectories)(controllers, esm ? ['.mts', '.ts', '.cts'] : ['.ts']);
if (allGlobFiles.length === 0) {
throw new exceptions_1.GenerateMetadataError(`[${controllers.join(', ')}] globs found 0 controllers.`);
}
return (0, typescript_1.createProgram)(allGlobFiles, this.compilerOptions || {});
}
extractNodeFromProgramSourceFiles() {
this.program.getSourceFiles().forEach(sf => {
if (this.ignorePaths && this.ignorePaths.length) {
for (const path of this.ignorePaths) {
if ((0, minimatch_1.minimatch)(sf.fileName, path)) {
return;
}
}
}
(0, typescript_1.forEachChild)(sf, node => {
if ((0, typescript_1.isClassDeclaration)(node) && (0, decoratorUtils_1.getDecorators)(node, identifier => identifier.text === 'Route').length) {
this.controllerNodes.push(node);
}
});
});
}
TypeChecker() {
return this.typeChecker;
}
AddReferenceType(referenceType) {
if (!referenceType.refName) {
throw new Error('no reference type name found');
}
this.referenceTypeMap[referenceType.refName] = referenceType;
}
GetReferenceType(refName) {
return this.referenceTypeMap[refName];
}
CheckModelUnicity(refName, positions) {
if (!this.modelDefinitionPosMap[refName]) {
this.modelDefinitionPosMap[refName] = positions;
}
else {
const origPositions = this.modelDefinitionPosMap[refName];
if (!(origPositions.length === positions.length && positions.every(pos => origPositions.find(origPos => pos.pos === origPos.pos && pos.fileName === origPos.fileName)))) {
throw new Error(`Found 2 different model definitions for model ${refName}: orig: ${JSON.stringify(origPositions)}, act: ${JSON.stringify(positions)}`);
}
}
}
CheckExpressionUnicity(formattedRefName, refName) {
if (!this.expressionOrigNameMap[formattedRefName]) {
this.expressionOrigNameMap[formattedRefName] = refName;
}
else {
if (this.expressionOrigNameMap[formattedRefName] !== refName) {
throw new Error(`Found 2 different type expressions for formatted name "${formattedRefName}": orig: "${this.expressionOrigNameMap[formattedRefName]}", act: "${refName}"`);
}
}
}
buildControllers() {
if (this.controllerNodes.length === 0) {
throw new Error('no controllers found, check tsoa configuration');
}
return this.controllerNodes
.map(classDeclaration => new controllerGenerator_1.ControllerGenerator(classDeclaration, this, this.rootSecurity))
.filter(generator => generator.IsValid())
.map(generator => generator.Generate());
}
}
exports.MetadataGenerator = MetadataGenerator;
//# sourceMappingURL=metadataGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
type HttpMethod = 'options' | 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head';
export declare class MethodGenerator {
private readonly node;
private readonly current;
private readonly commonResponses;
private readonly parentPath?;
private readonly parentTags?;
private readonly parentSecurity?;
private readonly isParentHidden?;
protected method?: HttpMethod;
protected path?: string;
private produces?;
private consumes?;
constructor(node: ts.MethodDeclaration, current: MetadataGenerator, commonResponses: Tsoa.Response[], parentPath?: string | undefined, parentTags?: string[] | undefined, parentSecurity?: Tsoa.Security[] | undefined, isParentHidden?: boolean | undefined);
IsValid(): this is {
method: HttpMethod;
path: string;
};
Generate(): Tsoa.Method;
private buildParameters;
private validateBodyParameters;
private validateQueryParameters;
private getExtensions;
private getCurrentLocation;
private processMethodDecorators;
private getProduces;
private getConsumes;
private getMethodResponses;
private getMethodSuccessResponse;
private getHeadersFromDecorator;
private getSchemaFromDecorator;
private getMethodSuccessExamples;
private supportsPathMethod;
private getIsDeprecated;
private getOperationId;
private getTags;
private getSecurity;
private getIsHidden;
private getDecoratorsByIdentifier;
private getProducesAdapter;
}
export {};

View File

@@ -0,0 +1,356 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.MethodGenerator = void 0;
const ts = __importStar(require("typescript"));
const path = __importStar(require("path"));
const isVoidType_1 = require("../utils/isVoidType");
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const jsDocUtils_1 = require("./../utils/jsDocUtils");
const extension_1 = require("./extension");
const exceptions_1 = require("./exceptions");
const parameterGenerator_1 = require("./parameterGenerator");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
class MethodGenerator {
constructor(node, current, commonResponses, parentPath, parentTags, parentSecurity, isParentHidden) {
this.node = node;
this.current = current;
this.commonResponses = commonResponses;
this.parentPath = parentPath;
this.parentTags = parentTags;
this.parentSecurity = parentSecurity;
this.isParentHidden = isParentHidden;
this.processMethodDecorators();
}
IsValid() {
return this.method !== undefined && this.path !== undefined;
}
Generate() {
if (!this.IsValid()) {
throw new exceptions_1.GenerateMetadataError("This isn't a valid a controller method.");
}
let nodeType = this.node.type;
if (!nodeType) {
const typeChecker = this.current.typeChecker;
const signature = typeChecker.getSignatureFromDeclaration(this.node);
const implicitType = typeChecker.getReturnTypeOfSignature(signature);
nodeType = typeChecker.typeToTypeNode(implicitType, undefined, ts.NodeBuilderFlags.NoTruncation);
}
const type = new typeResolver_1.TypeResolver(nodeType, this.current).resolve();
const responses = this.commonResponses.concat(this.getMethodResponses());
const { response: successResponse, status: successStatus } = this.getMethodSuccessResponse(type);
responses.push(successResponse);
const parameters = this.buildParameters();
const additionalResponses = parameters.filter((p) => p.in === 'res');
responses.push(...additionalResponses);
return {
extensions: this.getExtensions(),
deprecated: this.getIsDeprecated(),
description: (0, jsDocUtils_1.getJSDocDescription)(this.node),
isHidden: this.getIsHidden(),
method: this.method,
name: this.node.name.text,
operationId: this.getOperationId(),
parameters,
path: this.path,
produces: this.produces,
consumes: this.consumes,
responses,
successStatus,
security: this.getSecurity(),
summary: (0, jsDocUtils_1.getJSDocComment)(this.node, 'summary'),
tags: this.getTags(),
type,
};
}
buildParameters() {
if (!this.IsValid()) {
throw new exceptions_1.GenerateMetadataError("This isn't a valid a controller method.");
}
const fullPath = path.join(this.parentPath || '', this.path);
const method = this.method;
const parameters = this.node.parameters
.map(p => {
try {
return new parameterGenerator_1.ParameterGenerator(p, method, fullPath, this.current).Generate();
}
catch (e) {
const methodId = this.node.name;
const controllerId = this.node.parent.name;
const message = e instanceof Error ? e.message : String(e);
throw new exceptions_1.GenerateMetadataError(`${message} \n in '${controllerId.text}.${methodId.text}'`);
}
})
.reduce((flattened, params) => [...flattened, ...params], []);
this.validateBodyParameters(parameters);
this.validateQueryParameters(parameters);
return parameters;
}
validateBodyParameters(parameters) {
const bodyParameters = parameters.filter(p => p.in === 'body');
const bodyProps = parameters.filter(p => p.in === 'body-prop');
const hasFormDataParameters = parameters.some(p => p.in === 'formData');
const hasBodyParameter = bodyProps.length + bodyParameters.length > 0;
if (bodyParameters.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one body parameter allowed in '${this.getCurrentLocation()}' method.`);
}
if (bodyParameters.length > 0 && bodyProps.length > 0) {
throw new exceptions_1.GenerateMetadataError(`Choose either during @Body or @BodyProp in '${this.getCurrentLocation()}' method.`);
}
if (hasBodyParameter && hasFormDataParameters) {
throw new Error(`@Body or @BodyProp cannot be used with @FormField, @UploadedFile, or @UploadedFiles in '${this.getCurrentLocation()}' method.`);
}
}
validateQueryParameters(parameters) {
const queryParameters = parameters.filter(p => p.in === 'query');
const queriesParameters = parameters.filter(p => p.in === 'queries');
if (queriesParameters.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one queries parameter allowed in '${this.getCurrentLocation()}' method.`);
}
if (queriesParameters.length > 0 && queryParameters.length > 0) {
throw new exceptions_1.GenerateMetadataError(`Choose either during @Query or @Queries in '${this.getCurrentLocation()}' method.`);
}
}
getExtensions() {
const extensionDecorators = this.getDecoratorsByIdentifier(this.node, 'Extension');
if (!extensionDecorators || !extensionDecorators.length) {
return [];
}
return (0, extension_1.getExtensions)(extensionDecorators, this.current);
}
getCurrentLocation() {
const methodId = this.node.name;
const controllerId = this.node.parent.name;
return `${controllerId.text}.${methodId.text}`;
}
processMethodDecorators() {
const pathDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => this.supportsPathMethod(identifier.text));
if (!pathDecorators || !pathDecorators.length) {
return;
}
if (pathDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one path decorator in '${this.getCurrentLocation()}' method, Found: ${pathDecorators.map(d => d.text).join(', ')}`);
}
const decorator = pathDecorators[0];
this.method = decorator.text.toLowerCase();
// if you don't pass in a path to the method decorator, we'll just use the base route
// todo: what if someone has multiple no argument methods of the same type in a single controller?
// we need to throw an error there
this.path = (0, decoratorUtils_1.getPath)(decorator, this.current.typeChecker);
this.produces = this.getProduces();
this.consumes = this.getConsumes();
}
getProduces() {
const produces = (0, decoratorUtils_1.getProduces)(this.node, this.current.typeChecker);
return produces.length ? produces : undefined;
}
getConsumes() {
const consumesDecorators = this.getDecoratorsByIdentifier(this.node, 'Consumes');
if (!consumesDecorators || !consumesDecorators.length) {
return;
}
if (consumesDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Consumes decorator in '${this.getCurrentLocation()}' method, Found: ${consumesDecorators.map(d => d.text).join(', ')}`);
}
const [decorator] = consumesDecorators;
const [consumes] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
return consumes;
}
getMethodResponses() {
const responseExamplesByName = {};
const decorators = this.getDecoratorsByIdentifier(this.node, 'Response');
if (!decorators || !decorators.length) {
return [];
}
return decorators.map(decorator => {
const [name, description, example, produces] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
if (example !== undefined) {
responseExamplesByName[name] = responseExamplesByName[name] ? [...responseExamplesByName[name], example] : [example];
}
return {
description: description || '',
examples: responseExamplesByName[name] || undefined,
name: name || '200',
produces: this.getProducesAdapter(produces),
schema: this.getSchemaFromDecorator(decorator, 0),
headers: this.getHeadersFromDecorator(decorator, 1),
};
});
}
getMethodSuccessResponse(type) {
const decorators = this.getDecoratorsByIdentifier(this.node, 'SuccessResponse');
const examplesWithLabels = this.getMethodSuccessExamples();
if (!decorators || !decorators.length) {
const returnsDescription = (0, jsDocUtils_1.getJSDocComment)(this.node, 'returns') || 'Ok';
return {
response: {
description: (0, isVoidType_1.isVoidType)(type) ? 'No content' : returnsDescription,
examples: examplesWithLabels?.map(ex => ex.example),
exampleLabels: examplesWithLabels?.map(ex => ex.label),
name: (0, isVoidType_1.isVoidType)(type) ? '204' : '200',
produces: this.produces,
schema: type,
},
};
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one SuccessResponse decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const [firstDecorator] = decorators;
const [name, description, produces] = (0, decoratorUtils_1.getDecoratorValues)(firstDecorator, this.current.typeChecker);
const headers = this.getHeadersFromDecorator(firstDecorator, 0);
return {
response: {
description: description || '',
examples: examplesWithLabels?.map(ex => ex.example),
exampleLabels: examplesWithLabels?.map(ex => ex.label),
name: name || '200',
produces: this.getProducesAdapter(produces),
schema: type,
headers,
},
status: name && /^\d+$/.test(name) ? parseInt(name, 10) : undefined,
};
}
getHeadersFromDecorator({ parent: expression }, headersIndex) {
if (!ts.isCallExpression(expression)) {
return undefined;
}
return (0, headerTypeHelpers_1.getHeaderType)(expression.typeArguments, headersIndex, this.current);
}
getSchemaFromDecorator({ parent: expression }, schemaIndex) {
if (!ts.isCallExpression(expression) || !expression.typeArguments?.length) {
return undefined;
}
return new typeResolver_1.TypeResolver(expression.typeArguments[schemaIndex], this.current).resolve();
}
getMethodSuccessExamples() {
const exampleDecorators = this.getDecoratorsByIdentifier(this.node, 'Example');
if (!exampleDecorators || !exampleDecorators.length) {
return undefined;
}
const examples = exampleDecorators.map(exampleDecorator => {
const [example, label] = (0, decoratorUtils_1.getDecoratorValues)(exampleDecorator, this.current.typeChecker);
return { example, label };
});
return examples || undefined;
}
supportsPathMethod(method) {
return ['options', 'get', 'post', 'put', 'patch', 'delete', 'head'].some(m => m === method.toLowerCase());
}
getIsDeprecated() {
if ((0, jsDocUtils_1.isExistJSDocTag)(this.node, tag => tag.tagName.text === 'deprecated')) {
return true;
}
const depDecorators = this.getDecoratorsByIdentifier(this.node, 'Deprecated');
if (!depDecorators || !depDecorators.length) {
return false;
}
if (depDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Deprecated decorator allowed in '${this.getCurrentLocation()}' method.`);
}
return true;
}
getOperationId() {
const opDecorators = this.getDecoratorsByIdentifier(this.node, 'OperationId');
if (!opDecorators || !opDecorators.length) {
return undefined;
}
if (opDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one OperationId decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const values = (0, decoratorUtils_1.getDecoratorValues)(opDecorators[0], this.current.typeChecker);
return values && values[0];
}
getTags() {
const tagsDecorators = this.getDecoratorsByIdentifier(this.node, 'Tags');
if (!tagsDecorators || !tagsDecorators.length) {
return this.parentTags;
}
if (tagsDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Tags decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const tags = (0, decoratorUtils_1.getDecoratorValues)(tagsDecorators[0], this.current.typeChecker);
if (tags && this.parentTags) {
tags.push(...this.parentTags);
}
return tags;
}
getSecurity() {
const noSecurityDecorators = this.getDecoratorsByIdentifier(this.node, 'NoSecurity');
const securityDecorators = this.getDecoratorsByIdentifier(this.node, 'Security');
if (noSecurityDecorators?.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one NoSecurity decorator allowed in '${this.getCurrentLocation()}' method.`);
}
if (noSecurityDecorators?.length && securityDecorators?.length) {
throw new exceptions_1.GenerateMetadataError(`NoSecurity decorator cannot be used in conjunction with Security decorator in '${this.getCurrentLocation()}' method.`);
}
if (noSecurityDecorators?.length) {
return [];
}
if (!securityDecorators || !securityDecorators.length) {
return this.parentSecurity || [];
}
return securityDecorators.map(d => (0, decoratorUtils_1.getSecurites)(d, this.current.typeChecker));
}
getIsHidden() {
const hiddenDecorators = this.getDecoratorsByIdentifier(this.node, 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return !!this.isParentHidden;
}
if (this.isParentHidden) {
throw new exceptions_1.GenerateMetadataError(`Hidden decorator cannot be set on '${this.getCurrentLocation()}' it is already defined on the controller`);
}
if (hiddenDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed in '${this.getCurrentLocation()}' method.`);
}
return true;
}
getDecoratorsByIdentifier(node, id) {
return (0, decoratorUtils_1.getDecorators)(node, identifier => identifier.text === id);
}
getProducesAdapter(produces) {
if (Array.isArray(produces)) {
return produces;
}
else if (typeof produces === 'string') {
return [produces];
}
return;
}
}
exports.MethodGenerator = MethodGenerator;
//# sourceMappingURL=methodGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
export declare class ParameterGenerator {
private readonly parameter;
private readonly method;
private readonly path;
private readonly current;
constructor(parameter: ts.ParameterDeclaration, method: string, path: string, current: MetadataGenerator);
Generate(): Tsoa.Parameter[];
private getRequestParameter;
private getRequestPropParameter;
private extractTsoaResponse;
private getResParameters;
private getProducesFromResHeaders;
private getBodyPropParameter;
private getBodyParameter;
private getHeaderParameter;
private getUploadedFileParameter;
private getFormFieldParameter;
private getQueriesParameters;
private validateQueriesProperties;
private getQueryParameters;
private getPathParameter;
private getParameterDescription;
private getParameterDeprecation;
private getParameterExample;
private supportBodyMethod;
private supportParameterDecorator;
private supportPathDataType;
private getValidatedType;
private getQueryParameterIsHidden;
}

View File

@@ -0,0 +1,548 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParameterGenerator = void 0;
const ts = __importStar(require("typescript"));
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const jsDocUtils_1 = require("./../utils/jsDocUtils");
const validatorUtils_1 = require("./../utils/validatorUtils");
const exceptions_1 = require("./exceptions");
const initializer_value_1 = require("./initializer-value");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
class ParameterGenerator {
constructor(parameter, method, path, current) {
this.parameter = parameter;
this.method = method;
this.path = path;
this.current = current;
}
Generate() {
const decoratorName = (0, decoratorUtils_1.getNodeFirstDecoratorName)(this.parameter, identifier => this.supportParameterDecorator(identifier.text));
switch (decoratorName) {
case 'Request':
return [this.getRequestParameter(this.parameter)];
case 'RequestProp':
return [this.getRequestPropParameter(this.parameter)];
case 'Body':
return [this.getBodyParameter(this.parameter)];
case 'BodyProp':
return [this.getBodyPropParameter(this.parameter)];
case 'FormField':
return [this.getFormFieldParameter(this.parameter)];
case 'Header':
return [this.getHeaderParameter(this.parameter)];
case 'Query':
return this.getQueryParameters(this.parameter);
case 'Queries':
return [this.getQueriesParameters(this.parameter)];
case 'Path':
return [this.getPathParameter(this.parameter)];
case 'Res':
return this.getResParameters(this.parameter);
case 'Inject':
return [];
case 'UploadedFile':
return [this.getUploadedFileParameter(this.parameter)];
case 'UploadedFiles':
return [this.getUploadedFileParameter(this.parameter, true)];
default:
return [this.getPathParameter(this.parameter)];
}
}
getRequestParameter(parameter) {
const parameterName = parameter.name.text;
return {
description: this.getParameterDescription(parameter),
in: 'request',
name: parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type: { dataType: 'object' },
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getRequestPropParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'request-prop',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'ParameterProp') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
extractTsoaResponse(typeNode) {
if (!typeNode || !ts.isTypeReferenceNode(typeNode)) {
return undefined;
}
if (typeNode.typeName.getText() === 'TsoaResponse') {
return typeNode;
}
const symbol = this.current.typeChecker.getTypeAtLocation(typeNode).aliasSymbol;
if (!symbol || !symbol.declarations) {
return undefined;
}
const declaration = symbol.declarations[0];
if (!ts.isTypeAliasDeclaration(declaration) || !ts.isTypeReferenceNode(declaration.type)) {
return undefined;
}
return declaration.type.typeName.getText() === 'TsoaResponse' ? declaration.type : undefined;
}
getResParameters(parameter) {
const parameterName = parameter.name.text;
const decorator = (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Res') || parameterName;
if (!decorator) {
throw new exceptions_1.GenerateMetadataError('Could not find Decorator', parameter);
}
const typeNode = this.extractTsoaResponse(parameter.type);
if (!typeNode) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
if (!typeNode.typeArguments || !typeNode.typeArguments[0]) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
const statusArgument = typeNode.typeArguments[0];
const bodyArgument = typeNode.typeArguments[1];
// support a union of status codes, all with the same response body
const statusArguments = ts.isUnionTypeNode(statusArgument) ? [...statusArgument.types] : [statusArgument];
const statusArgumentTypes = statusArguments.map(a => this.current.typeChecker.getTypeAtLocation(a));
const isNumberLiteralType = (tsType) => {
// eslint-disable-next-line no-bitwise
return (tsType.getFlags() & ts.TypeFlags.NumberLiteral) !== 0;
};
const headers = (0, headerTypeHelpers_1.getHeaderType)(typeNode.typeArguments, 2, this.current);
return statusArgumentTypes.map(statusArgumentType => {
if (!isNumberLiteralType(statusArgumentType)) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
const status = String(statusArgumentType.value);
const type = new typeResolver_1.TypeResolver(bodyArgument, this.current, typeNode).resolve();
const { examples, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter) || '',
in: 'res',
name: status,
produces: headers ? this.getProducesFromResHeaders(headers) : undefined,
parameterName,
examples,
required: true,
type,
exampleLabels,
schema: type,
validators: {},
headers,
deprecated: this.getParameterDeprecation(parameter),
};
});
}
getProducesFromResHeaders(headers) {
const { properties } = headers;
const [contentTypeProp] = (properties || []).filter(p => p.name.toLowerCase() === 'content-type' && p.type.dataType === 'enum');
if (contentTypeProp) {
const type = contentTypeProp.type;
return type.enums;
}
return;
}
getBodyPropParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportBodyMethod(this.method)) {
throw new exceptions_1.GenerateMetadataError(`@BodyProp('${parameterName}') Can't support in ${this.method.toUpperCase()} method.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'body-prop',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'BodyProp') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getBodyParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportBodyMethod(this.method)) {
throw new exceptions_1.GenerateMetadataError(`@Body('${parameterName}') Can't support in ${this.method.toUpperCase()} method.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'body',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getHeaderParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Header('${parameterName}') Can't support '${type.dataType}' type.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'header',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Header') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getUploadedFileParameter(parameter, isArray) {
const parameterName = parameter.name.text;
const elementType = { dataType: 'file' };
let type;
if (isArray) {
type = { dataType: 'array', elementType };
}
else {
type = elementType;
}
if (!this.supportPathDataType(elementType)) {
throw new exceptions_1.GenerateMetadataError(`Parameter '${parameterName}:${type.dataType}' can't be passed as an uploaded file(s) parameter in '${this.method.toUpperCase()}'.`, parameter);
}
return {
description: this.getParameterDescription(parameter),
in: 'formData',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => {
if (isArray) {
return ident.text === 'UploadedFiles';
}
return ident.text === 'UploadedFile';
}) ?? parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
parameterName,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getFormFieldParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`Parameter '${parameterName}:${type.dataType}' can't be passed as form field parameter in '${this.method.toUpperCase()}'.`, parameter);
}
return {
description: this.getParameterDescription(parameter),
in: 'formData',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'FormField') ?? parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
parameterName,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getQueriesParameters(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
// Handle cases where TypeResolver doesn't properly resolve complex types
// like Zod's z.infer types to refObject or nestedObjectLiteral
if (type.dataType !== 'refObject' && type.dataType !== 'nestedObjectLiteral') {
// Try to resolve the type more aggressively for complex types
let typeNode = parameter.type;
if (!typeNode) {
const typeFromChecker = this.current.typeChecker.getTypeAtLocation(parameter);
typeNode = this.current.typeChecker.typeToTypeNode(typeFromChecker, undefined, ts.NodeBuilderFlags.NoTruncation);
}
// If it's a TypeReferenceNode (like z.infer), try to resolve it differently
if (ts.isTypeReferenceNode(typeNode)) {
try {
// Try to get the actual type from the type checker
const actualType = this.current.typeChecker.getTypeAtLocation(typeNode);
const typeNodeFromType = this.current.typeChecker.typeToTypeNode(actualType, undefined, ts.NodeBuilderFlags.NoTruncation);
const resolvedType = new typeResolver_1.TypeResolver(typeNodeFromType, this.current, parameter).resolve();
// Check if the resolved type is now acceptable
if (resolvedType.dataType === 'refObject' || resolvedType.dataType === 'nestedObjectLiteral') {
// Use the resolved type instead
for (const property of resolvedType.properties) {
this.validateQueriesProperties(property, parameterName);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'queries',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type: resolvedType,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
}
catch (error) {
// If resolution fails, log the error for debugging but continue with the original error
// This helps developers understand why the type resolution failed
console.warn(`Failed to resolve complex type for @Queries('${parameterName}'):`, error);
// Continue with the original error below
}
}
throw new exceptions_1.GenerateMetadataError(`@Queries('${parameterName}') only support 'refObject' or 'nestedObjectLiteral' types. If you want only one query parameter, please use the '@Query' decorator.`);
}
for (const property of type.properties) {
this.validateQueriesProperties(property, parameterName);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'queries',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
validateQueriesProperties(property, parentName) {
if (property.type.dataType === 'array') {
const arrayType = property.type;
if (arrayType.elementType.dataType === 'nestedObjectLiteral') {
// For arrays of nestedObjectLiteral, validate each property recursively
const nestedType = arrayType.elementType;
if (nestedType.properties) {
for (const nestedProperty of nestedType.properties) {
this.validateQueriesProperties(nestedProperty, `${parentName}.${property.name}[]`);
}
}
}
else if (!this.supportPathDataType(arrayType.elementType)) {
throw new exceptions_1.GenerateMetadataError(`@Queries('${parentName}') property '${property.name}' can't support array '${arrayType.elementType.dataType}' type.`);
}
}
else if (property.type.dataType === 'nestedObjectLiteral') {
// For nestedObjectLiteral, validate each property recursively
const nestedType = property.type;
if (nestedType.properties) {
for (const nestedProperty of nestedType.properties) {
this.validateQueriesProperties(nestedProperty, `${parentName}.${property.name}`);
}
}
}
else if (!this.supportPathDataType(property.type)) {
throw new exceptions_1.GenerateMetadataError(`@Queries('${parentName}') nested property '${property.name}' Can't support '${property.type.dataType}' type.`);
}
}
getQueryParameters(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
const commonProperties = {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'query',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Query') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
if (this.getQueryParameterIsHidden(parameter)) {
if (commonProperties.required) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support @Hidden because it is required (does not allow undefined and does not have a default value).`);
}
return [];
}
if (type.dataType === 'array') {
const arrayType = type;
if (!this.supportPathDataType(arrayType.elementType)) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support array '${arrayType.elementType.dataType}' type.`);
}
return [
{
...commonProperties,
collectionFormat: 'multi',
type: arrayType,
},
];
}
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support '${type.dataType}' type.`);
}
return [
{
...commonProperties,
type,
},
];
}
getPathParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const pathName = String((0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Path') || parameterName);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Path('${parameterName}') Can't support '${type.dataType}' type.`);
}
if (!this.path.includes(`{${pathName}}`) && !this.path.includes(`:${pathName}`)) {
throw new exceptions_1.GenerateMetadataError(`@Path('${parameterName}') Can't match in URL: '${this.path}'.`);
}
const { examples, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example: examples,
exampleLabels,
in: 'path',
name: pathName,
parameterName,
required: true,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getParameterDescription(node) {
const symbol = this.current.typeChecker.getSymbolAtLocation(node.name);
if (!symbol) {
return undefined;
}
const comments = symbol.getDocumentationComment(this.current.typeChecker);
if (comments.length) {
return ts.displayPartsToString(comments);
}
return undefined;
}
getParameterDeprecation(node) {
return (0, jsDocUtils_1.isExistJSDocTag)(node, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(node, identifier => identifier.text === 'Deprecated');
}
getParameterExample(node, parameterName) {
const exampleLabels = [];
const examples = (0, jsDocUtils_1.getJSDocTags)(node.parent, tag => {
const comment = (0, jsDocUtils_1.commentToString)(tag.comment);
const isExample = (tag.tagName.text === 'example' || tag.tagName.escapedText === 'example') && !!tag.comment && comment?.startsWith(parameterName);
if (isExample) {
const hasExampleLabel = (comment?.split(' ')[0].indexOf('.') || -1) > 0;
// custom example label is delimited by first '.' and the rest will all be included as example label
exampleLabels.push(hasExampleLabel ? comment?.split(' ')[0].split('.').slice(1).join('.') : undefined);
}
return isExample ?? false;
}).map(tag => ((0, jsDocUtils_1.commentToString)(tag.comment) || '').replace(`${(0, jsDocUtils_1.commentToString)(tag.comment)?.split(' ')[0] || ''}`, '').replace(/\r/g, ''));
if (examples.length === 0) {
return {
examples: undefined,
exampleLabels: undefined,
};
}
else {
try {
return {
examples: examples.map(example => JSON.parse(example)),
exampleLabels,
};
}
catch (e) {
const message = e instanceof Error ? e.message : String(e);
throw new exceptions_1.GenerateMetadataError(`JSON format is incorrect: ${message}`);
}
}
}
supportBodyMethod(method) {
return ['post', 'put', 'patch', 'delete'].some(m => m === method.toLowerCase());
}
supportParameterDecorator(decoratorName) {
return ['header', 'query', 'queries', 'path', 'body', 'bodyprop', 'request', 'requestprop', 'res', 'inject', 'uploadedfile', 'uploadedfiles', 'formfield'].some(d => d === decoratorName.toLocaleLowerCase());
}
supportPathDataType(parameterType) {
const supportedPathDataTypes = ['string', 'integer', 'long', 'float', 'double', 'date', 'datetime', 'buffer', 'boolean', 'enum', 'refEnum', 'file', 'any'];
if (supportedPathDataTypes.find(t => t === parameterType.dataType)) {
return true;
}
if (parameterType.dataType === 'refAlias') {
return this.supportPathDataType(parameterType.type);
}
if (parameterType.dataType === 'union') {
// skip undefined inside unions
return !parameterType.types.map(t => t.dataType === 'undefined' || this.supportPathDataType(t)).some(t => t === false);
}
return false;
}
getValidatedType(parameter) {
let typeNode = parameter.type;
if (!typeNode) {
const type = this.current.typeChecker.getTypeAtLocation(parameter);
typeNode = this.current.typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.NoTruncation);
}
return new typeResolver_1.TypeResolver(typeNode, this.current, parameter).resolve();
}
getQueryParameterIsHidden(parameter) {
const hiddenDecorators = (0, decoratorUtils_1.getDecorators)(parameter, identifier => identifier.text === 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return false;
}
if (hiddenDecorators.length > 1) {
const parameterName = parameter.name.text;
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed on @Query('${parameterName}').`);
}
return true;
}
}
exports.ParameterGenerator = ParameterGenerator;
//# sourceMappingURL=parameterGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import type { Node } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
export declare class DateTransformer extends Transformer {
transform(parentNode?: Node): Tsoa.DateType | Tsoa.DateTimeType;
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DateTransformer = void 0;
const transformer_1 = require("./transformer");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
class DateTransformer extends transformer_1.Transformer {
transform(parentNode) {
if (!parentNode) {
return { dataType: 'datetime' };
}
const tags = (0, jsDocUtils_1.getJSDocTagNames)(parentNode).filter(name => {
return ['isDate', 'isDateTime'].some(m => m === name);
});
if (tags.length === 0) {
return { dataType: 'datetime' };
}
switch (tags[0]) {
case 'isDate':
return { dataType: 'date' };
case 'isDateTime':
return { dataType: 'datetime' };
default:
return { dataType: 'datetime' };
}
}
}
exports.DateTransformer = DateTransformer;
//# sourceMappingURL=dateTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dateTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/dateTransformer.ts"],"names":[],"mappings":";;;AAGA,+CAA4C;AAC5C,uDAA0D;AAE1D,MAAa,eAAgB,SAAQ,yBAAW;IACvC,SAAS,CAAC,UAAiB;QAChC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAClC,CAAC;QACD,MAAM,IAAI,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YACtD,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAClC,CAAC;QACD,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,QAAQ;gBACX,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC9B,KAAK,YAAY;gBACf,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;YAClC;gBACE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QACpC,CAAC;IACH,CAAC;CACF;AArBD,0CAqBC"}

View File

@@ -0,0 +1,12 @@
import type { Node, EnumDeclaration, EnumMember } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
export declare class EnumTransformer extends Transformer {
static mergeMany(many: Tsoa.RefEnumType[]): Tsoa.RefEnumType;
static merge(first: Tsoa.RefEnumType, second: Tsoa.RefEnumType): Tsoa.RefEnumType;
static transformable(declaration: Node): declaration is EnumDeclaration | EnumMember;
transform(resolver: TypeResolver, declaration: EnumDeclaration | EnumMember, enumName: string): Tsoa.RefEnumType;
private transformDeclaration;
private transformMember;
}

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnumTransformer = void 0;
const typescript_1 = require("typescript");
const transformer_1 = require("./transformer");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
class EnumTransformer extends transformer_1.Transformer {
static mergeMany(many) {
let merged = this.merge(many[0], many[1]);
for (let i = 2; i < many.length; ++i) {
merged = this.merge(merged, many[i]);
}
return merged;
}
static merge(first, second) {
if (!first)
return second;
if (!second)
return first;
const description = first.description ? (second.description ? `${first.description}\n${second.description}` : first.description) : second.description;
const deprecated = first.deprecated || second.deprecated;
const enums = first.enums ? (second.enums ? [...first.enums, ...second.enums] : first.enums) : second.enums;
const enumVarnames = first.enumVarnames ? (second.enumVarnames ? [...first.enumVarnames, ...second.enumVarnames] : first.enumVarnames) : second.enumVarnames;
const example = first.example || second.example;
return {
dataType: 'refEnum',
description,
enums,
enumVarnames,
refName: first.refName,
deprecated,
example,
};
}
static transformable(declaration) {
return (0, typescript_1.isEnumDeclaration)(declaration) || (0, typescript_1.isEnumMember)(declaration);
}
transform(resolver, declaration, enumName) {
if ((0, typescript_1.isEnumDeclaration)(declaration)) {
return this.transformDeclaration(resolver, declaration, enumName);
}
return this.transformMember(resolver, declaration, enumName);
}
transformDeclaration(resolver, declaration, enumName) {
const isNotUndefined = (item) => {
return item === undefined ? false : true;
};
const enums = declaration.members.map(e => resolver.current.typeChecker.getConstantValue(e)).filter(isNotUndefined);
const enumVarnames = declaration.members.map(e => e.name.getText()).filter(isNotUndefined);
return {
dataType: 'refEnum',
description: resolver.getNodeDescription(declaration),
example: resolver.getNodeExample(declaration),
enums,
enumVarnames,
refName: enumName,
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(declaration, tag => tag.tagName.text === 'deprecated'),
};
}
transformMember(resolver, declaration, enumName) {
return {
dataType: 'refEnum',
refName: enumName,
enums: [resolver.current.typeChecker.getConstantValue(declaration)],
enumVarnames: [declaration.name.getText()],
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(declaration, tag => tag.tagName.text === 'deprecated'),
};
}
}
exports.EnumTransformer = EnumTransformer;
//# sourceMappingURL=enumTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"enumTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/enumTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,11 @@
import { type TypeNode, SyntaxKind } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
export declare class PrimitiveTransformer extends Transformer {
static resolveKindToPrimitive(syntaxKind: SyntaxKind): ResolvesToPrimitive;
transform(defaultNumberType: DefaultNumberType, typeNode: TypeNode, partentJsDocTagNames?: string[]): Tsoa.Type | undefined;
private transformNumber;
}
type DefaultNumberType = NonNullable<'double' | 'float' | 'integer' | 'long' | undefined>;
type ResolvesToPrimitive = 'number' | 'string' | 'boolean' | 'void' | 'undefined' | 'null' | undefined;
export {};

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrimitiveTransformer = void 0;
const typescript_1 = require("typescript");
const runtime_1 = require("@tsoa/runtime");
const transformer_1 = require("./transformer");
class PrimitiveTransformer extends transformer_1.Transformer {
static resolveKindToPrimitive(syntaxKind) {
switch (syntaxKind) {
case typescript_1.SyntaxKind.NumberKeyword:
return 'number';
case typescript_1.SyntaxKind.StringKeyword:
return 'string';
case typescript_1.SyntaxKind.BooleanKeyword:
return 'boolean';
case typescript_1.SyntaxKind.VoidKeyword:
return 'void';
case typescript_1.SyntaxKind.UndefinedKeyword:
return 'undefined';
case typescript_1.SyntaxKind.NullKeyword:
return 'null';
default:
return undefined;
}
}
transform(defaultNumberType, typeNode, partentJsDocTagNames) {
const resolvedType = PrimitiveTransformer.resolveKindToPrimitive(typeNode.kind);
if (!resolvedType) {
return;
}
switch (resolvedType) {
case 'number':
return this.transformNumber(defaultNumberType, partentJsDocTagNames);
case 'string':
case 'boolean':
case 'void':
case 'undefined':
return { dataType: resolvedType };
case 'null':
return {
dataType: 'enum',
enums: [null],
};
default:
return (0, runtime_1.assertNever)(resolvedType);
}
}
transformNumber(defaultNumberType, partentJsDocTagNames) {
if (!partentJsDocTagNames || partentJsDocTagNames.length === 0) {
return { dataType: defaultNumberType };
}
const tags = partentJsDocTagNames.filter(name => {
return ['isInt', 'isLong', 'isFloat', 'isDouble'].some(m => m === name);
});
switch (tags[0]) {
case 'isInt':
return { dataType: 'integer' };
case 'isLong':
return { dataType: 'long' };
case 'isFloat':
return { dataType: 'float' };
case 'isDouble':
return { dataType: 'double' };
default:
return { dataType: defaultNumberType };
}
}
}
exports.PrimitiveTransformer = PrimitiveTransformer;
//# sourceMappingURL=primitiveTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"primitiveTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/primitiveTransformer.ts"],"names":[],"mappings":";;;AAAA,2CAAuD;AACvD,2CAAkD;AAElD,+CAA4C;AAE5C,MAAa,oBAAqB,SAAQ,yBAAW;IAC5C,MAAM,CAAC,sBAAsB,CAAC,UAAsB;QACzD,QAAQ,UAAU,EAAE,CAAC;YACnB,KAAK,uBAAU,CAAC,aAAa;gBAC3B,OAAO,QAAQ,CAAC;YAClB,KAAK,uBAAU,CAAC,aAAa;gBAC3B,OAAO,QAAQ,CAAC;YAClB,KAAK,uBAAU,CAAC,cAAc;gBAC5B,OAAO,SAAS,CAAC;YACnB,KAAK,uBAAU,CAAC,WAAW;gBACzB,OAAO,MAAM,CAAC;YAChB,KAAK,uBAAU,CAAC,gBAAgB;gBAC9B,OAAO,WAAW,CAAC;YACrB,KAAK,uBAAU,CAAC,WAAW;gBACzB,OAAO,MAAM,CAAC;YAChB;gBACE,OAAO,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;IAEM,SAAS,CAAC,iBAAoC,EAAE,QAAkB,EAAE,oBAA+B;QACxG,MAAM,YAAY,GAAG,oBAAoB,CAAC,sBAAsB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,eAAe,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;YACvE,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,MAAM,CAAC;YACZ,KAAK,WAAW;gBACd,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;YACpC,KAAK,MAAM;gBACT,OAAO;oBACL,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,CAAC,IAAI,CAAC;iBACd,CAAC;YACJ;gBACE,OAAO,IAAA,qBAAW,EAAC,YAAY,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,iBAAoC,EAAE,oBAA+B;QAC3F,IAAI,CAAC,oBAAoB,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;QACzC,CAAC;QAED,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YAC9C,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,OAAO;gBACV,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YACjC,KAAK,QAAQ;gBACX,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC9B,KAAK,SAAS;gBACZ,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAC/B,KAAK,UAAU;gBACb,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAChC;gBACE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;CACF;AAlED,oDAkEC"}

View File

@@ -0,0 +1,12 @@
import type { Token, InterfaceDeclaration, ClassDeclaration } from 'typescript';
import { SyntaxKind } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
type OverrideToken = Token<SyntaxKind.QuestionToken> | Token<SyntaxKind.PlusToken> | Token<SyntaxKind.MinusToken> | undefined;
export declare class PropertyTransformer extends Transformer {
transform(resolver: TypeResolver, node: InterfaceDeclaration | ClassDeclaration, overrideToken?: OverrideToken): Tsoa.Property[];
private propertyFromSignature;
private propertyFromDeclaration;
}
export {};

View File

@@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertyTransformer = void 0;
const typescript_1 = require("typescript");
const transformer_1 = require("./transformer");
const exceptions_1 = require("../exceptions");
const typeResolver_1 = require("../typeResolver");
const initializer_value_1 = require("../initializer-value");
const validatorUtils_1 = require("../../utils/validatorUtils");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
const decoratorUtils_1 = require("../../utils/decoratorUtils");
const flowUtils_1 = require("../../utils/flowUtils");
class PropertyTransformer extends transformer_1.Transformer {
transform(resolver, node, overrideToken) {
const isIgnored = (e) => {
let ignore = (0, jsDocUtils_1.isExistJSDocTag)(e, tag => tag.tagName.text === 'ignore');
ignore = ignore || (e.flags & typescript_1.NodeFlags.ThisNodeHasError) > 0;
return ignore;
};
// Interface model
if ((0, typescript_1.isInterfaceDeclaration)(node)) {
return node.members
.filter((member) => !isIgnored(member) && (0, typescript_1.isPropertySignature)(member))
.map((member) => this.propertyFromSignature(resolver, member, overrideToken));
}
const properties = [];
for (const member of node.members) {
if (!isIgnored(member) && (0, typescript_1.isPropertyDeclaration)(member) && !this.hasStaticModifier(member) && this.hasPublicModifier(member)) {
properties.push(member);
}
}
const classConstructor = node.members.find(member => (0, typescript_1.isConstructorDeclaration)(member));
if (classConstructor && classConstructor.parameters) {
const constructorProperties = classConstructor.parameters.filter(parameter => this.isAccessibleParameter(parameter));
properties.push(...constructorProperties);
}
return properties.map(property => this.propertyFromDeclaration(resolver, property, overrideToken));
}
propertyFromSignature(resolver, propertySignature, overrideToken) {
(0, flowUtils_1.throwUnless)(propertySignature.type, new exceptions_1.GenerateMetadataError(`No valid type found for property declaration.`));
let required = !propertySignature.questionToken;
if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.QuestionToken) {
required = false;
}
const def = typeResolver_1.TypeResolver.getDefault(propertySignature);
const property = {
default: def,
description: resolver.getNodeDescription(propertySignature),
example: resolver.getNodeExample(propertySignature),
format: resolver.getNodeFormat(propertySignature),
name: resolver.getPropertyName(propertySignature),
required,
type: new typeResolver_1.TypeResolver(propertySignature.type, resolver.current, propertySignature.type.parent, resolver.context).resolve(),
validators: (0, validatorUtils_1.getPropertyValidators)(propertySignature) || {},
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertySignature, tag => tag.tagName.text === 'deprecated'),
extensions: resolver.getNodeExtension(propertySignature),
};
return property;
}
propertyFromDeclaration(resolver, propertyDeclaration, overrideToken) {
let typeNode = propertyDeclaration.type;
const tsType = resolver.current.typeChecker.getTypeAtLocation(propertyDeclaration);
if (!typeNode) {
// Type is from initializer
typeNode = resolver.current.typeChecker.typeToTypeNode(tsType, undefined, typescript_1.NodeBuilderFlags.NoTruncation);
}
const type = new typeResolver_1.TypeResolver(typeNode, resolver.current, propertyDeclaration, resolver.context, tsType).resolve();
let required = !propertyDeclaration.questionToken && !propertyDeclaration.initializer;
if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.QuestionToken) {
required = false;
}
let def = (0, initializer_value_1.getInitializerValue)(propertyDeclaration.initializer, resolver.current.typeChecker);
if (def === undefined) {
def = typeResolver_1.TypeResolver.getDefault(propertyDeclaration);
}
const property = {
default: def,
description: resolver.getNodeDescription(propertyDeclaration),
example: resolver.getNodeExample(propertyDeclaration),
format: resolver.getNodeFormat(propertyDeclaration),
name: resolver.getPropertyName(propertyDeclaration),
required,
type,
validators: (0, validatorUtils_1.getPropertyValidators)(propertyDeclaration) || {},
// class properties and constructor parameters may be deprecated either via jsdoc annotation or decorator
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertyDeclaration, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(propertyDeclaration, identifier => identifier.text === 'Deprecated'),
extensions: resolver.getNodeExtension(propertyDeclaration),
};
return property;
}
}
exports.PropertyTransformer = PropertyTransformer;
//# sourceMappingURL=propertyTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"propertyTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/propertyTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
import type { TypeAliasDeclaration, Type } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
export declare class ReferenceTransformer extends Transformer {
static merge(referenceTypes: Tsoa.ReferenceType[]): Tsoa.ReferenceType;
static mergeManyRefObj(many: Tsoa.RefObjectType[]): Tsoa.RefObjectType;
static mergeRefObj(first: Tsoa.RefObjectType, second: Tsoa.RefObjectType): Tsoa.RefObjectType;
transform(declaration: TypeAliasDeclaration, refTypeName: string, resolver: TypeResolver, referencer?: Type): Tsoa.ReferenceType;
}

View File

@@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReferenceTransformer = void 0;
const transformer_1 = require("./transformer");
const enumTransformer_1 = require("./enumTransformer");
const typeResolver_1 = require("../typeResolver");
const exceptions_1 = require("../exceptions");
const validatorUtils_1 = require("../../utils/validatorUtils");
class ReferenceTransformer extends transformer_1.Transformer {
static merge(referenceTypes) {
if (referenceTypes.length === 0) {
throw new exceptions_1.GenerateMetadataError('Cannot merge empty reference types array');
}
if (referenceTypes.length === 1) {
return referenceTypes[0];
}
if (referenceTypes.every(refType => refType.dataType === 'refEnum')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return enumTransformer_1.EnumTransformer.mergeMany(referenceTypes);
}
if (referenceTypes.every(refType => refType.dataType === 'refObject')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return this.mergeManyRefObj(referenceTypes);
}
throw new exceptions_1.GenerateMetadataError(`These resolved type merge rules are not defined: ${JSON.stringify(referenceTypes)}`);
}
static mergeManyRefObj(many) {
let merged = this.mergeRefObj(many[0], many[1]);
for (let i = 2; i < many.length; ++i) {
merged = this.mergeRefObj(merged, many[i]);
}
return merged;
}
static mergeRefObj(first, second) {
const description = first.description ? (second.description ? `${first.description}\n${second.description}` : first.description) : second.description;
const deprecated = first.deprecated || second.deprecated;
const example = first.example || second.example;
const properties = [...first.properties, ...second.properties.filter(prop => first.properties.every(firstProp => firstProp.name !== prop.name))];
const mergeAdditionalTypes = (first, second) => {
return {
dataType: 'union',
types: [first, second],
};
};
const additionalProperties = first.additionalProperties
? second.additionalProperties
? mergeAdditionalTypes(first.additionalProperties, second.additionalProperties)
: first.additionalProperties
: second.additionalProperties;
const result = {
dataType: 'refObject',
description,
properties,
additionalProperties,
refName: first.refName,
deprecated,
example,
};
return result;
}
transform(declaration, refTypeName, resolver, referencer) {
const example = resolver.getNodeExample(declaration);
const referenceType = {
dataType: 'refAlias',
default: typeResolver_1.TypeResolver.getDefault(declaration),
description: resolver.getNodeDescription(declaration),
refName: refTypeName,
format: resolver.getNodeFormat(declaration),
type: new typeResolver_1.TypeResolver(declaration.type, resolver.current, declaration, resolver.context, resolver.referencer || referencer).resolve(),
validators: (0, validatorUtils_1.getPropertyValidators)(declaration) || {},
...(example && { example }),
};
return referenceType;
}
}
exports.ReferenceTransformer = ReferenceTransformer;
//# sourceMappingURL=referenceTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"referenceTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/referenceTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,9 @@
import { type HasModifiers } from 'typescript';
/**
* Transformer responsible to transforming native ts node into tsoa type.
*/
export declare abstract class Transformer {
protected hasPublicModifier(node: HasModifiers): boolean;
protected hasStaticModifier(node: HasModifiers): boolean | undefined;
protected isAccessibleParameter(node: HasModifiers): boolean;
}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transformer = void 0;
const typescript_1 = require("typescript");
/**
* Transformer responsible to transforming native ts node into tsoa type.
*/
class Transformer {
hasPublicModifier(node) {
return (!node.modifiers ||
node.modifiers.every(modifier => {
return modifier.kind !== typescript_1.SyntaxKind.ProtectedKeyword && modifier.kind !== typescript_1.SyntaxKind.PrivateKeyword;
}));
}
hasStaticModifier(node) {
return (node.modifiers &&
node.modifiers.some(modifier => {
return modifier.kind === typescript_1.SyntaxKind.StaticKeyword;
}));
}
isAccessibleParameter(node) {
const modifiers = (0, typescript_1.getModifiers)(node);
if (modifiers == null || modifiers.length === 0) {
return false;
}
// public || public readonly
if (modifiers.some(modifier => modifier.kind === typescript_1.SyntaxKind.PublicKeyword)) {
return true;
}
// readonly, not private readonly, not public readonly
const isReadonly = modifiers.some(modifier => modifier.kind === typescript_1.SyntaxKind.ReadonlyKeyword);
const isProtectedOrPrivate = modifiers.some(modifier => {
return modifier.kind === typescript_1.SyntaxKind.ProtectedKeyword || modifier.kind === typescript_1.SyntaxKind.PrivateKeyword;
});
return isReadonly && !isProtectedOrPrivate;
}
}
exports.Transformer = Transformer;
//# sourceMappingURL=transformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/transformer.ts"],"names":[],"mappings":";;;AAAA,2CAAyE;AAEzE;;GAEG;AACH,MAAsB,WAAW;IACrB,iBAAiB,CAAC,IAAkB;QAC5C,OAAO,CACL,CAAC,IAAI,CAAC,SAAS;YACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;gBAC9B,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,gBAAgB,IAAI,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,cAAc,CAAC;YACtG,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAES,iBAAiB,CAAC,IAAkB;QAC5C,OAAO,CACL,IAAI,CAAC,SAAS;YACd,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAC7B,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,aAAa,CAAC;YACpD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAES,qBAAqB,CAAC,IAAkB;QAChD,MAAM,SAAS,GAAG,IAAA,yBAAY,EAAC,IAAI,CAAC,CAAC;QAErC,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,4BAA4B;QAC5B,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sDAAsD;QACtD,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,eAAe,CAAC,CAAC;QAC5F,MAAM,oBAAoB,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YACrD,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,gBAAgB,IAAI,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,cAAc,CAAC;QACtG,CAAC,CAAC,CAAC;QACH,OAAO,UAAU,IAAI,CAAC,oBAAoB,CAAC;IAC7C,CAAC;CACF;AAtCD,kCAsCC"}

View File

@@ -0,0 +1,51 @@
import { Tsoa } from '@tsoa/runtime';
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
type UsableDeclaration = ts.InterfaceDeclaration | ts.ClassDeclaration | ts.PropertySignature | ts.TypeAliasDeclaration | ts.EnumMember;
interface Context {
[name: string]: {
type: ts.TypeNode;
name: string;
};
}
export declare class TypeResolver {
private readonly typeNode;
readonly current: MetadataGenerator;
private readonly parentNode?;
context: Context;
readonly referencer?: ts.Type | undefined;
constructor(typeNode: ts.TypeNode, current: MetadataGenerator, parentNode?: ts.Node | undefined, context?: Context, referencer?: ts.Type | undefined);
static clearCache(): void;
resolve(): Tsoa.Type;
private resolveTypeOperatorNode;
private resolveIndexedAccessTypeNode;
private resolveTypeReferenceNode;
private getLiteralValue;
private getDesignatedModels;
private hasFlag;
private getReferencer;
private static typeReferenceToEntityName;
private calcRefTypeName;
private calcMemberJsDocProperties;
private calcTypeName;
private calcTypeReferenceTypeName;
private getReferenceType;
private addToLocalReferenceTypeCache;
private getModelReference;
private getRefTypeName;
private createCircularDependencyResolver;
private nodeIsUsable;
private getModelTypeDeclarations;
private getSymbolAtLocation;
private getModelAdditionalProperties;
private typeArgumentsToContext;
private getModelInheritedProperties;
getNodeDescription(node: UsableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.EnumDeclaration): string | undefined;
getNodeFormat(node: ts.Node): string | undefined;
getPropertyName(prop: ts.PropertySignature | ts.PropertyDeclaration | ts.ParameterDeclaration): string;
getNodeExample(node: ts.Node): any;
getNodeExtension(node: ts.Node): Tsoa.Extension[];
private getDecoratorsByIdentifier;
static getDefault(node: ts.Node): any;
}
export {};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa } from '@tsoa/runtime';
import { Config as BaseConfig } from '@tsoa/runtime';
export declare function generateRoutes<Config extends ExtendedRoutesConfig>(routesConfig: Config, compilerOptions?: ts.CompilerOptions, ignorePaths?: string[],
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata?: Tsoa.Metadata, defaultNumberType?: BaseConfig['defaultNumberType']): Promise<Tsoa.Metadata>;

90
node_modules/@tsoa/cli/dist/module/generate-routes.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateRoutes = generateRoutes;
const metadataGenerator_1 = require("../metadataGeneration/metadataGenerator");
const defaultRouteGenerator_1 = require("../routeGeneration/defaultRouteGenerator");
const fs_1 = require("../utils/fs");
const path = require("path");
async function generateRoutes(routesConfig, compilerOptions, ignorePaths,
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata, defaultNumberType) {
if (!metadata) {
metadata = new metadataGenerator_1.MetadataGenerator(routesConfig.entryFile, compilerOptions, ignorePaths, routesConfig.controllerPathGlobs, routesConfig.rootSecurity, defaultNumberType).Generate();
}
const routeGenerator = await getRouteGenerator(metadata, routesConfig);
await (0, fs_1.fsMkDir)(routesConfig.routesDir, { recursive: true });
await routeGenerator.GenerateCustomRoutes();
if (routesConfig.multerOpts) {
console.warn('Config MulterOptions is deprecated since v6.4.0 instroduces RegisterRoutes can pass multerOptions,' +
'we will quickly remove this options soon at future version.' +
'(https://github.com/lukeautry/tsoa/issues/1587#issuecomment-2391291433)' +
'(https://github.com/lukeautry/tsoa/pull/1638)');
}
return metadata;
}
async function getRouteGenerator(metadata, routesConfig) {
// default route generator for express/koa/hapi
// custom route generator
const routeGenerator = routesConfig.routeGenerator;
if (routeGenerator !== undefined) {
if (typeof routeGenerator === 'string') {
try {
// try as a module import
const module = (await Promise.resolve(`${routeGenerator}`).then(s => __importStar(require(s))));
return new module.default(metadata, routesConfig);
}
catch (_err) {
// try to find a relative import path
const relativePath = path.relative(__dirname, routeGenerator);
const module = (await Promise.resolve(`${relativePath}`).then(s => __importStar(require(s))));
return new module.default(metadata, routesConfig);
}
}
else {
return new routeGenerator(metadata, routesConfig);
}
}
if (routesConfig.middleware !== undefined || routesConfig.middlewareTemplate !== undefined) {
return new defaultRouteGenerator_1.DefaultRouteGenerator(metadata, routesConfig);
}
else {
routesConfig.middleware = 'express';
return new defaultRouteGenerator_1.DefaultRouteGenerator(metadata, routesConfig);
}
}
//# sourceMappingURL=generate-routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generate-routes.js","sourceRoot":"","sources":["../../src/module/generate-routes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,wCA6BC;AApCD,+EAA4E;AAE5E,oFAAiF;AACjF,oCAAsC;AACtC,6BAA8B;AAGvB,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,eAAoC,EACpC,WAAsB;AACtB;;GAEG;AACH,QAAwB,EACxB,iBAAmD;IAEnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,qCAAiB,CAAC,YAAY,CAAC,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpL,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAEvE,MAAM,IAAA,YAAO,EAAC,YAAY,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,MAAM,cAAc,CAAC,oBAAoB,EAAE,CAAC;IAE5C,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CACV,oGAAoG;YAClG,6DAA6D;YAC7D,yEAAyE;YACzE,+CAA+C,CAClD,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAsC,QAAuB,EAAE,YAAoB;IACjH,+CAA+C;IAC/C,yBAAyB;IACzB,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,CAAC;IACnD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,yBAAyB;gBACzB,MAAM,MAAM,GAAG,CAAC,yBAAa,cAAc,uCAAC,CAAiC,CAAC;gBAC9E,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,IAAI,EAAE,CAAC;gBACd,qCAAqC;gBACrC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;gBAC9D,MAAM,MAAM,GAAG,CAAC,yBAAa,YAAY,uCAAC,CAAiC,CAAC;gBAC5E,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IACD,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS,IAAI,YAAY,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC3F,OAAO,IAAI,6CAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,UAAU,GAAG,SAAS,CAAC;QACpC,OAAO,IAAI,6CAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
import { ExtendedSpecConfig } from '../cli';
import { Tsoa, Config } from '@tsoa/runtime';
export declare const getSwaggerOutputPath: (swaggerConfig: ExtendedSpecConfig) => string;
export declare const generateSpec: (swaggerConfig: ExtendedSpecConfig, compilerOptions?: ts.CompilerOptions, ignorePaths?: string[],
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata?: Tsoa.Metadata, defaultNumberType?: Config["defaultNumberType"]) => Promise<Tsoa.Metadata>;

79
node_modules/@tsoa/cli/dist/module/generate-spec.js generated vendored Normal file
View File

@@ -0,0 +1,79 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSpec = exports.getSwaggerOutputPath = void 0;
const YAML = __importStar(require("yaml"));
const metadataGenerator_1 = require("../metadataGeneration/metadataGenerator");
const specGenerator2_1 = require("../swagger/specGenerator2");
const specGenerator3_1 = require("../swagger/specGenerator3");
const specGenerator31_1 = require("../swagger/specGenerator31");
const fs_1 = require("../utils/fs");
const getSwaggerOutputPath = (swaggerConfig) => {
const ext = swaggerConfig.yaml ? 'yaml' : 'json';
const specFileBaseName = swaggerConfig.specFileBaseName || 'swagger';
return `${swaggerConfig.outputDirectory}/${specFileBaseName}.${ext}`;
};
exports.getSwaggerOutputPath = getSwaggerOutputPath;
const generateSpec = async (swaggerConfig, compilerOptions, ignorePaths,
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata, defaultNumberType) => {
if (!metadata) {
metadata = new metadataGenerator_1.MetadataGenerator(swaggerConfig.entryFile, compilerOptions, ignorePaths, swaggerConfig.controllerPathGlobs, swaggerConfig.rootSecurity, defaultNumberType).Generate();
}
let spec;
switch (swaggerConfig.specVersion) {
case 2:
spec = new specGenerator2_1.SpecGenerator2(metadata, swaggerConfig).GetSpec();
break;
case 3:
spec = new specGenerator3_1.SpecGenerator3(metadata, swaggerConfig).GetSpec();
break;
case 3.1:
default:
spec = new specGenerator31_1.SpecGenerator31(metadata, swaggerConfig).GetSpec();
}
await (0, fs_1.fsMkDir)(swaggerConfig.outputDirectory, { recursive: true });
let data = JSON.stringify(spec, null, '\t');
if (swaggerConfig.yaml) {
data = YAML.stringify(JSON.parse(data));
}
const outputPath = (0, exports.getSwaggerOutputPath)(swaggerConfig);
await (0, fs_1.fsWriteFile)(outputPath, data, { encoding: 'utf8' });
return metadata;
};
exports.generateSpec = generateSpec;
//# sourceMappingURL=generate-spec.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generate-spec.js","sourceRoot":"","sources":["../../src/module/generate-spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,2CAA6B;AAE7B,+EAA4E;AAE5E,8DAA2D;AAC3D,8DAA2D;AAC3D,gEAA6D;AAC7D,oCAAmD;AAE5C,MAAM,oBAAoB,GAAG,CAAC,aAAiC,EAAE,EAAE;IACxE,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,gBAAgB,GAAG,aAAa,CAAC,gBAAgB,IAAI,SAAS,CAAC;IAErE,OAAO,GAAG,aAAa,CAAC,eAAe,IAAI,gBAAgB,IAAI,GAAG,EAAE,CAAC;AACvE,CAAC,CAAC;AALW,QAAA,oBAAoB,wBAK/B;AAEK,MAAM,YAAY,GAAG,KAAK,EAC/B,aAAiC,EACjC,eAAoC,EACpC,WAAsB;AACtB;;GAEG;AACH,QAAwB,EACxB,iBAA+C,EAC/C,EAAE;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,qCAAiB,CAAC,aAAa,CAAC,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,CAAC,mBAAmB,EAAE,aAAa,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvL,CAAC;IAED,IAAI,IAAkB,CAAC;IAEvB,QAAQ,aAAa,CAAC,WAAW,EAAE,CAAC;QAClC,KAAK,CAAC;YACJ,IAAI,GAAG,IAAI,+BAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7D,MAAM;QACR,KAAK,CAAC;YACJ,IAAI,GAAG,IAAI,+BAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7D,MAAM;QACR,KAAK,GAAG,CAAC;QACT;YACE,IAAI,GAAG,IAAI,iCAAe,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,IAAA,YAAO,EAAC,aAAa,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElE,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,aAAa,CAAC,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,UAAU,GAAG,IAAA,4BAAoB,EAAC,aAAa,CAAC,CAAC;IACvD,MAAM,IAAA,gBAAW,EAAC,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAE1D,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAvCW,QAAA,YAAY,gBAuCvB"}

View File

@@ -0,0 +1,12 @@
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa } from '@tsoa/runtime';
import { AbstractRouteGenerator } from './routeGenerator';
export declare class DefaultRouteGenerator extends AbstractRouteGenerator<ExtendedRoutesConfig> {
pathTransformerFn: (path: string) => string;
template: string;
constructor(metadata: Tsoa.Metadata, options: ExtendedRoutesConfig);
GenerateCustomRoutes(): Promise<void>;
GenerateRoutes(middlewareTemplate: string): Promise<void>;
protected pathTransformer(path: string): string;
buildContent(middlewareTemplate: string): string;
}

View File

@@ -0,0 +1,117 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultRouteGenerator = void 0;
const fs = __importStar(require("fs"));
const handlebars = __importStar(require("handlebars"));
const path = __importStar(require("path"));
const runtime_1 = require("@tsoa/runtime");
const fs_1 = require("../utils/fs");
const pathUtils_1 = require("../utils/pathUtils");
const routeGenerator_1 = require("./routeGenerator");
class DefaultRouteGenerator extends routeGenerator_1.AbstractRouteGenerator {
constructor(metadata, options) {
super(metadata, options);
this.pathTransformerFn = pathUtils_1.convertBracesPathParams;
switch (options.middleware) {
case 'hapi':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/hapi.hbs');
this.pathTransformerFn = (path) => path;
break;
case 'koa':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/koa.hbs');
break;
case 'express':
default:
this.template = path.join(__dirname, '..', 'routeGeneration/templates/express.hbs');
}
if (options.middlewareTemplate) {
this.template = options.middlewareTemplate;
}
}
async GenerateCustomRoutes() {
const data = await (0, fs_1.fsReadFile)(path.join(this.template));
const file = data.toString();
return await this.GenerateRoutes(file);
}
async GenerateRoutes(middlewareTemplate) {
const allowedExtensions = this.options.esm ? ['.ts', '.mts', '.cts'] : ['.ts'];
if (!fs.lstatSync(this.options.routesDir).isDirectory()) {
throw new Error(`routesDir should be a directory`);
}
else if (this.options.routesFileName !== undefined) {
const ext = path.extname(this.options.routesFileName);
if (!allowedExtensions.includes(ext)) {
throw new Error(`routesFileName should be a valid typescript file.`);
}
}
const fileName = `${this.options.routesDir}/${this.options.routesFileName || 'routes.ts'}`;
const content = this.buildContent(middlewareTemplate);
if (await this.shouldWriteFile(fileName, content)) {
await (0, fs_1.fsWriteFile)(fileName, content);
}
}
pathTransformer(path) {
return this.pathTransformerFn(path);
}
buildContent(middlewareTemplate) {
handlebars.registerHelper('json', (context) => {
return JSON.stringify(context);
});
const additionalPropsHelper = (additionalProperties) => {
if (additionalProperties) {
// Then the model for this type explicitly allows additional properties and thus we should assign that
return JSON.stringify(additionalProperties);
}
else if (this.options.noImplicitAdditionalProperties === 'silently-remove-extras') {
return JSON.stringify(false);
}
else if (this.options.noImplicitAdditionalProperties === 'throw-on-extras') {
return JSON.stringify(false);
}
else if (this.options.noImplicitAdditionalProperties === 'ignore') {
return JSON.stringify(true);
}
else {
return (0, runtime_1.assertNever)(this.options.noImplicitAdditionalProperties);
}
};
handlebars.registerHelper('additionalPropsHelper', additionalPropsHelper);
const routesTemplate = handlebars.compile(middlewareTemplate, { noEscape: true });
return routesTemplate(this.buildContext());
}
}
exports.DefaultRouteGenerator = DefaultRouteGenerator;
//# sourceMappingURL=defaultRouteGenerator.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"defaultRouteGenerator.js","sourceRoot":"","sources":["../../src/routeGeneration/defaultRouteGenerator.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,56 @@
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa, TsoaRoute } from '@tsoa/runtime';
export declare abstract class AbstractRouteGenerator<Config extends ExtendedRoutesConfig> {
protected readonly metadata: Tsoa.Metadata;
protected readonly options: Config;
constructor(metadata: Tsoa.Metadata, options: Config);
/**
* This is the entrypoint for a generator to create a custom set of routes
*/
abstract GenerateCustomRoutes(): Promise<void>;
buildModels(): TsoaRoute.Models;
protected pathTransformer(path: string): string;
protected buildContext(): {
authenticationModule: string | undefined;
basePath: string;
canImportByAlias: boolean;
controllers: {
actions: {
fullPath: string;
method: string;
name: string;
parameters: {
[name: string]: TsoaRoute.ParameterSchema;
};
path: string;
uploadFile: boolean;
uploadFileName: {
name: string;
maxCount: number | undefined;
multiple: boolean;
}[];
security: Tsoa.Security[];
successStatus: string | number;
}[];
modulePath: string;
name: string;
path: string;
}[];
environment: NodeJS.ProcessEnv;
iocModule: string | undefined;
minimalSwaggerConfig: {
noImplicitAdditionalProperties: "ignore" | "throw-on-extras" | "silently-remove-extras";
bodyCoercion: boolean;
};
models: TsoaRoute.Models;
useFileUploads: boolean;
multerOpts: Config["multerOpts"];
useSecurity: boolean;
esm: boolean | undefined;
};
protected getRelativeImportPath(fileLocation: string): string;
protected buildPropertySchema(source: Tsoa.Property): TsoaRoute.PropertySchema;
protected buildParameterSchema(source: Tsoa.Parameter): TsoaRoute.ParameterSchema;
protected buildProperty(type: Tsoa.Type): TsoaRoute.PropertySchema;
protected shouldWriteFile(fileName: string, content: string): Promise<boolean>;
}

View File

@@ -0,0 +1,255 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractRouteGenerator = void 0;
const path = __importStar(require("path"));
const runtime_1 = require("@tsoa/runtime");
const internalTypeGuards_1 = require("../utils/internalTypeGuards");
const pathUtils_1 = require("../utils/pathUtils");
const fs_1 = require("../utils/fs");
class AbstractRouteGenerator {
constructor(metadata, options) {
this.metadata = metadata;
this.options = options;
}
buildModels() {
const models = {};
Object.keys(this.metadata.referenceTypeMap).forEach(name => {
const referenceType = this.metadata.referenceTypeMap[name];
let model;
if (referenceType.dataType === 'refEnum') {
const refEnumModel = {
dataType: 'refEnum',
enums: referenceType.enums,
};
model = refEnumModel;
}
else if (referenceType.dataType === 'refObject') {
const propertySchemaDictionary = {};
referenceType.properties.forEach(property => {
propertySchemaDictionary[property.name] = this.buildPropertySchema(property);
});
const refObjModel = {
dataType: 'refObject',
properties: propertySchemaDictionary,
};
if (referenceType.additionalProperties) {
refObjModel.additionalProperties = this.buildProperty(referenceType.additionalProperties);
}
else if (this.options.noImplicitAdditionalProperties !== 'ignore') {
refObjModel.additionalProperties = false;
}
else {
// Since Swagger allows "excess properties" (to use a TypeScript term) by default
refObjModel.additionalProperties = true;
}
model = refObjModel;
}
else if (referenceType.dataType === 'refAlias') {
const refType = {
dataType: 'refAlias',
type: {
...this.buildProperty(referenceType.type),
validators: referenceType.validators,
default: referenceType.default,
},
};
model = refType;
}
else {
model = (0, runtime_1.assertNever)(referenceType);
}
models[name] = model;
});
return models;
}
pathTransformer(path) {
return (0, pathUtils_1.convertBracesPathParams)(path);
}
buildContext() {
const authenticationModule = this.options.authenticationModule ? this.getRelativeImportPath(this.options.authenticationModule) : undefined;
const iocModule = this.options.iocModule ? this.getRelativeImportPath(this.options.iocModule) : undefined;
// Left in for backwards compatibility, previously if we're working locally then tsoa runtime code wasn't an importable module but now it is.
const canImportByAlias = true;
const normalisedBasePath = (0, pathUtils_1.normalisePath)(this.options.basePath, '/');
return {
authenticationModule,
basePath: normalisedBasePath,
canImportByAlias,
controllers: this.metadata.controllers.map(controller => {
const normalisedControllerPath = this.pathTransformer((0, pathUtils_1.normalisePath)(controller.path, '/'));
return {
actions: controller.methods.map(method => {
const parameterObjs = {};
method.parameters.forEach(parameter => {
parameterObjs[parameter.parameterName] = this.buildParameterSchema(parameter);
});
const normalisedMethodPath = this.pathTransformer((0, pathUtils_1.normalisePath)(method.path, '/'));
const normalisedFullPath = (0, pathUtils_1.normalisePath)(`${normalisedBasePath}${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
const uploadFilesWithDifferentFieldParameter = method.parameters.filter(parameter => parameter.type.dataType === 'file' || (parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file'));
return {
fullPath: normalisedFullPath,
method: method.method.toLowerCase(),
name: method.name,
parameters: parameterObjs,
path: normalisedMethodPath,
uploadFile: uploadFilesWithDifferentFieldParameter.length > 0,
uploadFileName: uploadFilesWithDifferentFieldParameter.map(parameter => ({
name: parameter.name,
maxCount: parameter.type.dataType === 'file' ? 1 : undefined,
multiple: parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file',
})),
security: method.security,
successStatus: method.successStatus ? method.successStatus : 'undefined',
};
}),
modulePath: this.getRelativeImportPath(controller.location),
name: controller.name,
path: normalisedControllerPath,
};
}),
environment: process.env,
iocModule,
minimalSwaggerConfig: { noImplicitAdditionalProperties: this.options.noImplicitAdditionalProperties, bodyCoercion: this.options.bodyCoercion },
models: this.buildModels(),
useFileUploads: this.metadata.controllers.some(controller => controller.methods.some(method => !!method.parameters.find(parameter => {
if (parameter.type.dataType === 'file') {
return true;
}
else if (parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file') {
return true;
}
return false;
}))),
multerOpts: {
limits: {
fileSize: 8388608, // 8mb
},
...this.options.multerOpts,
},
useSecurity: this.metadata.controllers.some(controller => controller.methods.some(method => !!method.security.length)),
esm: this.options.esm,
};
}
getRelativeImportPath(fileLocation) {
const currentExt = path.extname(fileLocation);
let newExtension = this.options.rewriteRelativeImportExtensions ? currentExt : '';
if (this.options.esm && !this.options.rewriteRelativeImportExtensions) {
switch (currentExt) {
case '.ts':
default:
newExtension = '.js';
break;
case '.mts':
newExtension = '.mjs';
break;
case '.cts':
newExtension = '.cjs';
break;
}
}
fileLocation = fileLocation.replace(/\.(ts|mts|cts)$/, ''); // no ts extension in import
return `./${path.relative(this.options.routesDir, fileLocation).replace(/\\/g, '/')}${newExtension}`;
}
buildPropertySchema(source) {
const propertySchema = this.buildProperty(source.type);
propertySchema.default = source.default;
propertySchema.required = source.required ? true : undefined;
if (Object.keys(source.validators).length > 0) {
propertySchema.validators = source.validators;
}
return propertySchema;
}
buildParameterSchema(source) {
const property = this.buildProperty(source.type);
const parameter = {
default: source.default,
in: source.in,
name: source.name,
required: source.required ? true : undefined,
};
const parameterSchema = Object.assign(parameter, property);
if (Object.keys(source.validators).length > 0) {
parameterSchema.validators = source.validators;
}
return parameterSchema;
}
buildProperty(type) {
const schema = {
dataType: type.dataType,
};
if ((0, internalTypeGuards_1.isRefType)(type)) {
schema.dataType = undefined;
schema.ref = type.refName;
}
if (type.dataType === 'array') {
const arrayType = type;
if ((0, internalTypeGuards_1.isRefType)(arrayType.elementType)) {
schema.array = {
dataType: arrayType.elementType.dataType,
ref: arrayType.elementType.refName,
};
}
else {
schema.array = this.buildProperty(arrayType.elementType);
}
}
if (type.dataType === 'enum') {
schema.enums = type.enums;
}
if (type.dataType === 'union' || type.dataType === 'intersection') {
schema.subSchemas = type.types.map(type => this.buildProperty(type));
}
if (type.dataType === 'nestedObjectLiteral') {
const objLiteral = type;
schema.nestedProperties = objLiteral.properties.reduce((acc, prop) => {
return { ...acc, [prop.name]: this.buildPropertySchema(prop) };
}, {});
schema.additionalProperties = objLiteral.additionalProperties && this.buildProperty(objLiteral.additionalProperties);
}
return schema;
}
async shouldWriteFile(fileName, content) {
if (this.options.noWriteIfUnchanged) {
if (await (0, fs_1.fsExists)(fileName)) {
const existingContent = (await (0, fs_1.fsReadFile)(fileName)).toString();
return content !== existingContent;
}
}
return true;
}
}
exports.AbstractRouteGenerator = AbstractRouteGenerator;
//# sourceMappingURL=routeGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,221 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, ExpressTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { expressAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import type { Request as ExRequest, Response as ExResponse, RequestHandler, Router } from 'express';
{{#if useFileUploads}}
{{#if esm}}
import multer from 'multer';
{{else}}
const multer = require('multer');
{{/if}}
{{/if}}
{{#if authenticationModule}}
const expressAuthenticationRecasted = expressAuthentication as (req: ExRequest, securityName: string, scopes?: string[], res?: ExResponse) => Promise<any>;
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new ExpressTemplateService(models, {{{ json minimalSwaggerConfig}}});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useFileUploads}}
export function RegisterRoutes(app: Router,opts?:{multer?:ReturnType<typeof multer>}) {
{{else}}
export function RegisterRoutes(app: Router) {
{{/if}}
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#if useFileUploads}}
const upload = opts?.multer || multer({{{json multerOpts}}});
{{/if}}
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
app.{{method}}('{{fullPath}}',
{{#if security.length}}
authenticateMiddleware({{json security}}),
{{/if}}
{{#if uploadFile}}
upload.fields([
{{#each uploadFileName}}
{
name: {{json name}},
{{#if maxCount}}
maxCount: {{maxCount}}
{{/if}}
}{{#if @last}}{{else}},{{/if}}
{{/each}}
]),
{{/if}}
...(fetchMiddlewares<RequestHandler>({{../name}})),
...(fetchMiddlewares<RequestHandler>({{../name}}.prototype.{{name}})),
async function {{../name}}_{{name}}(request: ExRequest, response: ExResponse, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, request, response });
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
await templateService.apiHandler({
methodName: '{{name}}',
controller,
response,
next,
validatedArgs,
successStatus: {{successStatus}},
});
} catch (err) {
return next(err);
}
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(request: any, response: any, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
try {
request['user'] = await Promise.any(secMethodOrPromises);
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next();
}
catch(err) {
// Show most recent error as response
const error = failedAttempts.pop();
error.status = error.status || 401;
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next(error);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

View File

@@ -0,0 +1,267 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, HapiTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { hapiAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import { boomify, isBoom, type Payload } from '@hapi/boom';
import type { Request, ResponseToolkit, RouteOptionsPreAllOptions } from '@hapi/hapi';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new HapiTemplateService(
models,
{{{ json minimalSwaggerConfig }}},
{ boomify, isBoom },
);
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
export function RegisterRoutes(server: any) {
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
server.route({
method: '{{method}}',
path: '{{fullPath}}',
options: {
pre: [
{{#if security.length}}
{
method: authenticateMiddleware({{json security}})
},
{{/if}}
{{#if uploadFile}}
{{#each uploadFileName}}
{
{{#if multiple}}
method: fileUploadMiddleware('{{name}}', true)
{{else}}
method: fileUploadMiddleware('{{name}}', false)
{{/if}}
},
{{/each}}
{{/if}}
...(fetchMiddlewares<RouteOptionsPreAllOptions>({{../name}})),
...(fetchMiddlewares<RouteOptionsPreAllOptions>({{../name}}.prototype.{{name}})),
],
{{#if uploadFile}}
payload: {
output: 'stream',
parse: true,
multipart: true,
allow: 'multipart/form-data'
},
{{/if}}
handler: {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: Request, h: ResponseToolkit) {
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, request, h });
} catch (err) {
const error = err as any;
if (isBoom(error)) {
throw error;
}
const boomErr = boomify(error instanceof Error ? error : new Error(error.message), { statusCode: error.status || 500 });
boomErr.output.statusCode = error.status || 500;
boomErr.output.payload = {
name: error.name,
fields: error.fields,
message: error.message,
} as unknown as Payload;
throw boomErr;
}
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
return templateService.apiHandler({
methodName: '{{name}}',
controller,
h,
validatedArgs,
successStatus: {{successStatus}},
});
}
}
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(request: any, h: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
hapiAuthentication(request, name, secMethod[name])
.catch(pushAndRethrow)
);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
hapiAuthentication(request, name, secMethod[name])
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
try {
request['user'] = await Promise.any(secMethodOrPromises);
return request['user'];
}
catch(err) {
// Show most recent error as response
const error = failedAttempts.pop();
if (isBoom(error)) {
throw error;
}
const boomErr = boomify(error instanceof Error ? error : new Error(error.message), { statusCode: error.status || 500 });
boomErr.output.statusCode = error.status || 401;
boomErr.output.payload = {
name: error.name,
message: error.message,
} as unknown as Payload;
throw boomErr;
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
{{#if useFileUploads}}
function fileUploadMiddleware(fieldname: string, multiple: boolean = false) {
return (request: Request, h: any) => {
if (!request.payload[fieldname]) {
return h.response(`${fieldname} is a required file(s).`).code(400);
}
const calculateFileInfo = (reqFile: any) => new Promise((resolve, reject) => {
const originalname = reqFile.hapi.filename;
const headers = reqFile.hapi.headers;
const contentTransferEncoding = headers['content-transfer-encoding'];
const encoding = contentTransferEncoding &&
contentTransferEncoding[0] &&
contentTransferEncoding[0].toLowerCase() || '7bit';
const mimetype = headers['content-type'] || 'text/plain';
const buffer = reqFile._data
return resolve({
fieldname,
originalname,
buffer,
encoding,
mimetype,
filename: originalname,
size: buffer.toString().length,
})
});
if (!multiple) {
return calculateFileInfo(request.payload[fieldname])
.then(fileMetadata => {
request.payload[fieldname] = fileMetadata;
return h.continue;
})
.catch(err => h.response(err.toString()).code(500));
} else {
const promises = request.payload[fieldname].map((reqFile: any) => calculateFileInfo(reqFile));
return Promise.all(promises)
.then(filesMetadata => {
request.payload[fieldname] = filesMetadata;
return h.continue;
})
.catch(err => h.response(err.toString()).code(500));
}
};
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

View File

@@ -0,0 +1,218 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, KoaTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { koaAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import type { Context, Next, Middleware, Request as KRequest, Response as KResponse } from 'koa';
import type * as KoaRouter from '@koa/router';
{{#if useFileUploads}}
{{#if esm}}
import multer from '@koa/multer';
{{else}}
const multer = require('@koa/multer');
{{/if}}
{{/if}}
{{#if authenticationModule}}
const koaAuthenticationRecasted = koaAuthentication as (req: KRequest, securityName: string, scopes?: string[], res?: KResponse) => Promise<any>;
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new KoaTemplateService(models, {{{ json minimalSwaggerConfig }}});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useFileUploads}}
export function RegisterRoutes(router: KoaRouter,opts?:{multer?:ReturnType<typeof multer>}) {
{{else}}
export function RegisterRoutes(router: KoaRouter) {
{{/if}}
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#if useFileUploads}}
const upload = opts?.multer || multer({{{json multerOpts}}});
{{/if}}
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
router.{{method}}('{{fullPath}}',
{{#if security.length}}
authenticateMiddleware({{json security}}),
{{/if}}
{{#if uploadFile}}
upload.fields([
{{#each uploadFileName}}
{
name: {{json name}},
{{#if maxCount}}
maxCount: {{maxCount}}
{{/if}}
}{{#if @last}}{{else}},{{/if}}
{{/each}}
]),
{{/if}}
...(fetchMiddlewares<Middleware>({{../name}})),
...(fetchMiddlewares<Middleware>({{../name}}.prototype.{{name}})),
async function {{../name}}_{{name}}(context: Context, next: Next) {
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, context, next });
} catch (err) {
const error = err as any;
error.message ||= JSON.stringify({ fields: error.fields });
context.status = error.status;
context.throw(context.status, error.message, error);
}
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(context.request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
return templateService.apiHandler({
methodName: '{{name}}',
controller,
context,
validatedArgs,
successStatus: {{successStatus}},
});
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(context: any, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
koaAuthenticationRecasted(context.request, name, secMethod[name], context.response)
.catch(pushAndRethrow)
);
}
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
koaAuthenticationRecasted(context.request, name, secMethod[name], context.response)
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
let success;
try {
const user = await Promise.any(secMethodOrPromises);
success = true;
context.request['user'] = user;
}
catch(err) {
// Response was sent in middleware, abort
if(context.response.body) {
return;
}
// Show most recent error as response
const error = failedAttempts.pop();
context.status = error.status || 401;
context.throw(context.status, error.message, error);
}
// Response was sent in middleware, abort
if(context.response.body) {
return;
}
if (success) {
await next();
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

33
node_modules/@tsoa/cli/dist/swagger/specGenerator.d.ts generated vendored Normal file
View File

@@ -0,0 +1,33 @@
import { ExtendedSpecConfig } from '../cli';
import { Tsoa, Swagger } from '@tsoa/runtime';
export declare abstract class SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
protected buildAdditionalProperties(type: Tsoa.Type): Swagger.BaseSchema<unknown>;
protected buildOperationIdTemplate(inlineTemplate: string): HandlebarsTemplateDelegate<any>;
protected getOperationId(controllerName: string, method: Tsoa.Method): string;
throwIfNotDataFormat(strToTest: string): Swagger.DataFormat;
throwIfNotDataType(strToTest: string): Swagger.DataType;
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
protected abstract getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): Swagger.BaseSchema;
protected abstract getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): Swagger.BaseSchema;
protected abstract buildProperties(properties: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema2;
} | {
[propertyName: string]: Swagger.Schema3;
} | {
[propertyName: string]: Swagger.Schema31;
};
getSwaggerTypeForObjectLiteral(objectLiteral: Tsoa.NestedObjectLiteralType, title?: string): Swagger.BaseSchema;
protected getSwaggerTypeForReferenceType(_referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
protected getSwaggerTypeForVoid(_dataType: 'void' | 'undefined'): Swagger.BaseSchema;
protected determineImplicitAdditionalPropertiesValue: () => boolean;
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
protected getSwaggerTypeForArrayType(arrayType: Tsoa.ArrayType, title?: string): Swagger.BaseSchema;
protected determineTypesUsedInEnum(anEnum: Array<string | number | boolean | null>): Set<"string" | "number" | "boolean">;
protected abstract getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema2 | Swagger.Schema3;
protected hasUndefined(property: Tsoa.Property): boolean;
protected queriesPropertyToQueryParameter(property: Tsoa.Property): Tsoa.Parameter;
protected isRequiredWithoutDefault(prop: Tsoa.Property | Tsoa.Parameter): boolean | undefined;
}

251
node_modules/@tsoa/cli/dist/swagger/specGenerator.js generated vendored Normal file
View File

@@ -0,0 +1,251 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator = void 0;
const runtime_1 = require("@tsoa/runtime");
const handlebars = __importStar(require("handlebars"));
class SpecGenerator {
constructor(metadata, config) {
this.metadata = metadata;
this.config = config;
this.determineImplicitAdditionalPropertiesValue = () => {
if (this.config.noImplicitAdditionalProperties === 'silently-remove-extras') {
return false;
}
else if (this.config.noImplicitAdditionalProperties === 'throw-on-extras') {
return false;
}
else if (this.config.noImplicitAdditionalProperties === 'ignore') {
return true;
}
else {
return (0, runtime_1.assertNever)(this.config.noImplicitAdditionalProperties);
}
};
}
buildAdditionalProperties(type) {
return this.getSwaggerType(type);
}
buildOperationIdTemplate(inlineTemplate) {
handlebars.registerHelper('titleCase', (value) => (value ? value.charAt(0).toUpperCase() + value.slice(1) : value));
handlebars.registerHelper('replace', (subject, searchValue, withValue = '') => (subject ? subject.replace(searchValue, withValue) : subject));
return handlebars.compile(inlineTemplate, { noEscape: true });
}
getOperationId(controllerName, method) {
return this.buildOperationIdTemplate(this.config.operationIdTemplate ?? '{{titleCase method.name}}')({
method,
controllerName,
});
}
throwIfNotDataFormat(strToTest) {
const guiltyUntilInnocent = strToTest;
if (guiltyUntilInnocent === 'int32' ||
guiltyUntilInnocent === 'int64' ||
guiltyUntilInnocent === 'float' ||
guiltyUntilInnocent === 'double' ||
guiltyUntilInnocent === 'byte' ||
guiltyUntilInnocent === 'binary' ||
guiltyUntilInnocent === 'date' ||
guiltyUntilInnocent === 'date-time' ||
guiltyUntilInnocent === 'password') {
return guiltyUntilInnocent;
}
else {
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
}
}
throwIfNotDataType(strToTest) {
const guiltyUntilInnocent = strToTest;
if (guiltyUntilInnocent === 'array' ||
guiltyUntilInnocent === 'boolean' ||
guiltyUntilInnocent === 'integer' ||
guiltyUntilInnocent === 'file' ||
guiltyUntilInnocent === 'number' ||
guiltyUntilInnocent === 'object' ||
guiltyUntilInnocent === 'string' ||
guiltyUntilInnocent === 'undefined') {
return guiltyUntilInnocent;
}
else {
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
}
}
getSwaggerType(type, title) {
if (type.dataType === 'void' || type.dataType === 'undefined') {
return this.getSwaggerTypeForVoid(type.dataType);
}
else if (type.dataType === 'refEnum' || type.dataType === 'refObject' || type.dataType === 'refAlias') {
return this.getSwaggerTypeForReferenceType(type);
}
else if (type.dataType === 'any' ||
type.dataType === 'binary' ||
type.dataType === 'boolean' ||
type.dataType === 'buffer' ||
type.dataType === 'byte' ||
type.dataType === 'date' ||
type.dataType === 'datetime' ||
type.dataType === 'double' ||
type.dataType === 'float' ||
type.dataType === 'file' ||
type.dataType === 'integer' ||
type.dataType === 'long' ||
type.dataType === 'object' ||
type.dataType === 'string') {
return this.getSwaggerTypeForPrimitiveType(type.dataType);
}
else if (type.dataType === 'array') {
return this.getSwaggerTypeForArrayType(type, title);
}
else if (type.dataType === 'enum') {
return this.getSwaggerTypeForEnumType(type, title);
}
else if (type.dataType === 'union') {
return this.getSwaggerTypeForUnionType(type, title);
}
else if (type.dataType === 'intersection') {
return this.getSwaggerTypeForIntersectionType(type, title);
}
else if (type.dataType === 'nestedObjectLiteral') {
return this.getSwaggerTypeForObjectLiteral(type, title);
}
else if (type.dataType === 'tuple') {
throw new Error('Tuple types are only supported in OpenAPI 3.1+');
}
else {
return (0, runtime_1.assertNever)(type);
}
}
getSwaggerTypeForObjectLiteral(objectLiteral, title) {
const properties = this.buildProperties(objectLiteral.properties);
const additionalProperties = objectLiteral.additionalProperties && this.getSwaggerType(objectLiteral.additionalProperties);
const required = objectLiteral.properties.filter(prop => this.isRequiredWithoutDefault(prop) && !this.hasUndefined(prop)).map(prop => prop.name);
// An empty list required: [] is not valid.
// If all properties are optional, do not specify the required keyword.
return {
...(title && { title }),
properties,
...(additionalProperties && { additionalProperties }),
...(required && required.length && { required }),
type: 'object',
};
}
getSwaggerTypeForReferenceType(_referenceType) {
return {
// Don't set additionalProperties value here since it will be set within the $ref's model when that $ref gets created
};
}
getSwaggerTypeForVoid(_dataType) {
// Described here: https://swagger.io/docs/specification/describing-responses/#empty
const voidSchema = {
// isn't allowed to have additionalProperties at all (meaning not a boolean or object)
};
return voidSchema;
}
getSwaggerTypeForPrimitiveType(dataType) {
if (dataType === 'object') {
if (process.env.NODE_ENV !== 'tsoa_test') {
// eslint-disable-next-line no-console
console.warn(`The type Object is discouraged. Please consider using an interface such as:
export interface IStringToStringDictionary {
[key: string]: string;
}
// or
export interface IRecordOfAny {
[key: string]: any;
}
`);
}
}
const map = {
any: {
// While the any type is discouraged, it does explicitly allows anything, so it should always allow additionalProperties
additionalProperties: true,
},
binary: { type: 'string', format: 'binary' },
boolean: { type: 'boolean' },
buffer: { type: 'string', format: 'byte' },
byte: { type: 'string', format: 'byte' },
date: { type: 'string', format: 'date' },
datetime: { type: 'string', format: 'date-time' },
double: { type: 'number', format: 'double' },
file: { type: 'file' },
float: { type: 'number', format: 'float' },
integer: { type: 'integer', format: 'int32' },
long: { type: 'integer', format: 'int64' },
object: {
additionalProperties: this.determineImplicitAdditionalPropertiesValue(),
type: 'object',
},
string: { type: 'string' },
};
return map[dataType];
}
getSwaggerTypeForArrayType(arrayType, title) {
return {
items: this.getSwaggerType(arrayType.elementType, title),
type: 'array',
};
}
determineTypesUsedInEnum(anEnum) {
const typesUsedInEnum = anEnum.reduce((theSet, curr) => {
const typeUsed = curr === null ? 'number' : typeof curr;
theSet.add(typeUsed);
return theSet;
}, new Set());
return typesUsedInEnum;
}
hasUndefined(property) {
return property.type.dataType === 'undefined' || (property.type.dataType === 'union' && property.type.types.some(type => type.dataType === 'undefined'));
}
queriesPropertyToQueryParameter(property) {
return {
parameterName: property.name,
example: [property.example],
description: property.description,
in: 'query',
name: property.name,
required: this.isRequiredWithoutDefault(property),
type: property.type,
default: property.default,
validators: property.validators,
deprecated: property.deprecated,
};
}
isRequiredWithoutDefault(prop) {
return prop.required && prop.default == null;
}
}
exports.SpecGenerator = SpecGenerator;
//# sourceMappingURL=specGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
import { Tsoa, Swagger } from '@tsoa/runtime';
import { SpecGenerator } from './specGenerator';
import { ExtendedSpecConfig } from '../cli';
export declare class SpecGenerator2 extends SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec2;
private buildDefinitions;
private buildPaths;
private buildMethod;
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation;
private buildBodyPropParameter;
private buildQueriesParameter;
private buildParameter;
protected buildProperties(source: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema2;
};
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType): Swagger.BaseSchema<unknown>;
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType): {
type: string;
properties: {};
};
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
private decideEnumType;
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType): Swagger.Schema2;
}

464
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js generated vendored Normal file
View File

@@ -0,0 +1,464 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator2 = void 0;
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const runtime_1 = require("@tsoa/runtime");
const specGenerator_1 = require("./specGenerator");
const isVoidType_1 = require("../utils/isVoidType");
const pathUtils_1 = require("../utils/pathUtils");
const swaggerUtils_1 = require("../utils/swaggerUtils");
const validatorUtils_1 = require("../utils/validatorUtils");
class SpecGenerator2 extends specGenerator_1.SpecGenerator {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
GetSpec() {
let spec = {
basePath: (0, pathUtils_1.normalisePath)(this.config.basePath, '/', undefined, false),
consumes: [swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE],
definitions: this.buildDefinitions(),
info: {
title: '',
},
paths: this.buildPaths(),
produces: [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE],
swagger: '2.0',
};
const securityDefinitions = this.config.securityDefinitions ? this.config.securityDefinitions : {};
const supportedSchemes = ['basic', 'apiKey', 'oauth2'];
for (const { type } of Object.values(securityDefinitions)) {
if (!supportedSchemes.includes(type)) {
throw new Error(`Swagger 2.0 does not support "${type}" security scheme (allowed values: ${supportedSchemes.join(',')})`);
}
}
spec.securityDefinitions = securityDefinitions;
if (this.config.name) {
spec.info.title = this.config.name;
}
if (this.config.version) {
spec.info.version = this.config.version;
}
if (this.config.host) {
spec.host = this.config.host;
}
if (this.config.description) {
spec.info.description = this.config.description;
}
if (this.config.termsOfService) {
spec.info.termsOfService = this.config.termsOfService;
}
if (this.config.tags) {
spec.tags = this.config.tags;
}
if (this.config.license) {
spec.info.license = { name: this.config.license };
}
if (this.config.contact) {
spec.info.contact = this.config.contact;
}
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
if (this.config.schemes) {
spec.schemes = this.config.schemes;
}
return spec;
}
buildDefinitions() {
const definitions = {};
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
const referenceType = this.metadata.referenceTypeMap[typeName];
if (referenceType.dataType === 'refObject') {
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
definitions[referenceType.refName] = {
description: referenceType.description,
properties: this.buildProperties(referenceType.properties),
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
type: 'object',
};
if (referenceType.additionalProperties) {
definitions[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
}
else {
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
// ...we need to make a decision
definitions[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
}
if (referenceType.example) {
definitions[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refEnum') {
definitions[referenceType.refName] = {
description: referenceType.description,
enum: referenceType.enums,
type: this.decideEnumType(referenceType.enums, referenceType.refName),
};
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
definitions[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
}
if (referenceType.example) {
definitions[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refAlias') {
const swaggerType = this.getSwaggerType(referenceType.type);
const format = referenceType.format;
const validators = Object.keys(referenceType.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: referenceType.validators[key].value,
};
}, {});
definitions[referenceType.refName] = {
...swaggerType,
default: referenceType.default || swaggerType.default,
example: referenceType.example,
format: format || swaggerType.format,
description: referenceType.description,
...validators,
};
}
else {
(0, runtime_1.assertNever)(referenceType);
}
if (referenceType.deprecated) {
definitions[referenceType.refName]['x-deprecated'] = true;
}
});
return definitions;
}
buildPaths() {
const paths = {};
this.metadata.controllers.forEach(controller => {
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
// construct documentation using all methods except @Hidden
controller.methods
.filter(method => !method.isHidden)
.forEach(method => {
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
path = (0, pathUtils_1.convertColonPathParams)(path);
paths[path] = paths[path] || {};
this.buildMethod(controller.name, method, paths[path], controller.produces);
});
});
return paths;
}
buildMethod(controllerName, method, pathObject, defaultProduces) {
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
pathMethod.description = method.description;
pathMethod.summary = method.summary;
pathMethod.tags = method.tags;
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
pathMethod.operationId = method.operationId || pathMethod.operationId;
if (method.deprecated) {
pathMethod.deprecated = method.deprecated;
}
if (method.security) {
pathMethod.security = method.security;
}
const queriesParams = method.parameters.filter(p => p.in === 'queries');
pathMethod.parameters = method.parameters
.filter(p => {
return ['request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
})
.map(p => this.buildParameter(p));
if (queriesParams.length > 1) {
throw new Error('Only one queries parameter allowed per controller method.');
}
if (queriesParams.length === 1) {
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
}
const bodyPropParameter = this.buildBodyPropParameter(controllerName, method);
if (bodyPropParameter) {
pathMethod.parameters.push(bodyPropParameter);
}
if (pathMethod.parameters.filter((p) => p.in === 'body').length > 1) {
throw new Error('Only one body parameter allowed per controller method.');
}
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
}
buildOperation(controllerName, method, defaultProduces) {
const swaggerResponses = {};
let produces = [];
method.responses.forEach((res) => {
swaggerResponses[res.name] = {
description: res.description,
};
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
if (res.produces) {
produces.push(...res.produces);
}
swaggerResponses[res.name].schema = this.getSwaggerType(res.schema);
}
if (res.examples && res.examples[0]) {
if ((res.exampleLabels?.filter(e => e).length || 0) > 0) {
console.warn('Example labels are not supported in OpenAPI 2');
}
swaggerResponses[res.name].examples = { [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE]: res.examples[0] };
}
if (res.headers) {
const headers = {};
if (res.headers.dataType === 'refObject' || res.headers.dataType === 'nestedObjectLiteral') {
res.headers.properties.forEach((each) => {
headers[each.name] = {
...this.getSwaggerType(each.type),
description: each.description,
};
});
}
else {
(0, runtime_1.assertNever)(res.headers);
}
swaggerResponses[res.name].headers = headers;
}
});
produces = Array.from(new Set(produces.filter(p => p !== undefined)));
if (produces.length === 0) {
produces = defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
}
const operation = {
operationId: this.getOperationId(controllerName, method),
produces: produces,
responses: swaggerResponses,
};
const hasBody = method.parameters.some(p => p.in === 'body');
const hasFormData = method.parameters.some(p => p.in === 'formData');
if (hasBody || hasFormData) {
operation.consumes = [];
if (hasBody) {
operation.consumes.push(method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE);
}
if (hasFormData) {
operation.consumes.push('multipart/form-data');
}
}
return operation;
}
buildBodyPropParameter(controllerName, method) {
const properties = {};
const required = [];
method.parameters
.filter(p => p.in === 'body-prop')
.forEach(p => {
properties[p.name] = this.getSwaggerType(p.type);
properties[p.name].default = p.default;
properties[p.name].description = p.description;
properties[p.name].example = p.example === undefined ? undefined : p.example[0];
if (this.isRequiredWithoutDefault(p)) {
required.push(p.name);
}
});
if (!Object.keys(properties).length) {
return;
}
const parameter = {
in: 'body',
name: 'body',
schema: {
properties,
title: `${this.getOperationId(controllerName, method)}Body`,
type: 'object',
},
};
if (required.length) {
parameter.schema.required = required;
}
return parameter;
}
buildQueriesParameter(source) {
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
const properties = source.type.properties;
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
}
throw new Error(`Queries '${source.name}' parameter must be an object.`);
}
buildParameter(source) {
let type = source.type;
if (source.in !== 'body' && source.type.dataType === 'refEnum') {
// swagger does not support referencing enums
// (except for body parameters), so we have to inline it
type = {
dataType: 'enum',
enums: source.type.enums,
};
}
const parameterType = this.getSwaggerType(type);
let parameter = {
default: source.default,
description: source.description,
in: source.in,
name: source.name,
required: this.isRequiredWithoutDefault(source),
...(source.deprecated ? { 'x-deprecated': true } : {}),
...(parameterType.$ref ? { schema: parameterType } : {}),
...(parameterType.format ? { format: this.throwIfNotDataFormat(parameterType.format) } : {}),
};
if (runtime_1.Swagger.isQueryParameter(parameter) && parameterType.type === 'array') {
parameter.collectionFormat = 'multi';
}
if (parameter.schema) {
return parameter;
}
const validatorObjs = {};
Object.keys(source.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
validatorObjs[key] = source.validators[key].value;
});
if (source.in === 'body' && source.type.dataType === 'array') {
parameter.schema = {
items: parameterType.items,
type: 'array',
};
}
else {
if (source.type.dataType === 'any') {
if (source.in === 'body') {
parameter.schema = { type: 'object' };
}
else {
parameter.type = 'string';
}
}
else {
if (parameterType.type) {
parameter.type = this.throwIfNotDataType(parameterType.type);
}
parameter.items = parameterType.items;
parameter.enum = parameterType.enum;
}
}
if (parameter.schema) {
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
}
else {
parameter = Object.assign({}, parameter, validatorObjs);
}
return parameter;
}
buildProperties(source) {
const properties = {};
source.forEach(property => {
let swaggerType = this.getSwaggerType(property.type);
const format = property.format;
swaggerType.description = property.description;
swaggerType.example = property.example;
swaggerType.format = format || swaggerType.format;
if (!swaggerType.$ref) {
swaggerType.default = property.default;
Object.keys(property.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
});
}
if (property.deprecated) {
swaggerType['x-deprecated'] = true;
}
if (property.extensions) {
property.extensions.forEach(property => {
swaggerType[property.key] = property.value;
});
}
properties[property.name] = swaggerType;
});
return properties;
}
getSwaggerTypeForUnionType(type) {
const typesWithoutUndefined = type.types.filter(x => x.dataType !== 'undefined');
// Backwards compatible representation of a literal enumeration
if (typesWithoutUndefined.every(subType => subType.dataType === 'enum')) {
const mergedEnum = { dataType: 'enum', enums: [] };
typesWithoutUndefined.forEach(t => {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
mergedEnum.enums = [...mergedEnum.enums, ...t.enums];
});
return this.getSwaggerTypeForEnumType(mergedEnum);
}
else if (typesWithoutUndefined.length === 2 && typesWithoutUndefined.find(typeInUnion => typeInUnion.dataType === 'enum' && typeInUnion.enums.includes(null))) {
// Backwards compatible representation of dataType or null, $ref does not allow any sibling attributes, so we have to bail out
const nullEnumIndex = typesWithoutUndefined.findIndex(type => type.dataType === 'enum' && type.enums.includes(null));
const typeIndex = nullEnumIndex === 1 ? 0 : 1;
const swaggerType = this.getSwaggerType(typesWithoutUndefined[typeIndex]);
const isRef = !!swaggerType.$ref;
if (isRef) {
return { type: 'object' };
}
else {
swaggerType['x-nullable'] = true;
return swaggerType;
}
}
else if (process.env.NODE_ENV !== 'tsoa_test') {
// eslint-disable-next-line no-console
console.warn('Swagger 2.0 does not support union types beyond string literals.\n' + 'If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
}
return { type: 'object' };
}
getSwaggerTypeForIntersectionType(type) {
const properties = type.types.reduce((acc, type) => {
if (type.dataType === 'refObject') {
let refType = type;
refType = this.metadata.referenceTypeMap[refType.refName];
const props = refType &&
refType.properties &&
refType.properties.reduce((acc, prop) => {
return {
...acc,
[prop.name]: this.getSwaggerType(prop.type),
};
}, {});
return { ...acc, ...props };
}
else {
process.env.NODE_ENV !== 'tsoa_test' &&
// eslint-disable-next-line no-console
console.warn('Swagger 2.0 does not fully support this kind of intersection types. If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
return { ...acc };
}
}, {});
return { type: 'object', properties };
}
getSwaggerTypeForReferenceType(referenceType) {
return { $ref: `#/definitions/${encodeURIComponent(referenceType.refName)}` };
}
decideEnumType(anEnum, nameOfEnum) {
const typesUsedInEnum = this.determineTypesUsedInEnum(anEnum);
const badEnumErrorMessage = () => {
const valuesDelimited = Array.from(typesUsedInEnum).join(',');
return `Enums can only have string or number values, but enum ${nameOfEnum} had ${valuesDelimited}`;
};
let enumTypeForSwagger;
if (typesUsedInEnum.has('string') && typesUsedInEnum.size === 1) {
enumTypeForSwagger = 'string';
}
else if (typesUsedInEnum.has('number') && typesUsedInEnum.size === 1) {
enumTypeForSwagger = 'number';
}
else {
throw new Error(badEnumErrorMessage());
}
return enumTypeForSwagger;
}
getSwaggerTypeForEnumType(enumType) {
const types = this.determineTypesUsedInEnum(enumType.enums);
const type = types.size === 1 ? types.values().next().value : 'string';
const nullable = enumType.enums.includes(null) ? true : false;
return { type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), ['x-nullable']: nullable };
}
}
exports.SpecGenerator2 = SpecGenerator2;
//# sourceMappingURL=specGenerator2.js.map

File diff suppressed because one or more lines are too long

158
node_modules/@tsoa/cli/dist/swagger/specGenerator3.d.ts generated vendored Normal file
View File

@@ -0,0 +1,158 @@
import { Swagger, Tsoa } from '@tsoa/runtime';
import { ExtendedSpecConfig } from '../cli';
import { SpecGenerator } from './specGenerator';
/**
* TODO:
* Handle formData parameters
* Handle requestBodies of type other than json
* Handle requestBodies as reusable objects
* Handle headers, examples, responses, etc.
* Cleaner interface between SpecGenerator2 and SpecGenerator3
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
*/
export declare class SpecGenerator3 extends SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec3;
protected buildInfo(): Swagger.Info;
protected buildComponents(): {
examples: {};
headers: {};
parameters: {};
requestBodies: {};
responses: {};
schemas: {
[name: string]: Swagger.Schema3;
};
securitySchemes: {};
};
protected translateSecurityDefinitions(definitions: {
[name: string]: Swagger.SecuritySchemes;
}): {
[name: string]: Swagger.SecuritySchemes;
};
protected hasOAuthFlow(definition: any): definition is {
flow: string;
};
protected hasOAuthFlows(definition: any): definition is {
flows: Swagger.OAuthFlow;
};
protected buildServers(): Swagger.Server[];
protected buildSchema(): {
[name: string]: Swagger.Schema3;
};
protected buildPaths(): {
[pathName: string]: Swagger.Path3;
};
protected buildMethod(controllerName: string, method: Tsoa.Method, pathObject: any, defaultProduces?: string[]): void;
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation3;
protected buildRequestBodyWithFormData(controllerName: string, method: Tsoa.Method, parameters: Tsoa.Parameter[]): Swagger.RequestBody;
protected buildRequestBody(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.RequestBody;
protected buildMediaType(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.MediaType;
protected buildQueriesParameter(source: Tsoa.Parameter): Swagger.Parameter3[];
protected buildParameter(source: Tsoa.Parameter): Swagger.Parameter3;
protected buildExamples(source: Pick<Tsoa.Parameter, 'example' | 'exampleLabels'>): {
example?: unknown;
examples?: {
[name: string]: Swagger.Example3;
};
};
protected buildProperties(source: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema3;
};
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
protected isNull(type: Tsoa.Type): boolean;
protected groupEnums(types: Swagger.BaseSchema[]): Swagger.BaseSchema<unknown>[];
protected removeDuplicateSwaggerTypes(types: Swagger.BaseSchema[]): Swagger.BaseSchema[];
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): {
allOf: Swagger.BaseSchema<unknown>[];
nullable: true;
} | {
nullable: true;
type?: string;
format?: Swagger.DataFormat;
$ref?: string;
title?: string;
description?: string;
default?: string | boolean | number | unknown;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
enum?: Array<boolean | string | number | null>;
'x-enum-varnames'?: string[];
additionalProperties?: boolean | Swagger.BaseSchema;
properties?: {
[propertyName: string]: unknown;
} | undefined;
discriminator?: string;
readOnly?: boolean;
xml?: Swagger.XML;
externalDocs?: Swagger.ExternalDocs;
example?: unknown;
required?: string[];
items?: Swagger.BaseSchema;
allOf?: undefined;
} | {
anyOf: Swagger.BaseSchema<unknown>[];
nullable: true;
title?: string | undefined;
allOf?: undefined;
} | {
type?: string;
format?: Swagger.DataFormat;
$ref?: string;
title?: string;
description?: string;
default?: string | boolean | number | unknown;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
enum?: Array<boolean | string | number | null>;
'x-enum-varnames'?: string[];
additionalProperties?: boolean | Swagger.BaseSchema;
properties?: {
[propertyName: string]: unknown;
} | undefined;
discriminator?: string;
readOnly?: boolean;
xml?: Swagger.XML;
externalDocs?: Swagger.ExternalDocs;
example?: unknown;
required?: string[];
items?: Swagger.BaseSchema;
allOf?: undefined;
nullable?: undefined;
} | {
anyOf: Swagger.BaseSchema<unknown>[];
title?: string | undefined;
allOf?: undefined;
nullable?: undefined;
};
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): {
title?: string | undefined;
allOf: Swagger.BaseSchema<unknown>[];
};
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema3;
}

633
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js generated vendored Normal file
View File

@@ -0,0 +1,633 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator3 = void 0;
const runtime_1 = require("@tsoa/runtime");
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const isVoidType_1 = require("../utils/isVoidType");
const validatorUtils_1 = require("../utils/validatorUtils");
const pathUtils_1 = require("./../utils/pathUtils");
const swaggerUtils_1 = require("./../utils/swaggerUtils");
const specGenerator_1 = require("./specGenerator");
/**
* TODO:
* Handle formData parameters
* Handle requestBodies of type other than json
* Handle requestBodies as reusable objects
* Handle headers, examples, responses, etc.
* Cleaner interface between SpecGenerator2 and SpecGenerator3
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
*/
class SpecGenerator3 extends specGenerator_1.SpecGenerator {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
GetSpec() {
let spec = {
openapi: '3.0.0',
components: this.buildComponents(),
info: this.buildInfo(),
paths: this.buildPaths(),
servers: this.buildServers(),
tags: this.config.tags,
};
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
return spec;
}
buildInfo() {
const info = {
title: this.config.name || '',
};
if (this.config.version) {
info.version = this.config.version;
}
if (this.config.description) {
info.description = this.config.description;
}
if (this.config.termsOfService) {
info.termsOfService = this.config.termsOfService;
}
if (this.config.license) {
info.license = { name: this.config.license };
}
if (this.config.contact) {
info.contact = this.config.contact;
}
return info;
}
buildComponents() {
const components = {
examples: {},
headers: {},
parameters: {},
requestBodies: {},
responses: {},
schemas: this.buildSchema(),
securitySchemes: {},
};
if (this.config.securityDefinitions) {
components.securitySchemes = this.translateSecurityDefinitions(this.config.securityDefinitions);
}
return components;
}
translateSecurityDefinitions(definitions) {
const defs = {};
Object.keys(definitions).forEach(key => {
if (definitions[key].type === 'basic') {
defs[key] = {
scheme: 'basic',
type: 'http',
};
}
else if (definitions[key].type === 'oauth2') {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const definition = definitions[key];
const oauth = (defs[key] || {
type: 'oauth2',
description: definitions[key].description,
flows: (this.hasOAuthFlows(definition) && definition.flows) || {},
});
if (this.hasOAuthFlow(definition) && definition.flow === 'password') {
oauth.flows.password = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'accessCode') {
oauth.flows.authorizationCode = { tokenUrl: definition.tokenUrl, authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'application') {
oauth.flows.clientCredentials = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'implicit') {
oauth.flows.implicit = { authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
}
defs[key] = oauth;
}
else {
defs[key] = definitions[key];
}
});
return defs;
}
hasOAuthFlow(definition) {
return !!definition.flow;
}
hasOAuthFlows(definition) {
return !!definition.flows;
}
buildServers() {
const prefix = this.config.disableBasePathPrefixSlash ? undefined : '/';
const basePath = (0, pathUtils_1.normalisePath)(this.config.basePath, prefix, undefined, false);
const scheme = this.config.schemes ? this.config.schemes[0] : 'https';
const hosts = this.config.servers ? this.config.servers : this.config.host ? [this.config.host] : undefined;
const convertHost = (host) => ({ url: `${scheme}://${host}${basePath}` });
return (hosts?.map(convertHost) || [{ url: basePath }]);
}
buildSchema() {
const schema = {};
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
const referenceType = this.metadata.referenceTypeMap[typeName];
if (referenceType.dataType === 'refObject') {
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
schema[referenceType.refName] = {
description: referenceType.description,
properties: this.buildProperties(referenceType.properties),
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
type: 'object',
};
if (referenceType.additionalProperties) {
schema[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
}
else {
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
// ...we need to make a decision
schema[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
}
if (referenceType.example) {
schema[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refEnum') {
const enumTypes = this.determineTypesUsedInEnum(referenceType.enums);
if (enumTypes.size === 1) {
schema[referenceType.refName] = {
description: referenceType.description,
enum: referenceType.enums,
type: enumTypes.has('string') ? 'string' : 'number',
};
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
schema[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
}
}
else {
schema[referenceType.refName] = {
description: referenceType.description,
anyOf: [
{
type: 'number',
enum: referenceType.enums.filter(e => typeof e === 'number'),
},
{
type: 'string',
enum: referenceType.enums.filter(e => typeof e === 'string'),
},
],
};
}
if (referenceType.example) {
schema[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refAlias') {
const swaggerType = this.getSwaggerType(referenceType.type);
const format = referenceType.format;
const validators = Object.keys(referenceType.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: referenceType.validators[key].value,
};
}, {});
schema[referenceType.refName] = {
...swaggerType,
default: referenceType.default || swaggerType.default,
example: referenceType.example,
format: format || swaggerType.format,
description: referenceType.description,
...validators,
};
}
else {
(0, runtime_1.assertNever)(referenceType);
}
if (referenceType.deprecated) {
schema[referenceType.refName].deprecated = true;
}
});
return schema;
}
buildPaths() {
const paths = {};
this.metadata.controllers.forEach(controller => {
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
// construct documentation using all methods except @Hidden
controller.methods
.filter(method => !method.isHidden)
.forEach(method => {
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
path = (0, pathUtils_1.convertColonPathParams)(path);
paths[path] = paths[path] || {};
this.buildMethod(controller.name, method, paths[path], controller.produces);
});
});
return paths;
}
buildMethod(controllerName, method, pathObject, defaultProduces) {
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
pathMethod.description = method.description;
pathMethod.summary = method.summary;
pathMethod.tags = method.tags;
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
pathMethod.operationId = method.operationId || pathMethod.operationId;
if (method.deprecated) {
pathMethod.deprecated = method.deprecated;
}
if (method.security) {
pathMethod.security = method.security;
}
const bodyParams = method.parameters.filter(p => p.in === 'body');
const bodyPropParams = method.parameters.filter(p => p.in === 'body-prop');
const formParams = method.parameters.filter(p => p.in === 'formData');
const queriesParams = method.parameters.filter(p => p.in === 'queries');
pathMethod.parameters = method.parameters
.filter(p => {
return ['body', 'formData', 'request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
})
.map(p => this.buildParameter(p));
if (queriesParams.length > 1) {
throw new Error('Only one queries parameter allowed per controller method.');
}
if (queriesParams.length === 1) {
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
}
if (bodyParams.length > 1) {
throw new Error('Only one body parameter allowed per controller method.');
}
if (bodyParams.length > 0 && formParams.length > 0) {
throw new Error('Either body parameter or form parameters allowed per controller method - not both.');
}
if (bodyPropParams.length > 0) {
if (!bodyParams.length) {
bodyParams.push({
in: 'body',
name: 'body',
parameterName: 'body',
required: true,
type: {
dataType: 'nestedObjectLiteral',
properties: [],
},
validators: {},
deprecated: false,
});
}
const type = bodyParams[0].type;
bodyPropParams.forEach((bodyParam) => {
type.properties.push(bodyParam);
});
}
if (bodyParams.length > 0) {
pathMethod.requestBody = this.buildRequestBody(controllerName, method, bodyParams[0]);
}
else if (formParams.length > 0) {
pathMethod.requestBody = this.buildRequestBodyWithFormData(controllerName, method, formParams);
}
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
}
buildOperation(controllerName, method, defaultProduces) {
const swaggerResponses = {};
method.responses.forEach((res) => {
swaggerResponses[res.name] = {
description: res.description,
};
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
swaggerResponses[res.name].content = {};
const produces = res.produces || defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
for (const p of produces) {
const { content } = swaggerResponses[res.name];
swaggerResponses[res.name].content = {
...content,
[p]: {
schema: this.getSwaggerType(res.schema, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'Response' : undefined),
},
};
}
if (res.examples) {
let exampleCounter = 1;
const examples = res.examples.reduce((acc, ex, currentIndex) => {
const exampleLabel = res.exampleLabels?.[currentIndex];
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
}, {});
for (const p of produces) {
/* eslint-disable @typescript-eslint/dot-notation */
(swaggerResponses[res.name].content || {})[p]['examples'] = examples;
}
}
}
if (res.headers) {
const headers = {};
if (res.headers.dataType === 'refObject') {
headers[res.headers.refName] = {
schema: this.getSwaggerTypeForReferenceType(res.headers),
description: res.headers.description,
};
}
else if (res.headers.dataType === 'nestedObjectLiteral') {
res.headers.properties.forEach((each) => {
headers[each.name] = {
schema: this.getSwaggerType(each.type),
description: each.description,
required: this.isRequiredWithoutDefault(each),
};
});
}
else {
(0, runtime_1.assertNever)(res.headers);
}
swaggerResponses[res.name].headers = headers;
}
});
const operation = {
operationId: this.getOperationId(controllerName, method),
responses: swaggerResponses,
};
return operation;
}
buildRequestBodyWithFormData(controllerName, method, parameters) {
const required = [];
const properties = {};
for (const parameter of parameters) {
const mediaType = this.buildMediaType(controllerName, method, parameter);
properties[parameter.name] = mediaType.schema;
if (this.isRequiredWithoutDefault(parameter)) {
required.push(parameter.name);
}
if (parameter.deprecated) {
properties[parameter.name].deprecated = parameter.deprecated;
}
}
const requestBody = {
required: required.length > 0,
content: {
'multipart/form-data': {
schema: {
type: 'object',
properties,
// An empty list required: [] is not valid.
// If all properties are optional, do not specify the required keyword.
...(required && required.length && { required }),
},
},
},
};
return requestBody;
}
buildRequestBody(controllerName, method, parameter) {
const mediaType = this.buildMediaType(controllerName, method, parameter);
const consumes = method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE;
const requestBody = {
description: parameter.description,
required: this.isRequiredWithoutDefault(parameter),
content: {
[consumes]: mediaType,
},
};
return requestBody;
}
buildMediaType(controllerName, method, parameter) {
const validators = Object.keys(parameter.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: parameter.validators[key].value,
};
}, {});
const mediaType = {
schema: {
...this.getSwaggerType(parameter.type, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'RequestBody' : undefined),
...validators,
...(parameter.description && { description: parameter.description }),
},
};
const parameterExamples = parameter.example;
const parameterExampleLabels = parameter.exampleLabels;
if (parameterExamples === undefined) {
mediaType.example = parameterExamples;
}
else if (parameterExamples.length === 1) {
mediaType.example = parameterExamples[0];
}
else {
let exampleCounter = 1;
mediaType.examples = parameterExamples.reduce((acc, ex, currentIndex) => {
const exampleLabel = parameterExampleLabels?.[currentIndex];
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
}, {});
}
return mediaType;
}
buildQueriesParameter(source) {
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
const properties = source.type.properties;
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
}
throw new Error(`Queries '${source.name}' parameter must be an object.`);
}
buildParameter(source) {
const parameter = {
description: source.description,
in: source.in,
name: source.name,
required: this.isRequiredWithoutDefault(source),
schema: {
default: source.default,
format: undefined,
},
};
if (source.deprecated) {
parameter.deprecated = true;
}
const parameterType = this.getSwaggerType(source.type);
if (parameterType.format) {
parameter.schema.format = this.throwIfNotDataFormat(parameterType.format);
}
if (parameterType.$ref) {
parameter.schema = parameterType;
return Object.assign(parameter, this.buildExamples(source));
}
const validatorObjs = {};
Object.keys(source.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
validatorObjs[key] = source.validators[key].value;
});
if (source.type.dataType === 'any') {
parameter.schema.type = 'string';
}
else {
if (parameterType.type) {
parameter.schema.type = this.throwIfNotDataType(parameterType.type);
}
parameter.schema.items = parameterType.items;
parameter.schema.enum = parameterType.enum;
}
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
return Object.assign(parameter, this.buildExamples(source));
}
buildExamples(source) {
const { example: parameterExamples, exampleLabels } = source;
if (parameterExamples === undefined) {
return { example: undefined };
}
if (parameterExamples.length === 1) {
return { example: parameterExamples[0] };
}
let exampleCounter = 1;
const examples = parameterExamples.reduce((acc, ex, idx) => {
const label = exampleLabels?.[idx];
const name = label ?? `Example ${exampleCounter++}`;
acc[name] = { value: ex };
return acc;
}, {});
return { examples };
}
buildProperties(source) {
const properties = {};
source.forEach(property => {
let swaggerType = this.getSwaggerType(property.type);
const format = property.format;
swaggerType.description = property.description;
swaggerType.example = property.example;
swaggerType.format = format || swaggerType.format;
if (!swaggerType.$ref) {
swaggerType.default = property.default;
Object.keys(property.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
});
}
if (property.deprecated) {
swaggerType.deprecated = true;
}
if (property.extensions) {
property.extensions.forEach(property => {
swaggerType[property.key] = property.value;
});
}
properties[property.name] = swaggerType;
});
return properties;
}
getSwaggerTypeForReferenceType(referenceType) {
return { $ref: `#/components/schemas/${encodeURIComponent(referenceType.refName)}` };
}
getSwaggerTypeForPrimitiveType(dataType) {
if (dataType === 'any') {
// Setting additionalProperties causes issues with code generators for OpenAPI 3
// Therefore, we avoid setting it explicitly (since it's the implicit default already)
return {};
}
else if (dataType === 'file') {
return { type: 'string', format: 'binary' };
}
return super.getSwaggerTypeForPrimitiveType(dataType);
}
isNull(type) {
return type.dataType === 'enum' && type.enums.length === 1 && type.enums[0] === null;
}
// Join disparate enums with the same type into one.
//
// grouping enums is helpful because it makes the spec more readable and it
// bypasses a failure in openapi-generator caused by using anyOf with
// duplicate types.
groupEnums(types) {
const returnTypes = [];
const enumValuesByType = {};
for (const type of types) {
if (type.enum && type.type) {
for (const enumValue of type.enum) {
if (!enumValuesByType[type.type]) {
enumValuesByType[type.type] = {};
}
enumValuesByType[type.type][String(enumValue)] = enumValue;
}
}
// preserve non-enum types
else {
returnTypes.push(type);
}
}
Object.keys(enumValuesByType).forEach(dataType => returnTypes.push({
type: dataType,
enum: Object.values(enumValuesByType[dataType]),
}));
return returnTypes;
}
removeDuplicateSwaggerTypes(types) {
if (types.length === 1) {
return types;
}
else {
const typesSet = new Set();
for (const type of types) {
typesSet.add(JSON.stringify(type));
}
return Array.from(typesSet).map(typeString => JSON.parse(typeString));
}
}
getSwaggerTypeForUnionType(type, title) {
// Filter out nulls and undefineds
const actualSwaggerTypes = this.removeDuplicateSwaggerTypes(this.groupEnums(type.types
.filter(x => !this.isNull(x))
.filter(x => x.dataType !== 'undefined')
.map(x => this.getSwaggerType(x))));
const nullable = type.types.some(x => this.isNull(x));
if (nullable) {
if (actualSwaggerTypes.length === 1) {
const [swaggerType] = actualSwaggerTypes;
// for ref union with null, use an allOf with a single
// element since you can't attach nullable directly to a ref.
// https://swagger.io/docs/specification/using-ref/#syntax
if (swaggerType.$ref) {
return { allOf: [swaggerType], nullable };
}
// Note that null must be explicitly included in the list of enum values. Using nullable: true alone is not enough here.
// https://swagger.io/docs/specification/data-models/enums/
if (swaggerType.enum) {
swaggerType.enum.push(null);
}
return { ...(title && { title }), ...swaggerType, nullable };
}
else {
return { ...(title && { title }), anyOf: actualSwaggerTypes, nullable };
}
}
else {
if (actualSwaggerTypes.length === 1) {
return { ...(title && { title }), ...actualSwaggerTypes[0] };
}
else {
return { ...(title && { title }), anyOf: actualSwaggerTypes };
}
}
}
getSwaggerTypeForIntersectionType(type, title) {
return { allOf: type.types.map(x => this.getSwaggerType(x)), ...(title && { title }) };
}
getSwaggerTypeForEnumType(enumType, title) {
const types = this.determineTypesUsedInEnum(enumType.enums);
if (types.size === 1) {
const type = types.values().next().value;
const nullable = enumType.enums.includes(null) ? true : false;
return { ...(title && { title }), type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), nullable };
}
else {
const valuesDelimited = Array.from(types).join(',');
throw new Error(`Enums can only have string or number values, but enum had ${valuesDelimited}`);
}
}
}
exports.SpecGenerator3 = SpecGenerator3;
//# sourceMappingURL=specGenerator3.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
import { Swagger, Tsoa } from '@tsoa/runtime';
import { ExtendedSpecConfig } from '../cli';
import { SpecGenerator3 } from './specGenerator3';
/**
* OpenAPI 3.1 Spec Generator
*
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
* - Tuple support via prefixItems
* - OpenAPI version 3.1.0
*
* Uses inheritance to reuse all building logic from SpecGenerator3,
* only overriding the version and adding tuple type support.
*/
export declare class SpecGenerator31 extends SpecGenerator3 {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec31;
/**
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
*/
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
private isTupleType;
}

73
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js generated vendored Normal file
View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator31 = void 0;
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const specGenerator3_1 = require("./specGenerator3");
/**
* OpenAPI 3.1 Spec Generator
*
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
* - Tuple support via prefixItems
* - OpenAPI version 3.1.0
*
* Uses inheritance to reuse all building logic from SpecGenerator3,
* only overriding the version and adding tuple type support.
*/
class SpecGenerator31 extends specGenerator3_1.SpecGenerator3 {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
// Override with OpenAPI 3.1 specific return type
// The base class returns Swagger.Spec30, but this generator produces Swagger.Spec31
GetSpec() {
let spec = {
openapi: '3.1.0',
components: this.buildComponents(),
info: this.buildInfo(),
paths: this.buildPaths(),
servers: this.buildServers(),
tags: this.config.tags,
};
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
return spec;
}
/**
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
*/
getSwaggerType(type, title) {
if (this.isTupleType(type)) {
const prefixItems = type.types.map((t) => this.getSwaggerType(t));
const schema = {
type: 'array',
prefixItems,
minItems: prefixItems.length,
...(type.restType
? {
items: this.getSwaggerType(type.restType),
}
: {
maxItems: prefixItems.length,
items: false,
}),
};
return schema;
}
return super.getSwaggerType(type, title);
}
isTupleType(type) {
return type.dataType === 'tuple';
}
}
exports.SpecGenerator31 = SpecGenerator31;
//# sourceMappingURL=specGenerator31.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"specGenerator31.js","sourceRoot":"","sources":["../../src/swagger/specGenerator31.ts"],"names":[],"mappings":";;;AACA,mDAAwD;AACxD,+CAAkD;AAIlD,qDAAkD;AAElD;;;;;;;;;GASG;AACH,MAAa,eAAgB,SAAQ,+BAAc;IACjD,YACqB,QAAuB,EACvB,MAA0B;QAE7C,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAHL,aAAQ,GAAR,QAAQ,CAAe;QACvB,WAAM,GAAN,MAAM,CAAoB;IAG/C,CAAC;IAED,iDAAiD;IACjD,oFAAoF;IACpE,OAAO;QACrB,IAAI,IAAI,GAAmB;YACzB,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,IAAI,CAAC,eAAe,EAA0B;YAC1D,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE;YACtB,KAAK,EAAE,IAAI,CAAC,UAAU,EAAwC;YAC9D,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE;YAC5B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SACvB,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,CAAC;YACjE,MAAM,UAAU,GAAgG;gBAC9G,SAAS,EAAE,MAAM,CAAC,MAAM;gBACxB,SAAS,EAAE,sBAAa;gBACxB,SAAS,EAAE,CAAC,IAAuB,EAAE,KAAwB,EAAqB,EAAE,CAAC,IAAA,oBAAS,EAAC,IAAI,EAAE,KAAK,CAAC;aAC5G,CAAC;YAEF,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAoC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAyB,CAA8B,CAAC;QACvJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACO,cAAc,CAAC,IAAe,EAAE,KAAc;QACtD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAuB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAqB,CAAC,CAAC;YAErH,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,OAAO;gBACb,WAAW;gBACX,QAAQ,EAAE,WAAW,CAAC,MAAM;gBAC5B,GAAG,CAAC,IAAI,CAAC,QAAQ;oBACf,CAAC,CAAC;wBACE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAqB;qBAC9D;oBACH,CAAC,CAAC;wBACE,QAAQ,EAAE,WAAW,CAAC,MAAM;wBAC5B,KAAK,EAAE,KAAK;qBACb,CAAC;aACP,CAAC;YAEF,OAAO,MAAuC,CAAC;QACjD,CAAC;QAED,OAAO,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAEO,WAAW,CAAC,IAAe;QACjC,OAAO,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC;IACnC,CAAC;CACF;AAhED,0CAgEC"}

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
export declare function getDecorators(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): ts.Identifier[];
export declare function getNodeFirstDecoratorName(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): string | undefined;
export declare function getNodeFirstDecoratorValue(node: ts.Node, typeChecker: ts.TypeChecker, isMatching: (identifier: ts.Identifier) => boolean): any;
export declare function getDecoratorValues(decorator: ts.Identifier, typeChecker: ts.TypeChecker): any[];
export declare function getSecurites(decorator: ts.Identifier, typeChecker: ts.TypeChecker): any;
export declare function isDecorator(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): boolean;
export declare function getPath(decorator: ts.Identifier, typeChecker: ts.TypeChecker): string;
export declare function getProduces(node: ts.Node, typeChecker: ts.TypeChecker): string[];

118
node_modules/@tsoa/cli/dist/utils/decoratorUtils.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDecorators = getDecorators;
exports.getNodeFirstDecoratorName = getNodeFirstDecoratorName;
exports.getNodeFirstDecoratorValue = getNodeFirstDecoratorValue;
exports.getDecoratorValues = getDecoratorValues;
exports.getSecurites = getSecurites;
exports.isDecorator = isDecorator;
exports.getPath = getPath;
exports.getProduces = getProduces;
const ts = __importStar(require("typescript"));
const initializer_value_1 = require("../metadataGeneration/initializer-value");
function tsHasDecorators(tsNamespace) {
return typeof tsNamespace.canHaveDecorators === 'function';
}
function getDecorators(node, isMatching) {
// beginning in ts4.8 node.decorator is undefined, use getDecorators instead.
const decorators = tsHasDecorators(ts) && ts.canHaveDecorators(node) ? ts.getDecorators(node) : [];
if (!decorators || !decorators.length) {
return [];
}
return decorators
.map((e) => {
while (e.expression !== undefined) {
e = e.expression;
}
return e;
})
.filter(isMatching);
}
function getNodeFirstDecoratorName(node, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return;
}
return decorators[0].text;
}
function getNodeFirstDecoratorValue(node, typeChecker, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return;
}
const values = getDecoratorValues(decorators[0], typeChecker);
return values && values[0];
}
function getDecoratorValues(decorator, typeChecker) {
const expression = decorator.parent;
const expArguments = expression.arguments;
if (!expArguments || !expArguments.length) {
return [];
}
return expArguments.map(a => (0, initializer_value_1.getInitializerValue)(a, typeChecker));
}
function getSecurites(decorator, typeChecker) {
const [first, second] = getDecoratorValues(decorator, typeChecker);
if (isObject(first)) {
return first;
}
return { [first]: second || [] };
}
function isDecorator(node, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return false;
}
return true;
}
function isObject(v) {
return typeof v === 'object' && v !== null;
}
function getPath(decorator, typeChecker) {
const [path] = getDecoratorValues(decorator, typeChecker);
if (path === undefined) {
return '';
}
return path;
}
function getProduces(node, typeChecker) {
const producesDecorators = getDecorators(node, identifier => identifier.text === 'Produces');
if (!producesDecorators || !producesDecorators.length) {
return [];
}
return producesDecorators.map(decorator => getDecoratorValues(decorator, typeChecker)[0]);
}
//# sourceMappingURL=decoratorUtils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decoratorUtils.js","sourceRoot":"","sources":["../../src/utils/decoratorUtils.ts"],"names":[],"mappings":"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"}

1
node_modules/@tsoa/cli/dist/utils/flowUtils.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export declare function throwUnless(condition: unknown, error: Error): asserts condition;

8
node_modules/@tsoa/cli/dist/utils/flowUtils.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.throwUnless = throwUnless;
function throwUnless(condition, error) {
if (!condition)
throw error;
}
//# sourceMappingURL=flowUtils.js.map

1
node_modules/@tsoa/cli/dist/utils/flowUtils.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"flowUtils.js","sourceRoot":"","sources":["../../src/utils/flowUtils.ts"],"names":[],"mappings":";;AAAA,kCAEC;AAFD,SAAgB,WAAW,CAAC,SAAkB,EAAE,KAAY;IAC1D,IAAI,CAAC,SAAS;QAAE,MAAM,KAAK,CAAC;AAC9B,CAAC"}

5
node_modules/@tsoa/cli/dist/utils/fs.d.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import * as fs from 'fs';
export declare const fsExists: typeof fs.exists.__promisify__;
export declare const fsMkDir: typeof fs.mkdir.__promisify__;
export declare const fsWriteFile: typeof fs.writeFile.__promisify__;
export declare const fsReadFile: typeof fs.readFile.__promisify__;

43
node_modules/@tsoa/cli/dist/utils/fs.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fsReadFile = exports.fsWriteFile = exports.fsMkDir = exports.fsExists = void 0;
const fs = __importStar(require("fs"));
const util_1 = require("util");
exports.fsExists = (0, util_1.promisify)(fs.exists);
exports.fsMkDir = (0, util_1.promisify)(fs.mkdir);
exports.fsWriteFile = (0, util_1.promisify)(fs.writeFile);
exports.fsReadFile = (0, util_1.promisify)(fs.readFile);
//# sourceMappingURL=fs.js.map

1
node_modules/@tsoa/cli/dist/utils/fs.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,+BAAiC;AAEpB,QAAA,QAAQ,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,MAAM,CAAC,CAAC;AAChC,QAAA,OAAO,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9B,QAAA,WAAW,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,SAAS,CAAC,CAAC;AACtC,QAAA,UAAU,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,QAAQ,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export declare const isNotNullOrUndefined: <TValue>(value: TValue | null | undefined) => value is TValue;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNotNullOrUndefined = void 0;
const isNotNullOrUndefined = (value) => {
return value !== null && value !== undefined;
};
exports.isNotNullOrUndefined = isNotNullOrUndefined;
//# sourceMappingURL=genericTypeGuards.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"genericTypeGuards.js","sourceRoot":"","sources":["../../src/utils/genericTypeGuards.ts"],"names":[],"mappings":";;;AAAO,MAAM,oBAAoB,GAAG,CAAS,KAAgC,EAAmB,EAAE;IAChG,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAC/C,CAAC,CAAC;AAFW,QAAA,oBAAoB,wBAE/B"}

View File

@@ -0,0 +1,5 @@
import { Tsoa } from '@tsoa/runtime';
import { NodeArray, TypeNode } from 'typescript';
import { MetadataGenerator } from '../metadataGeneration/metadataGenerator';
export declare function getHeaderType(typeArgumentNodes: NodeArray<TypeNode> | undefined, index: number, metadataGenerator: MetadataGenerator): Tsoa.HeaderType | undefined;
export declare function isSupportedHeaderDataType(parameterType: Tsoa.Type): parameterType is Tsoa.HeaderType;

27
node_modules/@tsoa/cli/dist/utils/headerTypeHelpers.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHeaderType = getHeaderType;
exports.isSupportedHeaderDataType = isSupportedHeaderDataType;
const exceptions_1 = require("../metadataGeneration/exceptions");
const typeResolver_1 = require("../metadataGeneration/typeResolver");
function getHeaderType(typeArgumentNodes, index, metadataGenerator) {
if (!typeArgumentNodes || !typeArgumentNodes[index]) {
return undefined;
}
const candidate = new typeResolver_1.TypeResolver(typeArgumentNodes[index], metadataGenerator).resolve();
if (candidate && isSupportedHeaderDataType(candidate)) {
return candidate;
}
else if (candidate) {
throw new exceptions_1.GenerateMetadataError(`Unable to parse Header Type ${typeArgumentNodes[index].getText()}`, typeArgumentNodes[index]);
}
return undefined;
}
function isSupportedHeaderDataType(parameterType) {
const supportedPathDataTypes = ['nestedObjectLiteral', 'refObject'];
if (supportedPathDataTypes.find(t => t === parameterType.dataType)) {
return true;
}
return false;
}
//# sourceMappingURL=headerTypeHelpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"headerTypeHelpers.js","sourceRoot":"","sources":["../../src/utils/headerTypeHelpers.ts"],"names":[],"mappings":";;AAMA,sCAcC;AAED,8DAOC;AA3BD,iEAAyE;AAEzE,qEAAkE;AAElE,SAAgB,aAAa,CAAC,iBAAkD,EAAE,KAAa,EAAE,iBAAoC;IACnI,IAAI,CAAC,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,2BAAY,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC;IAE1F,IAAI,SAAS,IAAI,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,OAAO,SAAS,CAAC;IACnB,CAAC;SAAM,IAAI,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,kCAAqB,CAAC,+BAA+B,iBAAiB,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;IACjI,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,yBAAyB,CAAC,aAAwB;IAChE,MAAM,sBAAsB,GAA6B,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;IAC9F,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}

View File

@@ -0,0 +1,4 @@
/**
* Loads all exported classes from the given directory.
*/
export declare function importClassesFromDirectories(directories: string[], formats?: string[]): string[];

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importClassesFromDirectories = importClassesFromDirectories;
const path_1 = require("path");
const glob_1 = require("glob");
/**
* Loads all exported classes from the given directory.
*/
function importClassesFromDirectories(directories, formats = ['.ts']) {
const allFiles = directories.reduce((allDirs, dir) => {
// glob docs says: Please only use forward-slashes in glob expressions.
// therefore do not do any normalization of dir path
return allDirs.concat((0, glob_1.sync)(dir));
}, []);
return allFiles.filter(file => {
const dtsExtension = file.substring(file.length - 5, file.length);
return formats.indexOf((0, path_1.extname)(file)) !== -1 && dtsExtension !== '.d.ts';
});
}
//# sourceMappingURL=importClassesFromDirectories.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"importClassesFromDirectories.js","sourceRoot":"","sources":["../../src/utils/importClassesFromDirectories.ts"],"names":[],"mappings":";;AAMA,oEAWC;AAjBD,+BAA+B;AAC/B,+BAA4B;AAE5B;;GAEG;AACH,SAAgB,4BAA4B,CAAC,WAAqB,EAAE,OAAO,GAAG,CAAC,KAAK,CAAC;IACnF,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;QACnD,uEAAuE;QACvE,oDAAoD;QACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAA,WAAI,EAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC,EAAE,EAAc,CAAC,CAAC;IAEnB,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAA,cAAO,EAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,OAAO,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,5 @@
import { Tsoa } from '@tsoa/runtime';
/**
* This will help us do exhaustive matching against only reference types. For example, once you have narrowed the input, you don't then have to check the case where it's a `integer` because it never will be.
*/
export declare function isRefType(metaType: Tsoa.Type): metaType is Tsoa.ReferenceType;

View File

@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isRefType = isRefType;
// This file is designed to contain functions that narrow the input type to a type within src\metadataGeneration\tsoa.ts
const runtime_1 = require("@tsoa/runtime");
/**
* This will help us do exhaustive matching against only reference types. For example, once you have narrowed the input, you don't then have to check the case where it's a `integer` because it never will be.
*/
function isRefType(metaType) {
switch (metaType.dataType) {
case 'any':
return false;
case 'array':
return false;
case 'binary':
return false;
case 'boolean':
return false;
case 'buffer':
return false;
case 'byte':
return false;
case 'date':
return false;
case 'file':
return false;
case 'datetime':
return false;
case 'double':
return false;
case 'enum':
return false;
case 'float':
return false;
case 'integer':
return false;
case 'intersection':
return false;
case 'long':
return false;
case 'nestedObjectLiteral':
return false;
case 'object':
return false;
case 'refEnum':
return true;
case 'refObject':
return true;
case 'refAlias':
return true;
case 'string':
return false;
case 'tuple':
return false;
case 'union':
return false;
case 'void':
return false;
case 'undefined':
return false;
default: {
return (0, runtime_1.assertNever)(metaType);
}
}
}
//# sourceMappingURL=internalTypeGuards.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"internalTypeGuards.js","sourceRoot":"","sources":["../../src/utils/internalTypeGuards.ts"],"names":[],"mappings":";;AAMA,8BAwDC;AA9DD,wHAAwH;AACxH,2CAAkD;AAElD;;GAEG;AACH,SAAgB,SAAS,CAAC,QAAmB;IAC3C,QAAQ,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,KAAK;YACR,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,cAAc;YACjB,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,qBAAqB;YACxB,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC;QACd,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;QACd,KAAK,UAAU;YACb,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,WAAW;YACd,OAAO,KAAK,CAAC;QACf,OAAO,CAAC,CAAC,CAAC;YACR,OAAO,IAAA,qBAAW,EAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC"}

2
node_modules/@tsoa/cli/dist/utils/isVoidType.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import { Tsoa } from '@tsoa/runtime';
export declare const isVoidType: (type: Tsoa.Type) => boolean;

16
node_modules/@tsoa/cli/dist/utils/isVoidType.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isVoidType = void 0;
const isVoidType = (type) => {
if (type.dataType === 'void' || type.dataType === 'undefined') {
return true;
}
else if (type.dataType === 'refAlias') {
return (0, exports.isVoidType)(type.type);
}
else {
return false;
}
};
exports.isVoidType = isVoidType;
//# sourceMappingURL=isVoidType.js.map

1
node_modules/@tsoa/cli/dist/utils/isVoidType.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"isVoidType.js","sourceRoot":"","sources":["../../src/utils/isVoidType.ts"],"names":[],"mappings":";;;AAEO,MAAM,UAAU,GAAG,CAAC,IAAe,EAAW,EAAE;IACrD,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACxC,OAAO,IAAA,kBAAU,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AARW,QAAA,UAAU,cAQrB"}

8
node_modules/@tsoa/cli/dist/utils/jsDocUtils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import * as ts from 'typescript';
export declare function getJSDocDescription(node: ts.Node): string | undefined;
export declare function getJSDocComment(node: ts.Node, tagName: string): string | undefined;
export declare function getJSDocComments(node: ts.Node, tagName: string): string[] | undefined;
export declare function getJSDocTagNames(node: ts.Node, requireTagName?: boolean): string[];
export declare function getJSDocTags(node: ts.Node, isMatching: (tag: ts.JSDocTag) => boolean): ts.JSDocTag[];
export declare function isExistJSDocTag(node: ts.Node, isMatching: (tag: ts.JSDocTag) => boolean): boolean;
export declare function commentToString(comment?: string | ts.NodeArray<ts.JSDocText | ts.JSDocLink | ts.JSDocComment>): string | undefined;

Some files were not shown because too many files have changed in this diff Show More