Compare commits
3 Commits
v0.107.28
...
fix-client
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46698078a0 | ||
|
|
59a3045615 | ||
|
|
1453c27d87 |
@@ -76,7 +76,7 @@
|
||||
"stylelint-webpack-plugin": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.0.0",
|
||||
"ts-loader": "^8.0.6",
|
||||
"ts-morph": "^8.1.2",
|
||||
"ts-morph": "^10.0.1",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.3",
|
||||
"url-loader": "^4.1.1",
|
||||
|
||||
@@ -9,4 +9,5 @@ export const trimQuotes = (str: string) => {
|
||||
return str.replace(/\'|\"/g, '');
|
||||
};
|
||||
|
||||
export const GENERATOR_ENTITY_ALLIAS = 'Entities/';
|
||||
export const GENERATOR_ENTITY_ALLIAS = 'Entities/';
|
||||
export const BAD_REQUES_HELPER = 'BadRequesHelper';
|
||||
@@ -4,9 +4,10 @@ import { OPEN_API_PATH } from '../consts';
|
||||
|
||||
import EntitiesGenerator from './src/generateEntities';
|
||||
import ApisGenerator from './src/generateApis';
|
||||
import { OpenApi } from './src/utils';
|
||||
|
||||
|
||||
const generateApi = (openApi: Record<string, any>) => {
|
||||
const generateApi = (openApi: OpenApi) => {
|
||||
const ent = new EntitiesGenerator(openApi);
|
||||
ent.save();
|
||||
|
||||
@@ -14,5 +15,5 @@ const generateApi = (openApi: Record<string, any>) => {
|
||||
api.save();
|
||||
}
|
||||
|
||||
const openApiFile = fs.readFileSync(OPEN_API_PATH, 'utf8');
|
||||
const openApiFile = fs.readFileSync('./scripts/generator/v1.yaml', 'utf8');
|
||||
generateApi(YAML.parse(openApiFile));
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { stringify } from 'qs';
|
||||
import { number } from 'prop-types';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import * as morph from 'ts-morph';
|
||||
|
||||
import {
|
||||
API_DIR as API_DIR_CONST,
|
||||
BAD_REQUES_HELPER,
|
||||
GENERATOR_ENTITY_ALLIAS,
|
||||
} from '../../consts';
|
||||
import { toCamel, capitalize, schemaParamParser } from './utils';
|
||||
import { toCamel, capitalize, schemaParamParser, OpenApi, uncapitalize, RequestBody } from './utils';
|
||||
|
||||
|
||||
const API_DIR = path.resolve(API_DIR_CONST);
|
||||
@@ -20,11 +21,15 @@ if (!fs.existsSync(API_DIR)) {
|
||||
|
||||
const { Project, QuoteKind } = morph;
|
||||
|
||||
enum PROCESS_AS {
|
||||
JSON = 'JSON',
|
||||
TEXT = 'TEXT',
|
||||
EMPTY = 'EMPTY',
|
||||
}
|
||||
|
||||
class ApiGenerator {
|
||||
project = new Project({
|
||||
tsConfigFilePath: './tsconfig.json',
|
||||
addFilesFromTsConfig: false,
|
||||
manipulationSettings: {
|
||||
quoteKind: QuoteKind.Single,
|
||||
usePrefixAndSuffixTextForRename: false,
|
||||
@@ -32,7 +37,7 @@ class ApiGenerator {
|
||||
},
|
||||
});
|
||||
|
||||
openapi: Record<string, any>;
|
||||
openapi: OpenApi;
|
||||
|
||||
serverUrl: string;
|
||||
|
||||
@@ -47,20 +52,28 @@ class ApiGenerator {
|
||||
|
||||
apis: morph.SourceFile[] = [];
|
||||
|
||||
constructor(openapi: Record<string, any>) {
|
||||
methods = ['patch', 'delete', 'post', 'get', 'put', 'head', 'options', 'trace'];
|
||||
|
||||
constructor(openapi: OpenApi) {
|
||||
this.openapi = openapi;
|
||||
this.paths = openapi.paths;
|
||||
this.serverUrl = openapi.servers[0].url;
|
||||
|
||||
Object.keys(this.paths).forEach((pathKey) => {
|
||||
Object.keys(this.paths[pathKey]).forEach((method) => {
|
||||
Object.keys(this.paths[pathKey]).filter((method) => this.methods.includes(method)).forEach((method) => {
|
||||
const {
|
||||
tags, operationId, parameters, responses, requestBody, security,
|
||||
tags, operationId, responses, requestBody, security, "x-skip-web-api": skip
|
||||
} = this.paths[pathKey][method];
|
||||
const controller = toCamel((tags ? tags[0] : pathKey.split('/')[1]).replace('-controller', ''));
|
||||
|
||||
const parameters = this.paths[pathKey][method].parameters || this.paths[pathKey].parameters;
|
||||
const controller = toCamel((tags ? tags[0] : pathKey.split('/')[1]));
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
if (!operationId) {
|
||||
console.log(pathKey);
|
||||
}
|
||||
if (this.controllers[controller]) {
|
||||
this.controllers[controller][operationId] = {
|
||||
this.controllers[controller][uncapitalize(operationId)] = {
|
||||
parameters,
|
||||
responses,
|
||||
method,
|
||||
@@ -69,7 +82,7 @@ class ApiGenerator {
|
||||
pathKey: pathKey.replace(/{/g, '${'),
|
||||
};
|
||||
} else {
|
||||
this.controllers[controller] = { [operationId]: {
|
||||
this.controllers[controller] = { [uncapitalize(operationId)]: {
|
||||
parameters,
|
||||
responses,
|
||||
method,
|
||||
@@ -97,7 +110,7 @@ class ApiGenerator {
|
||||
]);
|
||||
|
||||
// const schemaProperties = schemas[schemaName].properties;
|
||||
const importEntities: any[] = [];
|
||||
const importEntities: { type: string, isClass: boolean }[] = [];
|
||||
|
||||
// add api class to file
|
||||
const apiClass = apiFile.addClass({
|
||||
@@ -111,29 +124,34 @@ class ApiGenerator {
|
||||
// for each operation add fetcher
|
||||
operationList.forEach((operation) => {
|
||||
const {
|
||||
requestBody, responses, parameters, method, pathKey, security,
|
||||
requestBody, responses, parameters, method, pathKey,
|
||||
} = controllerOperations[operation];
|
||||
|
||||
const queryParams: any[] = []; // { name, type }
|
||||
const bodyParam: any[] = []; // { name, type }
|
||||
const queryParams: { name: string, type: string, hasQuestionToken: boolean }[] = [];
|
||||
const bodyParam: { name: string, countedType: string, type?: string, isClass?: boolean, hasQuestionToken: boolean }[] = [];
|
||||
|
||||
|
||||
let contentType: string = '';
|
||||
|
||||
let hasResponseBodyType: /* boolean | ReturnType<schemaParamParser> */ false | [string, boolean, boolean, boolean, boolean] = false;
|
||||
let contentType = '';
|
||||
if (parameters) {
|
||||
parameters.forEach((p: any) => {
|
||||
const [
|
||||
pType, isArray, isClass, isImport,
|
||||
] = schemaParamParser(p.schema, this.openapi);
|
||||
parameters.forEach((link: {$ref: string}) => {
|
||||
const temp = link.$ref.split('/').pop()
|
||||
const parameter = this.openapi.components.parameters[temp!];
|
||||
|
||||
const {
|
||||
type, isArray, isClass, isImport,
|
||||
} = schemaParamParser(parameter.schema, this.openapi);
|
||||
|
||||
if (isImport) {
|
||||
importEntities.push({ type: pType, isClass });
|
||||
importEntities.push({ type, isClass });
|
||||
}
|
||||
if (p.in === 'query') {
|
||||
if (parameter.in === 'query') {
|
||||
queryParams.push({
|
||||
name: p.name, type: `${pType}${isArray ? '[]' : ''}`, hasQuestionToken: !p.required });
|
||||
name: parameter.name, type: `${type}${isArray ? '[]' : ''}`, hasQuestionToken: !parameter.required });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (queryParams.length > 0) {
|
||||
const imp = apiFile.getImportDeclaration((i) => {
|
||||
return i.getModuleSpecifierValue() === 'qs';
|
||||
@@ -144,62 +162,120 @@ class ApiGenerator {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (requestBody) {
|
||||
let content = requestBody.content;
|
||||
const { $ref }: { $ref: string } = requestBody;
|
||||
|
||||
if (!content && $ref) {
|
||||
const name = $ref.split('/').pop() as string;
|
||||
content = this.openapi.components.requestBodies[name].content;
|
||||
}
|
||||
const name = $ref.split('/').pop();
|
||||
const { content, required } = this.openapi.components.requestBodies[name!];
|
||||
|
||||
|
||||
[contentType] = Object.keys(content);
|
||||
const data = content[contentType];
|
||||
const data = content[contentType as keyof RequestBody['content']]!;
|
||||
|
||||
const [
|
||||
pType, isArray, isClass, isImport,
|
||||
] = schemaParamParser(data.schema, this.openapi);
|
||||
const {
|
||||
type, isArray, isClass, isImport,
|
||||
} = schemaParamParser(data.schema, this.openapi);
|
||||
|
||||
if (isImport) {
|
||||
importEntities.push({ type: pType, isClass });
|
||||
bodyParam.push({ name: pType.toLowerCase(), type: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`, isClass, pType });
|
||||
importEntities.push({ type: type, isClass });
|
||||
bodyParam.push({
|
||||
name: type.toLowerCase(),
|
||||
countedType: `${isClass ? 'I' : ''}${type}${isArray ? '[]' : ''}`,
|
||||
isClass,
|
||||
type,
|
||||
hasQuestionToken: !required
|
||||
});
|
||||
} else {
|
||||
bodyParam.push({ name: 'data', type: `${pType}${isArray ? '[]' : ''}` });
|
||||
bodyParam.push({
|
||||
name: 'data',
|
||||
countedType: `${type}${isArray ? '[]' : ''}`,
|
||||
hasQuestionToken: !required });
|
||||
|
||||
}
|
||||
}
|
||||
if (responses['200']) {
|
||||
const { content, headers } = responses['200'];
|
||||
if (content && (content['*/*'] || content['application/json'])) {
|
||||
const { schema, examples } = content['*/*'] || content['application/json'];
|
||||
|
||||
if (!schema) {
|
||||
process.exit(0);
|
||||
const responsesCodes = Object.keys(responses);
|
||||
const responsesSchema = responsesCodes.map((code) => {
|
||||
const refLink = responses[code].$ref.split('/').pop();
|
||||
const ref = this.openapi.components.responses[refLink];
|
||||
|
||||
interface ResponseSchema {
|
||||
code: number,
|
||||
[PROCESS_AS.JSON]?: ReturnType<typeof schemaParamParser>;
|
||||
[PROCESS_AS.TEXT]?: {
|
||||
schema?: ReturnType<typeof schemaParamParser>;
|
||||
xErrorCode?: string;
|
||||
onlyText: boolean;
|
||||
}
|
||||
|
||||
const propType = schemaParamParser(schema, this.openapi);
|
||||
const [pType, , isClass, isImport] = propType;
|
||||
|
||||
if (isImport) {
|
||||
importEntities.push({ type: pType, isClass });
|
||||
}
|
||||
hasResponseBodyType = propType;
|
||||
[PROCESS_AS.EMPTY]?: boolean;
|
||||
}
|
||||
}
|
||||
let returnType = '';
|
||||
if (hasResponseBodyType) {
|
||||
const [pType, isArray, isClass] = hasResponseBodyType as any;
|
||||
let data = `Promise<${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
|
||||
returnType = data;
|
||||
} else {
|
||||
returnType = 'Promise<number';
|
||||
}
|
||||
const shouldValidate = bodyParam.filter(b => b.isClass);
|
||||
if (shouldValidate.length > 0) {
|
||||
returnType += ' | string[]';
|
||||
}
|
||||
// append Error to default type return;
|
||||
returnType += ' | Error>';
|
||||
const responseSchema: ResponseSchema = { code: Number(code) };
|
||||
|
||||
if (!ref.content) {
|
||||
responseSchema[PROCESS_AS.EMPTY] = true;
|
||||
return responseSchema;
|
||||
}
|
||||
if (ref.content?.['application/json']) {
|
||||
const { schema } = ref.content['application/json'];
|
||||
responseSchema[PROCESS_AS.JSON] = schemaParamParser(schema, this.openapi);
|
||||
}
|
||||
if (ref.content?.['text/palin']) {
|
||||
const {
|
||||
"x-error-class": xErrorClass,
|
||||
"x-error-code": xErrorCode,
|
||||
} = ref.content['text/palin'];
|
||||
if (xErrorClass) {
|
||||
const schemaLink = xErrorClass.split('/').pop();
|
||||
const schema = this.openapi.components.schemas[schemaLink!];
|
||||
responseSchema[PROCESS_AS.TEXT] = {
|
||||
schema: schemaParamParser(schema, this.openapi),
|
||||
xErrorCode,
|
||||
onlyText: false,
|
||||
}
|
||||
} else {
|
||||
responseSchema[PROCESS_AS.TEXT] = { onlyText: true };
|
||||
}
|
||||
}
|
||||
return responseSchema;
|
||||
});
|
||||
|
||||
|
||||
let returnTypes = new Set();
|
||||
|
||||
bodyParam.forEach((param) => {
|
||||
if (param.isClass) {
|
||||
returnTypes.add(BAD_REQUES_HELPER);
|
||||
importEntities.push({ type: BAD_REQUES_HELPER, isClass: true });
|
||||
}
|
||||
})
|
||||
|
||||
responsesSchema.forEach((responseSchema) => {
|
||||
if (responseSchema[PROCESS_AS.JSON]) {
|
||||
const { type, isClass, isImport } = responseSchema[PROCESS_AS.JSON]!;
|
||||
returnTypes.add(type);
|
||||
if (isImport) {
|
||||
importEntities.push({ type: type, isClass });
|
||||
}
|
||||
}
|
||||
if (responseSchema[PROCESS_AS.TEXT]) {
|
||||
const { onlyText, schema } = responseSchema[PROCESS_AS.TEXT]!;
|
||||
if (onlyText) {
|
||||
returnTypes.add('string');
|
||||
} else {
|
||||
const { type, isClass, isImport } = schema!;
|
||||
returnTypes.add(type);
|
||||
if (isImport) {
|
||||
importEntities.push({ type, isClass });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (responseSchema[PROCESS_AS.EMPTY]) {
|
||||
returnTypes.add('number');
|
||||
}
|
||||
});
|
||||
returnTypes.add('undefined');
|
||||
const returnType = `Promise<${Array.from(returnTypes).join(' | ')}>`;
|
||||
|
||||
const fetcher = apiClass.addMethod({
|
||||
isAsync: true,
|
||||
@@ -211,23 +287,19 @@ class ApiGenerator {
|
||||
fetcher.addParameters(params);
|
||||
|
||||
fetcher.setBodyText((w) => {
|
||||
// Add data to URLSearchParams
|
||||
if (contentType === 'text/plain') {
|
||||
bodyParam.forEach((b) => {
|
||||
w.writeLine(`const params = String(${b.name});`);
|
||||
});
|
||||
} else {
|
||||
if (contentType === 'application/json') {
|
||||
const shouldValidate = bodyParam.filter(b => b.isClass);
|
||||
if (shouldValidate.length > 0) {
|
||||
w.writeLine(`const haveError: string[] = [];`);
|
||||
shouldValidate.forEach((b) => {
|
||||
w.writeLine(`const ${b.name}Valid = new ${b.pType}(${b.name});`);
|
||||
w.writeLine(`haveError.push(...${b.name}Valid.validate());`);
|
||||
w.writeLine(`haveError.push(...${b.name}.validate());`);
|
||||
});
|
||||
w.writeLine(`if (haveError.length > 0) {`);
|
||||
w.writeLine(` return Promise.resolve(haveError);`)
|
||||
w.writeLine(` return Promise.resolve(new ${BAD_REQUES_HELPER}(haveError));`)
|
||||
w.writeLine(`}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch return of fetch in case on queryParams
|
||||
if (queryParams.length > 0) {
|
||||
w.writeLine('const queryParams = {');
|
||||
@@ -243,37 +315,36 @@ class ApiGenerator {
|
||||
w.writeLine(` method: '${method.toUpperCase()}',`);
|
||||
|
||||
// add Fetch options
|
||||
if (contentType && contentType !== 'multipart/form-data') {
|
||||
w.writeLine(' headers: {');
|
||||
w.writeLine(` 'Content-Type': '${contentType}',`);
|
||||
w.writeLine(' },');
|
||||
}
|
||||
if (contentType) {
|
||||
switch (contentType) {
|
||||
case 'text/plain':
|
||||
w.writeLine(' body: params,');
|
||||
break;
|
||||
default:
|
||||
w.writeLine(` body: JSON.stringify(${bodyParam.map((b) => b.isClass ? `${b.name}Valid.serialize()` : b.name).join(', ')}),`);
|
||||
break;
|
||||
w.writeLine(` body: JSON.stringify(${bodyParam.map((b) => b.isClass ? `${b.name}.serialize()` : b.name).join(', ')}),`);
|
||||
}
|
||||
|
||||
w.writeLine('}).then(async (res) => {');
|
||||
responsesSchema.forEach((responseSchema) => {
|
||||
const { code } = responseSchema;
|
||||
w.writeLine(` if (res.status === ${code}) {`);
|
||||
if (responseSchema[PROCESS_AS.EMPTY]) {
|
||||
w.writeLine(' return res.status;');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle response
|
||||
if (hasResponseBodyType) {
|
||||
w.writeLine('}).then(async (res) => {');
|
||||
w.writeLine(' if (res.status === 200) {');
|
||||
w.writeLine(' return res.json();');
|
||||
} else {
|
||||
w.writeLine('}).then(async (res) => {');
|
||||
w.writeLine(' if (res.status === 200) {');
|
||||
w.writeLine(' return res.status;');
|
||||
}
|
||||
|
||||
// Handle Error
|
||||
w.writeLine(' } else {');
|
||||
w.writeLine(' return new Error(String(res.status));');
|
||||
w.writeLine(' }');
|
||||
if (responseSchema[PROCESS_AS.TEXT]?.onlyText) {
|
||||
w.writeLine(' return res.text();')
|
||||
}
|
||||
if (responseSchema[PROCESS_AS.JSON] && responseSchema[PROCESS_AS.TEXT]) {
|
||||
const { type } = responseSchema[PROCESS_AS.JSON]!;
|
||||
const { schema, xErrorCode } = responseSchema[PROCESS_AS.TEXT]!;
|
||||
const { type: errType } = schema!;
|
||||
w.writeLine(' try {');
|
||||
w.writeLine(` return new ${type}(await res.json());`);
|
||||
w.writeLine(' } catch {');
|
||||
w.writeLine(` return new ${errType}({ msg: await res.text() code: ${xErrorCode}} as any);`);
|
||||
w.writeLine(' }');
|
||||
}
|
||||
if (responseSchema[PROCESS_AS.JSON]) {
|
||||
const { type } = responseSchema[PROCESS_AS.JSON]!;
|
||||
w.writeLine(` return new ${type}(await res.json());`);
|
||||
}
|
||||
w.writeLine(` }`);
|
||||
})
|
||||
w.writeLine('})');
|
||||
});
|
||||
});
|
||||
@@ -288,17 +359,16 @@ class ApiGenerator {
|
||||
}
|
||||
});
|
||||
imports.sort((a,b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||
const { type: pType, isClass } = ie;
|
||||
const { type: type, isClass } = ie;
|
||||
if (isClass) {
|
||||
apiFile.addImportDeclaration({
|
||||
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
|
||||
defaultImport: pType,
|
||||
namedImports: [`I${pType}`],
|
||||
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${type}`,
|
||||
defaultImport: type,
|
||||
});
|
||||
} else {
|
||||
apiFile.addImportDeclaration({
|
||||
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
|
||||
namedImports: [pType],
|
||||
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${type}`,
|
||||
namedImports: [type],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,8 +3,8 @@ import * as path from 'path';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import * as morph from 'ts-morph';
|
||||
|
||||
import { ENT_DIR } from '../../consts';
|
||||
import { TYPES, toCamel, schemaParamParser, uncapitalize } from './utils';
|
||||
import { ENT_DIR, BAD_REQUES_HELPER } from '../../consts';
|
||||
import { TYPES, toCamel, schemaParamParser, capitalize, OpenApi, Schema } from './utils';
|
||||
|
||||
const { Project, QuoteKind } = morph;
|
||||
|
||||
@@ -17,7 +17,6 @@ if (!fs.existsSync(EntDir)) {
|
||||
class EntitiesGenerator {
|
||||
project = new Project({
|
||||
tsConfigFilePath: './tsconfig.json',
|
||||
addFilesFromTsConfig: false,
|
||||
manipulationSettings: {
|
||||
quoteKind: QuoteKind.Single,
|
||||
usePrefixAndSuffixTextForRename: false,
|
||||
@@ -25,491 +24,480 @@ class EntitiesGenerator {
|
||||
},
|
||||
});
|
||||
|
||||
openapi: Record<string, any>;
|
||||
openapi: OpenApi;
|
||||
|
||||
schemas: Record<string, any>;
|
||||
schemas: Record<string, Schema>;
|
||||
|
||||
schemaNames: string[];
|
||||
|
||||
entities: morph.SourceFile[] = [];
|
||||
|
||||
constructor(openapi: Record<string, any>) {
|
||||
constructor(openapi: OpenApi) {
|
||||
this.openapi = openapi;
|
||||
this.schemas = openapi.components.schemas;
|
||||
this.schemaNames = Object.keys(this.schemas);
|
||||
this.generateEntities();
|
||||
this.generateUtils();
|
||||
}
|
||||
|
||||
|
||||
generateUtils = () => {
|
||||
const helperFile = this.project.createSourceFile(`${EntDir}/${BAD_REQUES_HELPER}.ts`);
|
||||
helperFile.addImportDeclaration({
|
||||
moduleSpecifier: `./BadRequestResp`,
|
||||
defaultImport: 'BadRequestResp',
|
||||
});
|
||||
helperFile.addImportDeclaration({
|
||||
moduleSpecifier: `./ErrorCode`,
|
||||
namedImports: ['ErrorCode'],
|
||||
});
|
||||
const helperClass = helperFile.addClass({
|
||||
name: 'BadRequestHelper',
|
||||
isDefaultExport: true,
|
||||
extends: 'BadRequestResp',
|
||||
properties: [{
|
||||
type: 'string[]',
|
||||
name: 'fields'
|
||||
}]
|
||||
});
|
||||
const helperConstructor = helperClass.addConstructor({
|
||||
parameters: [{
|
||||
type: 'string[]',
|
||||
name: 'fields'
|
||||
}],
|
||||
});
|
||||
helperConstructor.setBodyText((w) => {
|
||||
w.writeLine('super({ code: ErrorCode.JSN001, msg: \'Wrong fields value\' });');
|
||||
w.writeLine('this.fields = fields;')
|
||||
});
|
||||
this.entities.push(helperFile);
|
||||
}
|
||||
|
||||
generateEntities = () => {
|
||||
this.schemaNames.forEach(this.generateEntity);
|
||||
};
|
||||
|
||||
generateEntity = (sName: string) => {
|
||||
const { properties, type, oneOf } = this.schemas[sName];
|
||||
generateEntity = (schemaName: string) => {
|
||||
const { properties, type, oneOf, enum: en } = this.schemas[schemaName];
|
||||
const notAClass = !properties && TYPES[type as keyof typeof TYPES];
|
||||
|
||||
if (oneOf) {
|
||||
this.generateOneOf(sName);
|
||||
this.generateOneOf(schemaName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (en) {
|
||||
this.generateEnum(schemaName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (notAClass) {
|
||||
this.generateEnum(sName);
|
||||
this.generatePrimitive(schemaName)
|
||||
} else {
|
||||
this.generateClass(sName);
|
||||
this.generateClass(schemaName);
|
||||
}
|
||||
};
|
||||
|
||||
generateEnum = (sName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||
generatePrimitive = (schemaName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${schemaName}.ts`);
|
||||
entityFile.addStatements([
|
||||
'// This file was autogenerated. Please do not change.',
|
||||
'// All changes will be overwrited on commit.',
|
||||
'',
|
||||
]);
|
||||
const { type: schemaType, description, pattern } = this.schemas[schemaName];
|
||||
if (description) {
|
||||
entityFile.addStatements(['\n/*', `Description: ${description}`, '*/\n']);
|
||||
}
|
||||
|
||||
const { enum: enumMembers } = this.schemas[sName];
|
||||
if (pattern) {
|
||||
entityFile.addStatements(`const pattern = new RegExp('${pattern}')`);
|
||||
}
|
||||
|
||||
const type: string = TYPES[schemaType as keyof typeof TYPES];
|
||||
|
||||
const entityClass = entityFile.addClass({
|
||||
name: schemaName,
|
||||
isDefaultExport: true,
|
||||
extends: capitalize(type),
|
||||
});
|
||||
|
||||
|
||||
|
||||
const ctor = entityClass.addConstructor({
|
||||
parameters: [{
|
||||
name: 'v',
|
||||
type,
|
||||
|
||||
}],
|
||||
});
|
||||
ctor.setBodyText((w) => {
|
||||
const { minLength, minimum, maxLength, maximum } = this.schemas[schemaName];
|
||||
|
||||
if (type === 'string') {
|
||||
if (pattern) {
|
||||
w.writeLine('if (!v.match(pattern)) {');
|
||||
w.writeLine(' throw new Error();');
|
||||
w.writeLine('}');
|
||||
}
|
||||
if (typeof minLength === 'number') {
|
||||
w.writeLine(`if (v.length < ${minLength}) {`);
|
||||
w.writeLine(' throw new Error();');
|
||||
w.writeLine('}');
|
||||
}
|
||||
if (typeof maxLength === 'number') {
|
||||
w.writeLine(`if (v.length > ${maxLength}) {`);
|
||||
w.writeLine(' throw new Error();');
|
||||
w.writeLine('}');
|
||||
}
|
||||
}
|
||||
if (type === 'number') {
|
||||
if (typeof minimum === 'number') {
|
||||
w.writeLine(`if (v.length < ${minimum}) {`);
|
||||
w.writeLine(' throw new Error();');
|
||||
w.writeLine('}');
|
||||
}
|
||||
if (typeof maximum === 'number') {
|
||||
w.writeLine(`if (v.length > ${maximum}) {`);
|
||||
w.writeLine(' throw new Error();');
|
||||
w.writeLine('}');
|
||||
}
|
||||
}
|
||||
w.writeLine('super(v);');
|
||||
});
|
||||
|
||||
this.entities.push(entityFile);
|
||||
};
|
||||
|
||||
|
||||
generateEnum = (schemaName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${schemaName}.ts`);
|
||||
entityFile.addStatements([
|
||||
'// This file was autogenerated. Please do not change.',
|
||||
'',
|
||||
]);
|
||||
const { enum: enumMembers, description, example } = this.schemas[schemaName];
|
||||
if (description) {
|
||||
entityFile.addStatements(['\n/*', `Description: ${description}`, '*/\n']);
|
||||
}
|
||||
entityFile.addEnum({
|
||||
name: sName,
|
||||
members: enumMembers.map((e: string) => ({ name: e.toUpperCase(), value: e })),
|
||||
name: schemaName,
|
||||
members: enumMembers!.map((e: string) => ({ name: e.toUpperCase(), value: e })),
|
||||
isExported: true,
|
||||
});
|
||||
|
||||
this.entities.push(entityFile);
|
||||
};
|
||||
|
||||
generateOneOf = (sName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||
generateOneOf = (schemaName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${schemaName}.ts`);
|
||||
entityFile.addStatements([
|
||||
'// This file was autogenerated. Please do not change.',
|
||||
'// All changes will be overwrited on commit.',
|
||||
'',
|
||||
]);
|
||||
const importEntities: { type: string, isClass: boolean }[] = [];
|
||||
const entities = this.schemas[sName].oneOf.map((elem: any) => {
|
||||
const [
|
||||
pType, isArray, isClass, isImport,
|
||||
] = schemaParamParser(elem, this.openapi);
|
||||
importEntities.push({ type: pType, isClass });
|
||||
return { type: pType, isArray };
|
||||
const entities = this.schemas[schemaName].oneOf.map((elem: any) => {
|
||||
const {
|
||||
type: type, isArray, isClass, isImport,
|
||||
} = schemaParamParser(elem, this.openapi);
|
||||
importEntities.push({ type: type, isClass });
|
||||
return { type: type, isArray };
|
||||
});
|
||||
entityFile.addTypeAlias({
|
||||
name: sName,
|
||||
name: schemaName,
|
||||
isExported: true,
|
||||
type: entities.map((e: any) => e.isArray ? `I${e.type}[]` : `I${e.type}`).join(' | '),
|
||||
})
|
||||
|
||||
// add import
|
||||
importEntities.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||
const { type: pType, isClass } = ie;
|
||||
const { type: type, isClass } = ie;
|
||||
if (isClass) {
|
||||
entityFile.addImportDeclaration({
|
||||
moduleSpecifier: `./${pType}`,
|
||||
namedImports: [`I${pType}`],
|
||||
moduleSpecifier: `./${type}`,
|
||||
namedImports: [`I${type}`],
|
||||
});
|
||||
} else {
|
||||
entityFile.addImportDeclaration({
|
||||
moduleSpecifier: `./${pType}`,
|
||||
namedImports: [pType],
|
||||
moduleSpecifier: `./${type}`,
|
||||
namedImports: [type],
|
||||
});
|
||||
}
|
||||
});
|
||||
this.entities.push(entityFile);
|
||||
}
|
||||
|
||||
generateClass = (sName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||
generateClass = (schemaName: string) => {
|
||||
const entityFile = this.project.createSourceFile(`${EntDir}/${schemaName}.ts`);
|
||||
entityFile.addStatements([
|
||||
'// This file was autogenerated. Please do not change.',
|
||||
'// All changes will be overwrited on commit.',
|
||||
'',
|
||||
]);
|
||||
|
||||
let { properties, required, allOf, $ref } = this.schemas[schemaName];
|
||||
|
||||
if (allOf) {
|
||||
const refLink: string = allOf.find((obj: Record<string, any>) => obj.$ref).$ref;
|
||||
let ref: any = refLink.split('/')
|
||||
ref = ref.pop();
|
||||
|
||||
const reasign = allOf.find((obj: Record<string, any>) => !obj.$ref);
|
||||
const newSchema: Schema = { ...this.schemas[ref], ...reasign };
|
||||
|
||||
properties = newSchema.properties;
|
||||
required = newSchema.required;
|
||||
}
|
||||
|
||||
const { properties: sProps, required, $ref, additionalProperties } = this.schemas[sName];
|
||||
if ($ref) {
|
||||
const temp = $ref.split('/');
|
||||
const importSchemaName = `${temp[temp.length - 1]}`;
|
||||
const refLink = $ref.split('/').pop()!;
|
||||
entityFile.addImportDeclaration({
|
||||
defaultImport: importSchemaName,
|
||||
moduleSpecifier: `./${importSchemaName}`,
|
||||
namedImports: [`I${importSchemaName}`],
|
||||
defaultImport: refLink,
|
||||
moduleSpecifier: `./${refLink}`,
|
||||
namedImports: [`I${refLink}`],
|
||||
});
|
||||
|
||||
entityFile.addTypeAlias({
|
||||
name: `I${sName}`,
|
||||
type: `I${importSchemaName}`,
|
||||
name: `I${schemaName}`,
|
||||
type: `I${refLink}`,
|
||||
isExported: true,
|
||||
})
|
||||
|
||||
entityFile.addStatements(`export default ${importSchemaName};`);
|
||||
const entityClass = entityFile.addClass({
|
||||
name: schemaName,
|
||||
isDefaultExport: true,
|
||||
extends: refLink,
|
||||
})
|
||||
const ctor = entityClass.addConstructor({
|
||||
parameters: [{
|
||||
name: 'props',
|
||||
type: `I${schemaName}`,
|
||||
}],
|
||||
})
|
||||
ctor.setBodyText((w) => {
|
||||
w.writeLine('super(props);')
|
||||
});
|
||||
this.entities.push(entityFile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const importEntities: { type: string, isClass: boolean }[] = [];
|
||||
const entityInterface = entityFile.addInterface({
|
||||
name: `I${sName}`,
|
||||
name: `I${schemaName}`,
|
||||
isExported: true,
|
||||
});
|
||||
|
||||
const sortedSProps = Object.keys(sProps || {}).sort();
|
||||
const additionalPropsOnly = additionalProperties && sortedSProps.length === 0;
|
||||
|
||||
const sortedProperties = Object.keys(properties || {}).sort();
|
||||
let importEntities: { type: string, isClass: boolean }[] = [];
|
||||
|
||||
type SortedPropertiesTypesValues = ReturnType<typeof schemaParamParser> & {
|
||||
computedType: string;
|
||||
isRequired: boolean;
|
||||
}
|
||||
const sortedPropertiesTypes = sortedProperties.reduce((data, propertyName) => {
|
||||
const isRequired = !!(required && required.includes(propertyName));
|
||||
const parsed = schemaParamParser(properties![propertyName], this.openapi);
|
||||
data[propertyName] = {
|
||||
...parsed,
|
||||
isRequired,
|
||||
computedType: `${parsed.type}${parsed.isArray ? '[]' : ''}${isRequired ? '' : ' | undefined'}`
|
||||
};
|
||||
return data;
|
||||
}, {} as Record<string, SortedPropertiesTypesValues>);
|
||||
|
||||
// add server response interface to entityFile
|
||||
sortedSProps.forEach((sPropName) => {
|
||||
const [
|
||||
pType, isArray, isClass, isImport, isAdditional
|
||||
] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
sortedProperties.forEach((propertyName) => {
|
||||
const {
|
||||
type, isArray, isClass, isImport
|
||||
} = sortedPropertiesTypes[propertyName];
|
||||
|
||||
if (isImport) {
|
||||
importEntities.push({ type: pType, isClass });
|
||||
importEntities.push({ type: type, isClass });
|
||||
}
|
||||
const propertyType = isAdditional
|
||||
? `{ [key: string]: ${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''} }`
|
||||
: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
|
||||
entityInterface.addProperty({
|
||||
name: sPropName,
|
||||
type: propertyType,
|
||||
name: propertyName,
|
||||
type: `${isClass ? 'I' : ''}${type}${isArray ? '[]' : ''}`,
|
||||
hasQuestionToken: !(
|
||||
(required && required.includes(sPropName)) || sProps[sPropName].required
|
||||
(required && required.includes(propertyName)) || properties![propertyName].required
|
||||
),
|
||||
});
|
||||
});
|
||||
if (additionalProperties) {
|
||||
const [
|
||||
pType, isArray, isClass, isImport, isAdditional
|
||||
] = schemaParamParser(additionalProperties, this.openapi);
|
||||
|
||||
if (isImport) {
|
||||
importEntities.push({ type: pType, isClass });
|
||||
}
|
||||
const type = isAdditional
|
||||
? `{ [key: string]: ${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''} }`
|
||||
: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
|
||||
entityInterface.addIndexSignature({
|
||||
keyName: 'key',
|
||||
keyType: 'string',
|
||||
returnType: additionalPropsOnly ? type : `${type} | undefined`,
|
||||
});
|
||||
}
|
||||
|
||||
// add import
|
||||
const imports: { type: string, isClass: boolean }[] = [];
|
||||
const types: string[] = [];
|
||||
importEntities.forEach((i) => {
|
||||
importEntities = importEntities.filter((i) => {
|
||||
const { type } = i;
|
||||
if (!types.includes(type)) {
|
||||
imports.push(i);
|
||||
types.push(type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
imports.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||
const { type: pType, isClass } = ie;
|
||||
importEntities.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||
const { type: type, isClass } = ie;
|
||||
if (isClass) {
|
||||
entityFile.addImportDeclaration({
|
||||
defaultImport: pType,
|
||||
moduleSpecifier: `./${pType}`,
|
||||
namedImports: [`I${pType}`],
|
||||
defaultImport: type,
|
||||
moduleSpecifier: `./${type}`,
|
||||
namedImports: [`I${type}`],
|
||||
});
|
||||
} else {
|
||||
entityFile.addImportDeclaration({
|
||||
moduleSpecifier: `./${pType}`,
|
||||
namedImports: [pType],
|
||||
moduleSpecifier: `./${type}`,
|
||||
namedImports: [type],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const entityClass = entityFile.addClass({
|
||||
name: sName,
|
||||
name: schemaName,
|
||||
isDefaultExport: true,
|
||||
});
|
||||
|
||||
// addProperties to class;
|
||||
sortedSProps.forEach((sPropName) => {
|
||||
const [pType, isArray, isClass, isImport, isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
|
||||
const isRequred = (required && required.includes(sPropName))
|
||||
|| sProps[sPropName].required;
|
||||
|
||||
const propertyType = isAdditional
|
||||
? `{ [key: string]: ${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'} }`
|
||||
: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`;
|
||||
sortedProperties.forEach((propertyName) => {
|
||||
const { type, isArray, isClass, isEnum, isRequired, computedType } = sortedPropertiesTypes[propertyName];
|
||||
|
||||
entityClass.addProperty({
|
||||
name: `_${sPropName}`,
|
||||
name: `_${propertyName}`,
|
||||
isReadonly: true,
|
||||
type: propertyType,
|
||||
type: computedType,
|
||||
});
|
||||
|
||||
const getter = entityClass.addGetAccessor({
|
||||
name: toCamel(sPropName),
|
||||
returnType: propertyType,
|
||||
statements: [`return this._${sPropName};`],
|
||||
name: toCamel(propertyName),
|
||||
returnType: computedType,
|
||||
statements: [`return this._${propertyName};`],
|
||||
});
|
||||
const { description, example, minItems, maxItems, maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||
const { description, example, minItems, maxItems, maxLength, minLength, maximum, minimum } = properties![propertyName];
|
||||
|
||||
if (description || example) {
|
||||
getter.addJsDoc(`${example ? `Description: ${description}` : ''}${example ? `\nExample: ${example}` : ''}`);
|
||||
}
|
||||
|
||||
if (minItems) {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MinItems`,
|
||||
statements: [`return ${minItems};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MinItems`,
|
||||
initializer: `${minItems}`,
|
||||
});
|
||||
}
|
||||
if (maxItems) {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MaxItems`,
|
||||
statements: [`return ${maxItems};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MaxItems`,
|
||||
initializer: `${maxItems}`,
|
||||
});
|
||||
}
|
||||
if (typeof minLength === 'number') {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MinLength`,
|
||||
statements: [`return ${minLength};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MinLength`,
|
||||
initializer: `${minLength}`,
|
||||
});
|
||||
}
|
||||
if (maxLength) {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MaxLength`,
|
||||
statements: [`return ${maxLength};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MaxLength`,
|
||||
initializer: `${maxLength}`,
|
||||
});
|
||||
}
|
||||
if (typeof minimum === 'number') {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MinValue`,
|
||||
statements: [`return ${minimum};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MinValue`,
|
||||
initializer: `${minimum}`,
|
||||
});
|
||||
}
|
||||
if (maximum) {
|
||||
entityClass.addGetAccessor({
|
||||
entityClass.addProperty({
|
||||
isStatic: true,
|
||||
name: `${toCamel(sPropName)}MaxValue`,
|
||||
statements: [`return ${maximum};`],
|
||||
isReadonly: true,
|
||||
name: `${capitalize(toCamel(propertyName))}MaxValue`,
|
||||
initializer: `${maximum}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!(isArray && isClass) && !isClass) {
|
||||
const isEnum = !isClass && isImport;
|
||||
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
|
||||
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||
const haveValidationFields = maxLength || typeof minLength === 'number' || maximum || typeof minimum === 'number';
|
||||
if (isRequired || haveValidationFields) {
|
||||
const prop = toCamel(sPropName);
|
||||
const validateField = entityClass.addMethod({
|
||||
isStatic: true,
|
||||
name: `${prop}Validate`,
|
||||
returnType: `boolean`,
|
||||
parameters: [{
|
||||
name: prop,
|
||||
type: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`,
|
||||
}],
|
||||
})
|
||||
|
||||
validateField.setBodyText((w) => {
|
||||
w.write('return ');
|
||||
const nonRequiredCall = isRequired ? prop : `!${prop} ? true : ${prop}`;
|
||||
if (pType === 'string') {
|
||||
if (isArray) {
|
||||
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && (typeof p === 'string' && !!p.trim()), true)`);
|
||||
} else {
|
||||
if (typeof minLength === 'number' && maxLength) {
|
||||
w.write(`(${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength})`);
|
||||
}
|
||||
if (typeof minLength !== 'number' || !maxLength) {
|
||||
w.write(`${isRequired ? `typeof ${prop} === 'string'` : `!${prop} ? true : typeof ${prop} === 'string'`} && !!${nonRequiredCall}.trim()`);
|
||||
}
|
||||
}
|
||||
} else if (pType === 'number') {
|
||||
if (isArray) {
|
||||
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && typeof p === 'number', true)`);
|
||||
} else {
|
||||
if (typeof minimum === 'number' && maximum) {
|
||||
w.write(`${isRequired ? `${prop} >= ${minimum} && ${prop} <= ${maximum}` : `!${prop} ? true : ((${prop} >= ${minimum}) && (${prop} <= ${maximum}))`}`);
|
||||
}
|
||||
if (typeof minimum !== 'number' || !maximum) {
|
||||
w.write(`${isRequired ? `typeof ${prop} === 'number'` : `!${prop} ? true : typeof ${prop} === 'number'`}`);
|
||||
}
|
||||
}
|
||||
} else if (pType === 'boolean') {
|
||||
w.write(`${isRequired ? `typeof ${prop} === 'boolean'` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
|
||||
} else if (isEnum) {
|
||||
if (isArray){
|
||||
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && Object.keys(${pType}).includes(${prop}), true)`);
|
||||
} else {
|
||||
w.write(`${isRequired ? `Object.keys(${pType}).includes(${prop})` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
|
||||
}
|
||||
}
|
||||
|
||||
w.write(';');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (additionalProperties) {
|
||||
const [
|
||||
pType, isArray, isClass, isImport, isAdditional
|
||||
] = schemaParamParser(additionalProperties, this.openapi);
|
||||
const type = `Record<string, ${pType}${isArray ? '[]' : ''}>`;
|
||||
|
||||
entityClass.addProperty({
|
||||
name: additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`,
|
||||
isReadonly: true,
|
||||
type: type,
|
||||
});
|
||||
}
|
||||
|
||||
// add constructor;
|
||||
const ctor = entityClass.addConstructor({
|
||||
parameters: [{
|
||||
name: 'props',
|
||||
type: `I${sName}`,
|
||||
type: `I${schemaName}`,
|
||||
}],
|
||||
});
|
||||
ctor.setBodyText((w) => {
|
||||
if (additionalProperties) {
|
||||
const [
|
||||
pType, isArray, isClass, isImport, isAdditional
|
||||
] = schemaParamParser(additionalProperties, this.openapi);
|
||||
w.writeLine(`this.${additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`} = Object.entries(props).reduce<Record<string, ${pType}>>((prev, [key, value]) => {`);
|
||||
if (isClass) {
|
||||
w.writeLine(` prev[key] = new ${pType}(value!);`);
|
||||
sortedProperties.forEach((propertyName) => {
|
||||
const { type, isArray, isClass, isRequired } = sortedPropertiesTypes[propertyName];
|
||||
|
||||
const indent = !isRequired ? ' ' : '';
|
||||
if (!isRequired) {
|
||||
if ((type === 'boolean' || type === 'number' || type ==='string') && !isClass && !isArray) {
|
||||
w.writeLine(`if (typeof props.${propertyName} === '${type}') {`);
|
||||
} else {
|
||||
w.writeLine(`if (props.${propertyName}) {`);
|
||||
}
|
||||
}
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(`${indent}this._${propertyName} = props.${propertyName}.map((p) => new ${type}(p));`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(`${indent}this._${propertyName} = new ${type}(props.${propertyName});`);
|
||||
} else {
|
||||
w.writeLine(' prev[key] = value!;')
|
||||
}
|
||||
w.writeLine(' return prev;');
|
||||
w.writeLine('}, {})');
|
||||
return;
|
||||
}
|
||||
sortedSProps.forEach((sPropName) => {
|
||||
const [
|
||||
pType, isArray, isClass, , isAdditional
|
||||
] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
const req = (required && required.includes(sPropName))
|
||||
|| sProps[sPropName].required;
|
||||
if (!req) {
|
||||
if ((pType === 'boolean' || pType === 'number' || pType ==='string') && !isClass && !isArray) {
|
||||
w.writeLine(`if (typeof props.${sPropName} === '${pType}') {`);
|
||||
if (type === 'string' && !isArray) {
|
||||
w.writeLine(`${indent}this._${propertyName} = props.${propertyName}.trim();`);
|
||||
} else {
|
||||
w.writeLine(`if (props.${sPropName}) {`);
|
||||
w.writeLine(`${indent}this._${propertyName} = props.${propertyName};`);
|
||||
}
|
||||
}
|
||||
if (isAdditional) {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => {
|
||||
return { ...prev, [key]: new ${pType}(p[key])};
|
||||
},{}))`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||
return { ...prev, [key]: new ${pType}(props.${sPropName}[key])};
|
||||
},{})`);
|
||||
} else {
|
||||
if (pType === 'string' && !isArray) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||
return { ...prev, [key]: props.${sPropName}[key].trim()};
|
||||
},{})`);
|
||||
} else {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||
return { ...prev, [key]: props.${sPropName}[key]};
|
||||
},{})`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => new ${pType}(p));`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = new ${pType}(props.${sPropName});`);
|
||||
} else {
|
||||
if (pType === 'string' && !isArray) {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.trim();`);
|
||||
} else {
|
||||
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName};`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!req) {
|
||||
if (!isRequired) {
|
||||
w.writeLine('}');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// add serialize method;
|
||||
const serialize = entityClass.addMethod({
|
||||
isStatic: false,
|
||||
name: 'serialize',
|
||||
returnType: `I${sName}`,
|
||||
returnType: `I${schemaName}`,
|
||||
});
|
||||
serialize.setBodyText((w) => {
|
||||
if (additionalProperties) {
|
||||
const [
|
||||
pType, isArray, isClass, isImport, isAdditional
|
||||
] = schemaParamParser(additionalProperties, this.openapi);
|
||||
w.writeLine(`return Object.entries(this.${additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`}).reduce<Record<string, ${isClass ? 'I' : ''}${pType}>>((prev, [key, value]) => {`);
|
||||
if (isClass) {
|
||||
w.writeLine(` prev[key] = value.serialize();`);
|
||||
} else {
|
||||
w.writeLine(' prev[key] = value;')
|
||||
}
|
||||
w.writeLine(' return prev;');
|
||||
w.writeLine('}, {})');
|
||||
return;
|
||||
}
|
||||
w.writeLine(`const data: I${sName} = {`);
|
||||
w.writeLine(`const data: I${schemaName} = {`);
|
||||
|
||||
const unReqFields: string[] = [];
|
||||
sortedSProps.forEach((sPropName) => {
|
||||
const req = (required && required.includes(sPropName))
|
||||
|| sProps[sPropName].required;
|
||||
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
if (!req) {
|
||||
unReqFields.push(sPropName);
|
||||
|
||||
sortedProperties.forEach((propertyName) => {
|
||||
const {isArray, isClass, isRequired } = sortedPropertiesTypes[propertyName];
|
||||
if (!isRequired) {
|
||||
unReqFields.push(propertyName);
|
||||
return;
|
||||
}
|
||||
if (isAdditional) {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }))),`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce<Record<string, any>>((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {}),`);
|
||||
} else {
|
||||
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] })),`);
|
||||
}
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` ${propertyName}: this._${propertyName}.map((p) => p.serialize()),`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` ${propertyName}: this._${propertyName}.serialize(),`);
|
||||
} else {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => p.serialize()),`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` ${sPropName}: this._${sPropName}.serialize(),`);
|
||||
} else {
|
||||
w.writeLine(` ${sPropName}: this._${sPropName},`);
|
||||
}
|
||||
w.writeLine(` ${propertyName}: this._${propertyName},`);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
w.writeLine('};');
|
||||
unReqFields.forEach((sPropName) => {
|
||||
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
w.writeLine(`if (typeof this._${sPropName} !== 'undefined') {`);
|
||||
if (isAdditional) {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }), {}));`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {});`);
|
||||
} else {
|
||||
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] }), {});`);
|
||||
}
|
||||
|
||||
unReqFields.forEach((propertyName) => {
|
||||
const { isArray, isClass } = sortedPropertiesTypes[propertyName];
|
||||
w.writeLine(`if (typeof this._${propertyName} !== 'undefined') {`);
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` data.${propertyName} = this._${propertyName}.map((p) => p.serialize());`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` data.${propertyName} = this._${propertyName}.serialize();`);
|
||||
} else {
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => p.serialize());`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` data.${sPropName} = this._${sPropName}.serialize();`);
|
||||
} else {
|
||||
w.writeLine(` data.${sPropName} = this._${sPropName};`);
|
||||
|
||||
}
|
||||
w.writeLine(` data.${propertyName} = this._${propertyName};`);
|
||||
|
||||
}
|
||||
|
||||
w.writeLine(`}`);
|
||||
});
|
||||
w.writeLine('return data;');
|
||||
@@ -522,74 +510,55 @@ class EntitiesGenerator {
|
||||
returnType: `string[]`,
|
||||
})
|
||||
validate.setBodyText((w) => {
|
||||
if (additionalPropsOnly) {
|
||||
w.writeLine('return []')
|
||||
return;
|
||||
}
|
||||
w.writeLine('const validate = {');
|
||||
Object.keys(sProps || {}).forEach((sPropName) => {
|
||||
const [pType, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||
w.writeLine('const validateRequired = {');
|
||||
Object.keys(properties || {}).forEach((propertyName) => {
|
||||
const { isArray, isClass, type, isRequired } = sortedPropertiesTypes[propertyName];
|
||||
const { maxLength, minLength, maximum, minimum } = properties![propertyName];
|
||||
|
||||
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||
|
||||
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
|
||||
const nonRequiredCall = isRequired ? `this._${sPropName}` : `!this._${sPropName} ? true : this._${sPropName}`;
|
||||
const nonRequiredCall = isRequired ? `this._${propertyName}` : `!this._${propertyName} ? true : this._${propertyName}`;
|
||||
|
||||
if (isArray && isClass) {
|
||||
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && p.validate().length === 0, true),`);
|
||||
} else if (isClass && !isAdditional) {
|
||||
w.writeLine(` ${sPropName}: ${nonRequiredCall}.validate().length === 0,`);
|
||||
w.writeLine(` ${propertyName}: ${nonRequiredCall}.reduce<boolean>((result, p) => result && p.validate().length === 0, true),`);
|
||||
} else if (isClass) {
|
||||
w.writeLine(` ${propertyName}: ${nonRequiredCall}.validate().length === 0,`);
|
||||
} else {
|
||||
if (pType === 'string') {
|
||||
if (type === 'string') {
|
||||
if (isArray) {
|
||||
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'string', true),`);
|
||||
w.writeLine(` ${propertyName}: ${nonRequiredCall}.reduce<boolean>((result, p) => result && typeof p === 'string', true),`);
|
||||
} else {
|
||||
if (typeof minLength === 'number' && maxLength) {
|
||||
w.writeLine(` ${sPropName}: (${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength}),`);
|
||||
w.writeLine(` ${propertyName}: (${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength}),`);
|
||||
}
|
||||
if (typeof minLength !== 'number' || !maxLength) {
|
||||
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'string'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'string'`} && !this._${sPropName} ? true : this._${sPropName},`);
|
||||
w.writeLine(` ${propertyName}: ${isRequired ? `typeof this._${propertyName} === 'string' && !!this._${propertyName}.trim()` : `!this._${propertyName} ? true : typeof this._${propertyName} === 'string'`},`);
|
||||
}
|
||||
}
|
||||
} else if (pType === 'number') {
|
||||
} else if (type === 'number') {
|
||||
if (isArray) {
|
||||
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'number', true),`);
|
||||
w.writeLine(` ${propertyName}: ${nonRequiredCall}.reduce<boolean>((result, p) => result && typeof p === 'number', true),`);
|
||||
} else {
|
||||
if (typeof minimum === 'number' && maximum) {
|
||||
w.writeLine(` ${sPropName}: ${isRequired ? `this._${sPropName} >= ${minimum} && this._${sPropName} <= ${maximum}` : `!this._${sPropName} ? true : ((this._${sPropName} >= ${minimum}) && (this._${sPropName} <= ${maximum}))`},`);
|
||||
w.writeLine(` ${propertyName}: ${isRequired ? `this._${propertyName} >= ${minimum} && this._${propertyName} <= ${maximum}` : `!this._${propertyName} ? true : ((this._${propertyName} >= ${minimum}) && (this._${propertyName} <= ${maximum}))`},`);
|
||||
}
|
||||
if (typeof minimum !== 'number' || !maximum) {
|
||||
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'number'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'number'`},`);
|
||||
w.writeLine(` ${propertyName}: ${isRequired ? `typeof this._${propertyName} === 'number'` : `!this._${propertyName} ? true : typeof this._${propertyName} === 'number'`},`);
|
||||
}
|
||||
}
|
||||
} else if (pType === 'boolean') {
|
||||
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'boolean'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'boolean'`},`);
|
||||
} else if (type === 'boolean') {
|
||||
w.writeLine(` ${propertyName}: ${isRequired ? `typeof this._${propertyName} === 'boolean'` : `!this._${propertyName} ? true : typeof this._${propertyName} === 'boolean'`},`);
|
||||
}
|
||||
}
|
||||
});
|
||||
w.writeLine('};');
|
||||
w.writeLine('const isError: string[] = [];')
|
||||
w.writeLine('Object.keys(validate).forEach((key) => {');
|
||||
w.writeLine(' if (!(validate as any)[key]) {');
|
||||
w.writeLine(' isError.push(key);');
|
||||
w.writeLine('const errorInFields: string[] = [];')
|
||||
w.writeLine('Object.keys(validateRequired).forEach((key) => {');
|
||||
w.writeLine(' if (!(validateRequired as any)[key]) {');
|
||||
w.writeLine(' errorInFields.push(key);');
|
||||
w.writeLine(' }');
|
||||
w.writeLine('});');
|
||||
w.writeLine('return isError;');
|
||||
|
||||
w.writeLine('return errorInFields;');
|
||||
});
|
||||
|
||||
// add update method;
|
||||
const update = entityClass.addMethod({
|
||||
isStatic: false,
|
||||
name: 'update',
|
||||
returnType: `${sName}`,
|
||||
});
|
||||
update.addParameter({
|
||||
name: 'props',
|
||||
type: additionalPropsOnly ? `I${sName}` : `Partial<I${sName}>`,
|
||||
});
|
||||
update.setBodyText((w) => { w.writeLine(`return new ${sName}({ ...this.serialize(), ...props });`); });
|
||||
|
||||
this.entities.push(entityFile);
|
||||
};
|
||||
|
||||
|
||||
@@ -19,65 +19,154 @@ const TYPES = {
|
||||
boolean: 'boolean',
|
||||
};
|
||||
|
||||
export enum SchemaType {
|
||||
STRING = 'string',
|
||||
OBJECT = 'object',
|
||||
ARRAY = 'array',
|
||||
BOOLEAN = 'boolean',
|
||||
NUMBER = 'number',
|
||||
INTEGER = 'integer',
|
||||
}
|
||||
|
||||
export interface Schema {
|
||||
allOf?: any[];
|
||||
example?: string;
|
||||
properties?: Record<string, Schema>;
|
||||
required?: string[];
|
||||
description?: string;
|
||||
enum?: string[];
|
||||
type: SchemaType;
|
||||
pattern?: string;
|
||||
oneOf?: any
|
||||
items?: Schema;
|
||||
additionalProperties?: Schema;
|
||||
$ref?: string;
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
maximum?: number;
|
||||
minimum?: number;
|
||||
}
|
||||
|
||||
export interface Parameter {
|
||||
description?: string;
|
||||
example?: string;
|
||||
in?: 'query' | 'body' | 'headers';
|
||||
name: string;
|
||||
schema: Schema;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface RequestBody {
|
||||
content: {
|
||||
'application/json'?: {
|
||||
schema: Schema;
|
||||
example?: string;
|
||||
};
|
||||
}
|
||||
required?: boolean;
|
||||
}
|
||||
export interface Response {
|
||||
content: {
|
||||
'application/json'?: {
|
||||
schema: Schema;
|
||||
example?: string;
|
||||
};
|
||||
'text/palin'?: {
|
||||
example?: string;
|
||||
'x-error-class'?: string;
|
||||
'x-error-code'?: string;
|
||||
}
|
||||
}
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface Schemas {
|
||||
parameters: Record<string, Parameter>;
|
||||
requestBodies: Record<string, RequestBody>;
|
||||
responses: Record<string, Response>;
|
||||
schemas: Record<string, Schema>;
|
||||
}
|
||||
|
||||
export interface OpenApi {
|
||||
components: Schemas;
|
||||
paths: any;
|
||||
servers: {
|
||||
description: string;
|
||||
url: string;
|
||||
}[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param schemaProp: valueof shema.properties[key]
|
||||
* @param openApi: openapi object
|
||||
* @returns [propType - basicType or import one, isArray, isClass, isImport]
|
||||
*/
|
||||
const schemaParamParser = (schemaProp: any, openApi: any): [string, boolean, boolean, boolean, boolean] => {
|
||||
interface SchemaParamParserReturn {
|
||||
type: string;
|
||||
isArray: boolean;
|
||||
isClass: boolean;
|
||||
isImport: boolean;
|
||||
isAdditional: boolean;
|
||||
isEnum: boolean;
|
||||
}
|
||||
|
||||
const schemaParamParser = (schemaProp: Schema, openApi: OpenApi): SchemaParamParserReturn => {
|
||||
let type = '';
|
||||
let isImport = false;
|
||||
let isClass = false;
|
||||
let isArray = false;
|
||||
let isAdditional = false;
|
||||
let isEnum = false;
|
||||
|
||||
if (schemaProp.$ref || schemaProp.additionalProperties?.$ref) {
|
||||
const temp = (schemaProp.$ref || schemaProp.additionalProperties?.$ref).split('/');
|
||||
type = (schemaProp.$ref || schemaProp.additionalProperties?.$ref)!.split('/').pop()!;
|
||||
|
||||
if (schemaProp.additionalProperties) {
|
||||
isAdditional = true;
|
||||
}
|
||||
|
||||
type = `${temp[temp.length - 1]}`;
|
||||
|
||||
const cl = openApi ? openApi.components.schemas[type] : {};
|
||||
const cl = openApi.components.schemas[type];
|
||||
|
||||
if (cl.allOf) {
|
||||
const ref = cl.allOf.find((e) => !!e.$ref);
|
||||
const link = schemaParamParser(ref, openApi);
|
||||
return {...link, type};
|
||||
}
|
||||
|
||||
if (cl.$ref) {
|
||||
const link = schemaParamParser(cl, openApi);
|
||||
link.shift();
|
||||
return [type, ...link] as any;
|
||||
return {...link, type};
|
||||
}
|
||||
|
||||
if (cl.type === 'string' && cl.enum) {
|
||||
isImport = true;
|
||||
isEnum = true;
|
||||
}
|
||||
|
||||
if (cl.type === 'object' && !cl.oneOf) {
|
||||
isClass = true;
|
||||
isImport = true;
|
||||
} else if (cl.type === 'array') {
|
||||
const temp: any = schemaParamParser(cl.items, openApi);
|
||||
type = `${temp[0]}`;
|
||||
const temp = schemaParamParser(cl.items!, openApi);
|
||||
type = temp.type;
|
||||
isArray = true;
|
||||
isClass = isClass || temp[2];
|
||||
isImport = isImport || temp[3];
|
||||
isClass = isClass || temp.isClass;
|
||||
isImport = isImport || temp.isImport;
|
||||
isEnum = isEnum || temp.isEnum;
|
||||
}
|
||||
} else if (schemaProp.type === 'array') {
|
||||
const temp: any = schemaParamParser(schemaProp.items, openApi);
|
||||
type = `${temp[0]}`;
|
||||
const temp = schemaParamParser(schemaProp.items!, openApi);
|
||||
type = temp.type
|
||||
isArray = true;
|
||||
isClass = isClass || temp[2];
|
||||
isImport = isImport || temp[3];
|
||||
isClass = isClass || temp.isClass;
|
||||
isImport = isImport || temp.isImport;
|
||||
isEnum = isEnum || temp.isEnum;
|
||||
} else {
|
||||
type = (TYPES as Record<any, string>)[schemaProp.type];
|
||||
}
|
||||
if (!type) {
|
||||
// TODO: Fix bug with Error fields.
|
||||
type = 'any';
|
||||
// throw new Error('Failed to find entity type');
|
||||
}
|
||||
|
||||
return [type, isArray, isClass, isImport, isAdditional];
|
||||
return { type, isArray, isClass, isImport, isAdditional, isEnum };
|
||||
};
|
||||
|
||||
export { TYPES, toCamel, capitalize, uncapitalize, schemaParamParser };
|
||||
|
||||
@@ -370,17 +370,16 @@
|
||||
remark "^13.0.0"
|
||||
unist-util-find-all-after "^3.0.2"
|
||||
|
||||
"@ts-morph/common@~0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.6.0.tgz#cbd4ee57c5ef971511b9c5778e0bb8eb27de4783"
|
||||
integrity sha512-pI35nZz5bs3tL3btSVX2cWkAE8rc80F+Fn4TwSC6bQvn7fgn9IyLXVcAfpG6X6NBY5wN9TkSWXn/QYUkBvR/Fw==
|
||||
"@ts-morph/common@~0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.8.0.tgz#ae7b292df8258040465c50b378108ec8f09a9516"
|
||||
integrity sha512-YbjWiMXLMKxWxcMqP47nwZVWVBwoF5B65dtRz0lya2LetjldAPxTxRbRo1n4Iszr2tSvzXeaa+f1AbULmfc5uA==
|
||||
dependencies:
|
||||
"@dsherret/to-absolute-glob" "^2.0.2"
|
||||
fast-glob "^3.2.4"
|
||||
fs-extra "^9.0.1"
|
||||
fast-glob "^3.2.5"
|
||||
is-negated-glob "^1.0.0"
|
||||
multimatch "^4.0.0"
|
||||
typescript "~4.0.2"
|
||||
mkdirp "^1.0.4"
|
||||
multimatch "^5.0.0"
|
||||
|
||||
"@types/anymatch@*":
|
||||
version "1.3.1"
|
||||
@@ -1172,11 +1171,6 @@ async@^2.6.2:
|
||||
dependencies:
|
||||
lodash "^4.17.14"
|
||||
|
||||
at-least-node@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
atob@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
@@ -1593,7 +1587,7 @@ coa@^2.0.2:
|
||||
chalk "^2.4.1"
|
||||
q "^1.1.2"
|
||||
|
||||
code-block-writer@^10.1.0:
|
||||
code-block-writer@^10.1.1:
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-10.1.1.tgz#ad5684ed4bfb2b0783c8b131281ae84ee640a42f"
|
||||
integrity sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==
|
||||
@@ -2910,9 +2904,9 @@ fastest-levenshtein@^1.0.12:
|
||||
integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.0.tgz#74dbefccade964932cdf500473ef302719c652bb"
|
||||
integrity sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
|
||||
integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
@@ -3084,16 +3078,6 @@ fs-extra@^8.1.0:
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^9.0.1:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
|
||||
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-minipass@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
|
||||
@@ -3267,11 +3251,16 @@ gonzales-pe@^4.3.0:
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.4:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||
|
||||
handle-thing@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
|
||||
@@ -4145,15 +4134,6 @@ jsonfile@^4.0.0:
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
|
||||
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
|
||||
dependencies:
|
||||
universalify "^2.0.0"
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
"jsx-ast-utils@^2.4.1 || ^3.0.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
|
||||
@@ -4784,10 +4764,10 @@ multicast-dns@^6.0.1:
|
||||
dns-packet "^1.3.1"
|
||||
thunky "^1.0.2"
|
||||
|
||||
multimatch@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3"
|
||||
integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==
|
||||
multimatch@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6"
|
||||
integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==
|
||||
dependencies:
|
||||
"@types/minimatch" "^3.0.3"
|
||||
array-differ "^3.0.0"
|
||||
@@ -6292,6 +6272,11 @@ querystringify@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3"
|
||||
integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==
|
||||
|
||||
quick-lru@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||
@@ -7086,9 +7071,11 @@ rimraf@^3.0.2:
|
||||
glob "^7.1.3"
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.1.10"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef"
|
||||
integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
|
||||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
@@ -7942,14 +7929,14 @@ ts-loader@^8.0.6:
|
||||
micromatch "^4.0.0"
|
||||
semver "^7.3.4"
|
||||
|
||||
ts-morph@^8.1.2:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-8.2.0.tgz#41d83cd501cbd897eb029ac489d6d5b927555c57"
|
||||
integrity sha512-NHHWu+7I2/AOZiTni5w3f+xCfIxrkzPCcQbTGa81Yk3pr23a4h9xLLEE6tIGuYIubWjkjr9QVC3ITqgmA5touQ==
|
||||
ts-morph@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-10.0.1.tgz#5a620cc4ef85e3e6d161989e690f44d0a0f723b0"
|
||||
integrity sha512-T1zufImtp5goTLTFhzi7XuKR1y/f+Jwz1gSULzB045LFjXuoqVlR87sfkpyWow8u2JwgusCJrhOnwmHCFNutTQ==
|
||||
dependencies:
|
||||
"@dsherret/to-absolute-glob" "^2.0.2"
|
||||
"@ts-morph/common" "~0.6.0"
|
||||
code-block-writer "^10.1.0"
|
||||
"@ts-morph/common" "~0.8.0"
|
||||
code-block-writer "^10.1.1"
|
||||
|
||||
ts-node@^9.0.0:
|
||||
version "9.1.1"
|
||||
@@ -8032,11 +8019,6 @@ typescript@^4.0.3:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
|
||||
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
|
||||
|
||||
typescript@~4.0.2:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389"
|
||||
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
|
||||
|
||||
unc-path-regex@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||
@@ -8112,11 +8094,6 @@ universalify@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
Reference in New Issue
Block a user