jswagger-generator
Version:
This is jswagger's generator package.
262 lines (248 loc) • 9.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const property_mapper_1 = require("../property-mapper");
const utils_1 = require("../utils");
exports.PRIMITIVES = {
// boolean types
boolean: 'boolean',
// string types
binary: 'string',
byte: 'string',
date: 'string',
dateTime: 'string',
password: 'string',
string: 'string',
// number types
double: 'number',
float: 'number',
integer: 'number',
number: 'number',
};
function snakeToCamel(str) {
return str.replace(/([-_][a-z])/g, (group) => group.toUpperCase()
.replace('-', '')
.replace('_', ''));
}
// type conversions
function transform(node, options) {
const _options = options || {};
const resolvedType = utils_1.nodeType(node);
switch (resolvedType) {
case 'ref': {
return utils_1.transformRef(node.$ref);
}
case 'string':
if (_options.classMode) {
switch (node.format) {
case 'byte':
return 'Buffer';
case 'date-time':
return 'Date';
}
}
case 'number':
case 'boolean': {
return resolvedType;
}
case 'enum': {
return utils_1.tsUnionOf(node.enum.map((item) => `'${item}'`));
}
case 'object': {
if ((!node.properties || !Object.keys(node.properties).length) &&
!node.allOf &&
!node.additionalProperties) {
return '{ [key: string]: any }';
}
let properties = createKeys(node.properties || {}, Array.isArray(node.required) ? node.required : [], options);
// if additional properties, add to end of properties
if (node.additionalProperties) {
properties += `[key: string]: ${utils_1.nodeType(node.additionalProperties) || 'any'};\n`;
}
return utils_1.tsIntersectionOf([
...(node.allOf ? node.allOf.map(v => transform(v, options)) : []),
...(properties ? [`{ ${properties} }`] : []),
]);
break;
}
case 'array': {
return utils_1.tsArrayOf(transform(node.items, options));
}
}
return '';
}
var GenerateTypesOnlyFlag;
(function (GenerateTypesOnlyFlag) {
GenerateTypesOnlyFlag[GenerateTypesOnlyFlag["INTERFACE"] = 1] = "INTERFACE";
GenerateTypesOnlyFlag[GenerateTypesOnlyFlag["CLASS"] = 2] = "CLASS";
})(GenerateTypesOnlyFlag = exports.GenerateTypesOnlyFlag || (exports.GenerateTypesOnlyFlag = {}));
function generateTypesOnly(node, flags) {
if ('$ref' in node) {
const ref = node.$ref;
const definitionRegex = /#\/definitions\/(.*)/;
const definitionMatcher = definitionRegex.exec(ref);
if (!definitionMatcher) {
return 'never';
}
else {
const list = [];
if (flags & GenerateTypesOnlyFlag.INTERFACE) {
list.push(`definition.I${definitionMatcher[1]}`);
}
if (flags & GenerateTypesOnlyFlag.CLASS) {
list.push(`definition.${definitionMatcher[1]}`);
}
return list.join(' | ');
}
}
else {
return transform(node, {
classMode: !!(flags & GenerateTypesOnlyFlag.CLASS)
});
}
}
exports.generateTypesOnly = generateTypesOnly;
function createKeys(obj, required = [], options = {}) {
let output = '';
Object.entries(obj).forEach(([key, value]) => {
// 1. JSDoc comment (goes above property)
if (value.description) {
output += utils_1.comment(value.description);
}
// 2. name (with “?” if optional property)
output += `"${key}"${!required || !required.includes(key) ? '?' : ''}: `;
// 3. get value
output += transform(value, options);
// 4. close type
output += ';\n';
});
return output;
}
function generateTypesV2(schema, propertyMapped, options) {
// note: make sure that base-level definitions are required
return createKeys(propertyMapped, Object.keys(propertyMapped));
}
function generateTypeAliases(keys) {
return keys.map(key => {
return `export type I${key} = definitions['${key}'];`;
}).join('\n');
}
function extractChilds(input) {
const unpackAChar = (text, find) => {
if (text.charAt(0) === find[0] && text.charAt(text.length - 1) === find[1])
return text.substring(1, text.length - 1).trim();
return text;
};
let output = input.trim();
output = unpackAChar(output, ['(', ')']);
output = unpackAChar(output, ['{', '}']);
output = unpackAChar(output, ['(', ')']);
return output;
}
function generateClass(schema, key, schemaObject, options) {
const className = key;
const definitionMetadata = schemaObject.properties;
const isWildcardType = key === 'WildcardType';
const properties = schemaObject.properties && Object.keys(schemaObject.properties) || [];
const toJsonObjectFunction = isWildcardType ?
`return Object.keys(this)
.reduce((result, key) => {
result[key] = toJsonObject(metadata, ${className}[S_DEFINITION_METADATA], key, this[key]);
return result;
}, {});` :
`return {
${properties.map(v => v + `: toJsonObject(metadata, ${className}[S_DEFINITION_METADATA], '${v}', this['${v}'])`).join(',')}
};`;
const isInstanceFunction = isWildcardType ?
'return isSwaggerDefinition(this);' :
`return o[S_DEFINITION_NAME] === ${className}.SWAGGER_DEFINITION_NAME;`;
const assignFunction = isWildcardType ?
`Object.entries(schema)
.forEach(([key, value]) => {
this[key] = value;
});` :
properties.map(v => `this.${v} = toClassValue(metadata, ${className}[S_DEFINITION_METADATA], '${v}', schema.${v});`).join(';\n');
// options && options.propertyToCamel
// schemaObject.properties
return `export class ${key} extends BaseClass {
${extractChilds(transform(schemaObject, {
classMode: true
}))}
public static SWAGGER_DEFINITION_NAME = '${className}';
public readonly [S_DEFINITION_NAME]: string;
public constructor(params?: {schema?: I${className}}) {
super();
this[S_DEFINITION_NAME] = '${className}';
const schema = (params && params.schema) || ({} as any);
${assignFunction}
}
public _assignFrom(schema: I${className}) {
${assignFunction}
}
public static isInstance(o: any): boolean {
${isInstanceFunction}
}
public [S_METHOD_TO_JSON_OBJECT](): IJsonObject {
${toJsonObjectFunction}
}
}
Object.defineProperty(${className}, S_DEFINITION_NAME, {
get: () => '${className}'
});
Object.defineProperty(${className}, S_DEFINITION_METADATA, {
get: () => (${JSON.stringify(definitionMetadata)})
});
`;
}
function generateClasses(schema, propertyMapped, options) {
return Object.entries(propertyMapped)
.map(([key, value]) => {
return generateClass(schema, key, value, options);
}).join('\n');
}
function index(schema, options) {
if (!schema.definitions) {
throw new Error('⛔️ \'definitions\' missing from schema https://swagger.io/specification/v2/#definitions-object');
}
// propertyMapper
const propertyMapped = options
? property_mapper_1.default(schema.definitions, options.propertyMapper)
: schema.definitions;
return `
const S_DEFINITION_NAME = Symbol('Definition Name');
const S_METHOD_TO_JSON_OBJECT = Symbol('toJsonObject');
const S_DEFINITION_METADATA = Symbol('Definition Metadata');
export type IJsonObject = any;
export namespace definition {
export abstract class BaseClass {
public abstract [S_METHOD_TO_JSON_OBJECT](): IJsonObject;
}
export interface definitions {
${generateTypesV2(schema, propertyMapped, options)}
}
${generateTypeAliases(Object.keys(propertyMapped))}
${generateClasses(schema, propertyMapped, options)}
}
export function isSwaggerDefinition(o: any): boolean {
return (typeof o[S_DEFINITION_NAME] !== 'undefined');
}
export function getSwaggerDefinitionName(o: any): string {
return o[S_DEFINITION_NAME];
}
export function swaggerDefinitionToJson(o: any): IJsonObject {
return o[S_METHOD_TO_JSON_OBJECT]();
}
export const metadata = Object.freeze({
isSwaggerDefinition, getSwaggerDefinitionName, swaggerDefinitionToJson,
classes: [
${Object.keys(propertyMapped).map(v => 'definition.' + v).join(', ')}
],
definitions: {
${Object.entries(propertyMapped).map(([key, prop]) => `'#/definitions/${key}': ${JSON.stringify(prop)}`).join(',\n')}
}
});
export interface definitions {
${Object.keys(propertyMapped).map(v => `${v}: definition.${v}`).join(';\n')}
}
`;
}
exports.default = index;