You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

452 lines
16 KiB

4 years ago
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const utils_1 = require("./utils");
const compiler_1 = require("./compiler");
var PrimitiveType;
(function (PrimitiveType) {
PrimitiveType["number"] = "number";
PrimitiveType["string"] = "string";
PrimitiveType["boolean"] = "boolean";
})(PrimitiveType = exports.PrimitiveType || (exports.PrimitiveType = {}));
class Contextable {
constructor(arg = {}) {
_.forEach(arg, (value, key) => {
if (value !== undefined) {
this[key] = value;
}
});
}
getDsName() {
const context = this.getContext();
if (context && context.dataSource) {
return context.dataSource.name;
}
return '';
}
getContext() {
return this.context;
}
setContext(context) {
this.context = context;
}
toJSON() {
return _.mapValues(this, (value, key) => {
if (key === 'context') {
return undefined;
}
return value;
});
}
}
class DataType {
constructor() {
this.isArr = false;
this.customType = '';
this.reference = '';
this.enum = [];
this.isTemplateRef = false;
}
}
function dateTypeRefs2Ast(refStr, originName, compileTemplateKeyword) {
let ref = refStr.replace(new RegExp(`defs.${originName}.`, 'g'), '');
ref = ref.replace(/defs./g, '');
ref = ref.replace(/= any/g, '');
const PreTemplate = '«';
const EndTemplate = '»';
ref = ref.replace(/</g, PreTemplate).replace(/>/g, EndTemplate);
const ast = compiler_1.compileTemplate(ref, compileTemplateKeyword);
return ast;
}
function dataType2StandardDataType(dataType, originName, defNames, compileTemplateKeyword) {
let standardDataType = null;
if (dataType.enum && dataType.enum.length) {
standardDataType = new StandardDataType([], '', false, -1, compileTemplateKeyword);
standardDataType.setEnum(dataType.enum);
}
else if (dataType.primitiveType) {
standardDataType = new StandardDataType([], dataType.primitiveType, false, -1, compileTemplateKeyword);
}
else if (dataType.reference) {
const ast = dateTypeRefs2Ast(dataType.reference, originName, compileTemplateKeyword);
standardDataType = compiler_1.parseAst2StandardDataType(ast, defNames, []);
}
if (dataType.isArr) {
if (!standardDataType) {
standardDataType = new StandardDataType();
}
return new StandardDataType([standardDataType], 'Array', false, -1, compileTemplateKeyword);
}
if (!standardDataType) {
return new StandardDataType();
}
return standardDataType;
}
class StandardDataType extends Contextable {
constructor(typeArgs = [], typeName = '', isDefsType = false, templateIndex = -1, compileTemplateKeyword = '#/definitions/') {
super();
this.typeArgs = typeArgs;
this.typeName = typeName;
this.isDefsType = isDefsType;
this.templateIndex = templateIndex;
this.compileTemplateKeyword = compileTemplateKeyword;
this.enum = [];
this.typeProperties = [];
}
setEnum(enums = []) {
this.enum = enums.map((value) => {
if (typeof value === 'string') {
if (!value.startsWith("'")) {
value = "'" + value;
}
if (!value.endsWith("'")) {
value = value + "'";
}
}
return value;
});
}
static constructorWithEnum(enums = []) {
const dataType = new StandardDataType();
dataType.setEnum(enums);
return dataType;
}
static constructorFromJSON(dataType, originName, defNames) {
if (Object.getOwnPropertyNames(dataType).includes('reference')) {
return dataType2StandardDataType(dataType, originName, defNames, dataType.compileTemplateKeyword);
}
const { isDefsType, templateIndex, typeArgs = [], typeName, typeProperties } = dataType;
if (typeArgs.length) {
const instance = new StandardDataType(typeArgs.map((arg) => StandardDataType.constructorFromJSON(arg, originName, defNames)), typeName, isDefsType, templateIndex);
instance.setEnum(dataType.enum);
return instance;
}
const result = new StandardDataType([], typeName, isDefsType, templateIndex);
result.setEnum(dataType.enum);
result.typeProperties = (typeProperties || []).map((prop) => new Property(prop));
return result;
}
setTemplateIndex(classTemplateArgs) {
const codes = classTemplateArgs.map((arg) => arg.generateCode());
const index = codes.indexOf(this.generateCode());
this.typeArgs.forEach((arg) => arg.setTemplateIndex(classTemplateArgs));
this.templateIndex = index;
}
getDefNameWithTemplate() { }
generateCodeWithTemplate() { }
getDefName(originName) {
let name = this.typeName;
if (this.isDefsType) {
name = originName ? `defs.${originName}.${this.typeName}` : `defs.${this.typeName}`;
}
return name;
}
getEnumType() {
return this.enum.join(' | ') || 'string';
}
generateCode(originName = '') {
if (this.templateIndex !== -1) {
return `T${this.templateIndex}`;
}
if (this.enum.length) {
return this.getEnumType();
}
const name = this.getDefName(originName);
if (this.typeArgs.length) {
return `${name}<${this.typeArgs.map((arg) => arg.generateCode(originName)).join(', ')}>`;
}
if (this.typeProperties.length) {
const interfaceCode = `{${this.typeProperties.map((property) => property.toPropertyCode())}
}`;
if (name) {
return `${name}<${interfaceCode}>`;
}
return interfaceCode;
}
return name || 'any';
}
getInitialValue(usingDef = true) {
if (this.typeName === 'Array') {
return '[]';
}
if (this.isDefsType) {
const originName = this.getDsName();
if (!usingDef) {
return `new ${this.typeName}()`;
}
return `new ${this.getDefName(originName)}()`;
}
if (this.templateIndex > -1) {
return 'undefined';
}
if (this.typeName === 'string') {
return "''";
}
if (this.typeName === 'boolean') {
return 'false';
}
if (this.enum && this.enum.length) {
const str = this.enum[0];
if (typeof str === 'string') {
return `${str}`;
}
return str + '';
}
return 'undefined';
}
get initialValue() {
return this.getInitialValue();
}
}
exports.StandardDataType = StandardDataType;
class Property extends Contextable {
constructor(prop) {
super(prop);
if (this.name.includes('.')) {
this.name = this.name.slice(this.name.lastIndexOf('.') + 1);
}
}
setContext(context) {
super.setContext(context);
this.dataType.setContext(context);
}
toPropertyCode(surrounding = utils_1.Surrounding.typeScript, hasRequired = false, optional = false) {
let optionalSignal = hasRequired && optional ? '?' : '';
if (hasRequired && !this.required) {
optionalSignal = '?';
}
let name = this.name;
if (!name.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)) {
name = `'${name}'`;
}
const fieldTypeDeclaration = surrounding === utils_1.Surrounding.javaScript
? ''
: `${optionalSignal}: ${this.dataType.generateCode(this.getDsName())}`;
return `
/** ${this.description || this.name} */
${name}${fieldTypeDeclaration};`;
}
toPropertyCodeWithInitValue(baseName = '') {
let typeWithValue = `= ${this.dataType.getInitialValue(false)}`;
if (!this.dataType.getInitialValue(false)) {
typeWithValue = `: ${this.dataType.generateCode(this.getDsName())}`;
}
if (this.dataType.typeName === baseName) {
typeWithValue = `= {}`;
}
let name = this.name;
if (!name.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)) {
name = `'${name}'`;
}
return `
/** ${this.description || this.name} */
${name} ${typeWithValue}
`;
}
toBody() {
return this.dataType.generateCode(this.getDsName());
}
}
exports.Property = Property;
class Interface extends Contextable {
constructor(inter) {
super(inter);
}
get responseType() {
return this.response.generateCode(this.getDsName());
}
getParamsCode(className = 'Params', surrounding = utils_1.Surrounding.typeScript) {
return `class ${className} {
${this.parameters
.filter((param) => param.in === 'path' || param.in === 'query')
.map((param) => param.toPropertyCode(surrounding, true))
.join('')}
}
`;
}
getParamList() {
const form = !!this.parameters.find((param) => param.in === 'formData');
const paramList = [
{
paramKey: 'params',
paramType: 'Params'
},
{
paramKey: 'form',
paramType: form ? 'FormData' : ''
},
{
paramKey: 'body',
paramType: this.getBodyParamsCode()
},
{
paramKey: 'options',
optional: true,
paramType: 'any',
initialValue: '{}'
}
];
return paramList;
}
getRequestContent() {
const paramList = this.getParamList().filter((param) => param.paramType);
const method = this.method.toUpperCase();
const hasForm = paramList.map((param) => param.paramKey).includes('form');
const hasBody = paramList.map((param) => param.paramKey).includes('body');
const hasOptions = paramList.map((param) => param.paramKey).includes('options');
return `{
method: "${method}",
${hasForm ? 'body: form,' : ''}
${hasBody ? 'body,' : ''}
${hasOptions ? '...options,' : ''}
}`;
}
getRequestParams(surrounding = utils_1.Surrounding.typeScript) {
const paramList = this.getParamList().filter((param) => param.paramType);
if (surrounding === utils_1.Surrounding.typeScript) {
return paramList.map((param) => `${param.paramKey}${param.optional ? '?' : ''}: ${param.paramType}`).join(', ');
}
return paramList
.map((param) => `${param.paramKey}${param.initialValue ? ` = ${param.initialValue}` : ''}`)
.join(', ');
}
getBodyParamsCode() {
const bodyParam = this.parameters.find((param) => param.in === 'body');
return (bodyParam && bodyParam.dataType.generateCode(this.getDsName())) || '';
}
setContext(context) {
super.setContext(context);
this.parameters.forEach((param) => param.setContext(context));
this.response && this.response.setContext(context);
}
}
exports.Interface = Interface;
class Mod extends Contextable {
constructor(mod) {
super(mod);
this.interfaces = _.orderBy(this.interfaces, 'path');
}
setContext(context) {
super.setContext(context);
this.interfaces.forEach((inter) => inter.setContext(Object.assign(Object.assign({}, context), { mod: this })));
}
}
exports.Mod = Mod;
class BaseClass extends Contextable {
constructor(base) {
super(base);
this.properties = _.orderBy(this.properties, 'name');
}
setContext(context) {
super.setContext(context);
this.properties.forEach((prop) => prop.setContext(context));
}
}
exports.BaseClass = BaseClass;
class StandardDataSource {
constructor(standard) {
this.mods = standard.mods;
if (standard.name) {
this.name = standard.name;
}
this.baseClasses = standard.baseClasses;
this.reOrder();
this.setContext();
}
reOrder() {
this.baseClasses = _.orderBy(this.baseClasses, 'name');
this.mods = _.orderBy(this.mods, 'name');
}
validate() {
const errors = [];
this.mods.forEach((mod) => {
if (!mod.name) {
errors.push(`lock 文件不合法,发现没有 name 属性的模块;`);
}
});
this.baseClasses.forEach((base) => {
if (!base.name) {
errors.push(`lock 文件不合法,发现没有 name 属性的基类;`);
}
});
const dupMod = utils_1.getDuplicateById(this.mods, 'name');
const dupBase = utils_1.getDuplicateById(this.baseClasses, 'name');
if (dupMod) {
errors.push(`模块 ${dupMod.name} 重复了。`);
}
if (dupBase) {
errors.push(`基类 ${dupBase.name} 重复了。`);
}
if (errors && errors.length) {
throw new Error(errors.join('\n'));
}
return errors;
}
serialize() {
return JSON.stringify({
mods: this.mods,
baseClasses: this.baseClasses
}, null, 2);
}
setContext() {
this.baseClasses.forEach((base) => base.setContext({ dataSource: this }));
this.mods.forEach((mod) => mod.setContext({ dataSource: this }));
}
static constructorFromLock(localDataObject, originName) {
try {
const defNames = localDataObject.baseClasses.map((base) => {
if (base.name.includes('<')) {
return base.name.slice(0, base.name.indexOf('<'));
}
return base.name;
});
const baseClasses = localDataObject.baseClasses.map((base) => {
const props = base.properties.map((prop) => {
return new Property(Object.assign(Object.assign({}, prop), { dataType: StandardDataType.constructorFromJSON(prop.dataType, originName, defNames) }));
});
let templateArgs = base.templateArgs;
let name = base.name;
if (!templateArgs && base.name.includes('<')) {
const defNameAst = dateTypeRefs2Ast(base.name, localDataObject.name);
const dataType = compiler_1.parseAst2StandardDataType(defNameAst, defNames, []);
templateArgs = dataType.typeArgs;
name = dataType.typeName;
}
return new BaseClass({
description: base.description,
name,
templateArgs,
properties: _.unionBy(props, 'name')
});
});
const mods = localDataObject.mods.map((mod) => {
const interfaces = mod.interfaces.map((inter) => {
const response = StandardDataType.constructorFromJSON(inter.response, localDataObject.name, defNames);
const parameters = inter.parameters
.map((param) => {
const dataType = StandardDataType.constructorFromJSON(param.dataType, localDataObject.name, defNames);
return new Property(Object.assign(Object.assign({}, param), { dataType }));
})
.filter(_.identity);
return new Interface(Object.assign(Object.assign({}, inter), { parameters,
response }));
});
return new Mod({
description: mod.description,
name: mod.name,
interfaces
});
});
return new StandardDataSource({
baseClasses,
mods,
name: localDataObject.name
});
}
catch (e) {
throw new Error(e);
}
}
}
exports.StandardDataSource = StandardDataSource;
//# sourceMappingURL=standard.js.map