UNPKG

jspurefix

Version:
198 lines (181 loc) 7.61 kB
import { ICompilerSettings } from './compiler-settings' import { FixDefinitions } from '../definition' import { ContainedGroupField, ContainedSimpleField, ContainedComponentField } from '../contained' import { StandardSnippet } from './standard-snippet' import { ContainedSetType } from '../dict-primitive' import { CompilerType } from './compiler-type' import { Dictionary } from '../../collections' import { ElasticBuffer, Ascii, Tags } from '../../buffer' import * as fs from 'fs' import * as Util from 'util' import * as Path from 'path' import { dispatchFields } from '../fields-dispatch' import { reduceSet } from '../set-reduce' export class MsgCompiler { readonly queue: CompilerType[] = [] readonly snippets: StandardSnippet readonly buffer: ElasticBuffer = new ElasticBuffer() readonly completed: Dictionary<CompilerType> constructor (public readonly definitions: FixDefinitions, public readonly settings: ICompilerSettings) { this.completed = new Dictionary<CompilerType>() this.snippets = new StandardSnippet(this.settings) } public async generate () { const types: string[] = this.settings.types || this.definitions.message.keys() return this.createTypes(types) } private getFileName (compilerType: CompilerType): string { const settings = this.settings const fileName = `${compilerType.snaked}.ts` return Path.join(settings.output, fileName) } private async createTypes (types: string[]) { const definitions = this.definitions types.forEach((type: string) => { const definition = definitions.containedSet(type) if (!definition) { throw new Error(`no type ${type} defined`) } const ct = new CompilerType(this.definitions, definition, definition.name) this.enqueue(ct) }) await this.work() await this.index() } private async work () { const q = this.queue const writeFile = Util.promisify(fs.writeFile) while (q.length > 0) { const compilerType: CompilerType = q.pop() const api: string = this.generateMessages(compilerType) const fullName = this.getFileName(compilerType) await writeFile(fullName, api, { encoding: 'utf8'} ) } } private async index () { const writeFile = Util.promisify(fs.writeFile) const settings = this.settings const fileName = 'index.ts' const done = this.completed.values() const exports: string[] = done.reduce((prev: string[], current: CompilerType) => { prev.push(`export * from '${current.snaked}'`) return prev }, [`export * from './enum'`] as string[]) const newLine = require('os').EOL exports.sort() exports.push('') const api: string = exports.join(newLine) const fullName: string = Path.join(settings.output, fileName) await writeFile(fullName, api, { encoding: 'utf8'} ) } private generateMessages (compilerType: CompilerType): string { const definition = compilerType.set const buffer: ElasticBuffer = this.buffer buffer.reset() const snippets = this.snippets const newLine = require('os').EOL // a single class with dependencies included let ptr: number = this.imports(compilerType) if (ptr > 0) { buffer.writeString(newLine) buffer.writeString(newLine) } if (this.settings.comment && definition.description) { const comment = snippets.commentBox(definition.description) buffer.writeString(snippets.startBlockComment(0)) buffer.writeString(newLine) buffer.writeString(comment) buffer.writeString(newLine) buffer.writeString(snippets.endBlockComment(0)) buffer.writeString(newLine) } buffer.writeString(snippets.interface(compilerType.qualifiedName, 0)) buffer.writeChar(Ascii.Space) buffer.writeString(snippets.startBlock(0)) buffer.writeString(newLine) this.fields(compilerType) buffer.writeString(snippets.endBlock(0)) buffer.writeString(newLine) return buffer.toString() } // only generate once private enqueue (ct: CompilerType): void { const completed = this.completed const fullName = this.getFileName(ct) if (completed.get(fullName)) { return } this.queue.push(ct) completed.addUpdate(fullName, ct) } private fieldSimple (simple: ContainedSimpleField): void { const snippets = this.snippets const settings = this.settings const buffer = this.buffer const newLine = require('os').EOL const len = buffer.writeString(snippets.simple(simple.name, Tags.toJSType(simple), simple.required, 1)) if (settings.tags) { buffer.writeString(snippets.commentLine(`${simple.definition.tag}`, 50 - len)) } buffer.writeString(newLine) } private fieldGroup (groupField: ContainedGroupField, compilerType: CompilerType): void { const newLine = require('os').EOL const buffer = this.buffer const snippets = this.snippets // for a group its field name is as defined in type, its type is qualified to avoid collisions const extended: string = compilerType.getExtended(groupField) buffer.writeString(snippets.group(compilerType.getFieldGroupName(groupField), extended, groupField.required, 1)) this.enqueue(new CompilerType(this.definitions, groupField.definition, extended)) buffer.writeString(newLine) } private fieldComponent (componentField: ContainedComponentField, compilerType: CompilerType): void { const newLine = require('os').EOL const buffer = this.buffer const snippets = this.snippets const extended: string = compilerType.getExtended(componentField) buffer.writeString(snippets.component(extended, componentField.required, 1)) this.enqueue(new CompilerType(this.definitions, componentField.definition, extended)) buffer.writeString(newLine) } private fields (compilerType: CompilerType): void { this.attributes(compilerType) dispatchFields(compilerType.set.fields, { group: (g: ContainedGroupField) => this.fieldGroup(g, compilerType), simple: (simple: ContainedSimpleField) => this.fieldSimple(simple), component: (c: ContainedComponentField) => this.fieldComponent(c, compilerType) }) } private attributes (compilerType: CompilerType): void { const settings = this.settings const newLine = require('os').EOL const snippets = this.snippets const buffer = this.buffer compilerType.set.localAttribute.forEach((simple: ContainedSimpleField) => { const len = buffer.writeString(snippets.simple(simple.definition.name, Tags.toJSType(simple), simple.required, 1)) if (settings.tags) { buffer.writeString(snippets.commentLine(`${simple.definition.tag}`, 50 - len)) } buffer.writeString(newLine) }) } private imports (compilerType: CompilerType): number { const isMsg = compilerType.set.type === ContainedSetType.Msg const snippets = this.snippets const imports: string[] = reduceSet<string[]>(compilerType.set, { component: (a: string[], c: ContainedComponentField) => { a.push(`${snippets.import(compilerType.getExtended(c), isMsg, 0)}`) return a }, group: (a: string[], g: ContainedGroupField) => { a.push(`${snippets.import(compilerType.getExtended(g), isMsg, 0)}`) return a } }, []) return this.buffer.writeString(imports.join(require('os').EOL)) } }