export namespace codegen { type Param = number | boolean | string | undefined; type Callback = (...args: string[]) => string; function isParam(p: MayParamOrConfig): p is Param { return typeof (p) === 'number' || typeof (p) === 'boolean' || typeof (p) === 'string' || typeof (p) === 'undefined'; } function isCallback(p: MayParamOrConfig): p is Callback { return typeof (p) === 'function'; } function isArray(p: MayParamOrConfig): p is Array { return Array.isArray(p); } // function isEmptyObject(p: MayParamOrConfig): boolean { // return !isParam(p) && !isCallback(p) && !isArray(p) && Object.keys(p).length === 0; // } interface Config { [key: string]: MayParamOrConfig; } type MayParamOrConfig = Param | Config | Callback | Array; const identitier = /^::([a-zA-Z\_0-9]*)/; const funcParams = /^[a-zA-Z\_]*\s*\(\s*([a-zA-Z\_]*)*\s*\)/; export function identifier(val: string): T { return `::${val}` as any; } export function toBeKey(str: string): string { if (str.indexOf('/') > -1 || str.indexOf('-') > -1 || str.indexOf('.') > -1) { return `"${str}"`; } return str; } export class Printer { private output: string = ''; private param(p: Param): string { if (typeof (p) === 'string') { if (p.indexOf('::') === 0) { return `${p.substring(2)}`; } else { return JSON.stringify(p); } } return typeof (p) !== 'undefined' ? p.toString() : 'undefined'; } private params(ps: MayParamOrConfig[]): string { // 暂时不支持直接在参数使用函数 return `${ps.map(p => this.object(p as Config)).join(',')}`; } private object(value: MayParamOrConfig): string { if (isParam(value)) { return this.param(value); } if (isCallback(value)) { const params = (funcParams.exec(value.toString()) || []) as RegExpExecArray; params.shift(); const inputParams = params.join(','); const identifiers = value(...(params.map((p: string) => identifier(p)))); return `(${inputParams}) => {${identifiers}}`; } if (isArray(value)) { return `[${value.map(v => this.object(v)).join(',')}]`; } // null if (!value && typeof value === 'object') { return 'null'; } const keys = Object.keys(value); if (keys.length === 0) { return '{}'; } else { let localStr = '{'; keys.forEach(key => { const v = value[key]; if (typeof (v) === 'undefined') return; localStr += toBeKey(key); localStr += ':'; localStr += this.object(v); localStr += ','; }); localStr += '}'; return localStr; } } ref(target: any, isCtor = false): T { let caller: string; const res = identitier.exec(target.toString()); if (res) { caller = res[1]; } else { caller = target; } return new Proxy({} as T, { get: (_, prop: string) => (...args: MayParamOrConfig[]) => { this.output += `${isCtor ? 'new ' : ''}${caller}.${prop}(${this.params(args)});\n`; } }); } flush(): string { const str = this.output; this.output = ''; return str; } } }