mirror of https://github.com/abpframework/abp.git
committed by
GitHub
8 changed files with 235 additions and 73 deletions
@ -1,43 +1,97 @@ |
|||||
import { Import } from './import'; |
import { Import } from './import'; |
||||
import { Omissible } from './util'; |
import { Options } from './util'; |
||||
|
|
||||
export class Model { |
export class Model { |
||||
imports: Import[] = []; |
readonly imports: Import[] = []; |
||||
interfaces: Interface[] = []; |
readonly interfaces: Interface[] = []; |
||||
namespace: string; |
readonly namespace: string; |
||||
path: string; |
readonly path: string; |
||||
|
|
||||
constructor(options: ModelOptions) { |
constructor(options: ModelOptions) { |
||||
Object.assign(this, options); |
Object.assign(this, options); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export type ModelOptions = Omissible<Model, 'imports' | 'interfaces'>; |
export type ModelOptions = Options<Model, 'imports' | 'interfaces'>; |
||||
|
|
||||
export class Interface { |
export class Interface { |
||||
base: string | null; |
readonly base: string | null; |
||||
identifier: string; |
readonly identifier: string; |
||||
namespace: string; |
readonly namespace: string; |
||||
properties: Property[] = []; |
readonly generics: Generic[] = []; |
||||
ref: string; |
readonly properties: Property[] = []; |
||||
|
readonly ref: string; |
||||
|
|
||||
constructor(options: InterfaceOptions) { |
constructor(options: InterfaceOptions) { |
||||
Object.assign(this, options); |
Object.assign(this, options); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export type InterfaceOptions = Omissible<Interface, 'properties'>; |
export type InterfaceOptions = Options<Interface, 'generics' | 'properties'>; |
||||
|
|
||||
export class Property { |
abstract class TypeRef { |
||||
name: string; |
readonly refs: string[] = []; |
||||
type: string; |
|
||||
default = ''; |
|
||||
optional: '' | '?' = ''; |
|
||||
refs: string[] = []; |
|
||||
|
|
||||
constructor(options: PropertyOptions) { |
protected _type = ''; |
||||
|
get type() { |
||||
|
return this._type; |
||||
|
} |
||||
|
set type(value: string) { |
||||
|
if (!value) return; |
||||
|
this._type = value; |
||||
|
} |
||||
|
|
||||
|
protected _default = ''; |
||||
|
get default() { |
||||
|
return this._default; |
||||
|
} |
||||
|
set default(value: string) { |
||||
|
if (!value) return; |
||||
|
this._default = ` = ${value}`; |
||||
|
} |
||||
|
|
||||
|
constructor(options: TypeRefOptions) { |
||||
Object.assign(this, options); |
Object.assign(this, options); |
||||
} |
} |
||||
|
|
||||
|
setDefault(value: string) { |
||||
|
this.default = value; |
||||
|
} |
||||
|
|
||||
|
setType(value: string) { |
||||
|
this.type = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type TypeRefOptionalKeys = 'default' | 'refs'; |
||||
|
type TypeRefOptions = Options<TypeRef, TypeRefOptionalKeys>; |
||||
|
|
||||
|
export class Generic extends TypeRef { |
||||
|
constructor(options: GenericOptions) { |
||||
|
super(options); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export type GenericOptions = Options<Generic, TypeRefOptionalKeys>; |
||||
|
|
||||
|
export class Property extends TypeRef { |
||||
|
readonly name: string; |
||||
|
private _optional: '' | '?' = ''; |
||||
|
get optional() { |
||||
|
return this.default ? '' : this._optional; |
||||
|
} |
||||
|
|
||||
|
set optional(value: '' | '?') { |
||||
|
this._optional = value; |
||||
|
} |
||||
|
|
||||
|
constructor(options: PropertyOptions) { |
||||
|
super(options); |
||||
|
} |
||||
|
|
||||
|
setOptional(isOptional: boolean) { |
||||
|
this.optional = isOptional ? '?' : ''; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
export type PropertyOptions = Omissible<Property, 'default' | 'optional' | 'refs'>; |
export type PropertyOptions = Options<Property, TypeRefOptionalKeys | 'optional'>; |
||||
|
|||||
@ -1,2 +1,16 @@ |
|||||
// Omissible (given keys will become optional)
|
// Omissible (given keys will become optional)
|
||||
export type Omissible<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>; |
export type Omissible<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>; |
||||
|
|
||||
|
// ExcludeKeys (keys will be excluded based on their type)
|
||||
|
type ExcludeKeys<Type, Excluded> = Exclude< |
||||
|
{ |
||||
|
[Key in keyof Type]: Type[Key] extends Excluded ? never : Key; |
||||
|
}[keyof Type], |
||||
|
never |
||||
|
>; |
||||
|
|
||||
|
// tslint:disable-next-line: ban-types
|
||||
|
type ExcludeMethods<Type> = Pick<Type, ExcludeKeys<Type, Function>>; |
||||
|
|
||||
|
// Options (methods will be omitted, given keys will become optional)
|
||||
|
export type Options<T, K extends keyof ExcludeMethods<T>> = Omissible<ExcludeMethods<T>, K>; |
||||
|
|||||
@ -0,0 +1,100 @@ |
|||||
|
import { Generic } from '../models'; |
||||
|
|
||||
|
export class GenericsCollector { |
||||
|
private _generics: Generic[] = []; |
||||
|
get generics() { |
||||
|
return this._generics; |
||||
|
} |
||||
|
|
||||
|
apply = (value: string, index: number) => { |
||||
|
const generic = this.get(index); |
||||
|
if (generic) { |
||||
|
if (!generic.type) generic.setType(value); |
||||
|
return value + generic.default; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
}; |
||||
|
|
||||
|
constructor(private getTypeIdentifier = (type: string) => type) {} |
||||
|
|
||||
|
private createGeneric(type: string, ref: string, defaultValue: string) { |
||||
|
const _default = this.getTypeIdentifier(defaultValue); |
||||
|
const refs = [generateRefWithPlaceholders(ref)]; |
||||
|
const generic = new Generic({ type, default: _default, refs }); |
||||
|
return generic; |
||||
|
} |
||||
|
|
||||
|
private register(index: number, generic: Generic) { |
||||
|
const existing = this.get(index); |
||||
|
if (existing) { |
||||
|
existing.setDefault(generic.default); |
||||
|
existing.setType(generic.type); |
||||
|
} else this.set(index, generic); |
||||
|
} |
||||
|
|
||||
|
collect(generics: string[], genericArguments: string[]) { |
||||
|
generics.forEach((ref, i) => { |
||||
|
const generic = this.createGeneric( |
||||
|
genericArguments[i], |
||||
|
ref, |
||||
|
genericArguments.includes(ref) ? '' : ref, |
||||
|
); |
||||
|
this.register(i, generic); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
get(index: number) { |
||||
|
return this.generics[index]; |
||||
|
} |
||||
|
|
||||
|
set(index: number, value: Generic) { |
||||
|
this.generics[index] = value; |
||||
|
} |
||||
|
|
||||
|
reset() { |
||||
|
this._generics = []; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function generateRefWithPlaceholders(sourceType: string) { |
||||
|
let { identifier, generics } = extractGenerics(sourceType); |
||||
|
|
||||
|
identifier = identifier; |
||||
|
generics = generics.map((_, i) => `T${i}`); |
||||
|
|
||||
|
return generics.length ? `${identifier}<${generics}>` : identifier; |
||||
|
} |
||||
|
|
||||
|
export function extractSimpleGenerics(sourceType: string) { |
||||
|
const { identifier, generics } = extractGenerics(sourceType); |
||||
|
|
||||
|
return { |
||||
|
identifier: getLastSegment(identifier), |
||||
|
generics: generics.map(getLastSegment), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export function extractGenerics(sourceType: string) { |
||||
|
const regex = /(?<identifier>[^<]+)(<(?<generics>.+)>)?/g; |
||||
|
const { identifier = '', generics = '' } = regex.exec(sourceType)?.groups ?? {}; |
||||
|
|
||||
|
return { |
||||
|
identifier, |
||||
|
generics: generics.split(/,\s*/).filter(Boolean), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
function getLastSegment(str: string) { |
||||
|
return str.split('.').pop()!; |
||||
|
} |
||||
|
|
||||
|
export function replacePlaceholdersWithGenerics( |
||||
|
type: string, |
||||
|
generics: string[], |
||||
|
genericsCollector: GenericsCollector, |
||||
|
) { |
||||
|
return generics |
||||
|
.map(genericsCollector.apply) |
||||
|
.reduce((acc, v, i) => acc.replace(new RegExp(`([<, ])T${i}([,>])`, 'g'), `$1${v}$2`), type); |
||||
|
} |
||||
Loading…
Reference in new issue