bobflux-gen
Version:
Generator for monkey files in bobflux application.
121 lines (111 loc) • 6.56 kB
text/typescript
import * as g from './generator';
import * as tsa from './tsAnalyzer';
import * as tsch from './tsCompilerHost';
import * as log from './logger';
import * as nameUnifier from './nameUnifier';
import * as ts from 'typescript';
import * as fs from 'fs';
import * as pathPlatformDependent from 'path';
const path = pathPlatformDependent.posix; // This works everythere, just use forward slashes
var defaultLibFilename = path.join(path.dirname(require.resolve("typescript").replace(/\\/g, '/')), "lib.es6.d.ts");
export default (project: g.IGenerationProject, tsAnalyzer: tsa.ITsAnalyzer, logger: log.ILogger, rootStateKey: string = null): g.IGenerationProcess => {
return {
run: () => runBase(false, project, tsAnalyzer, logger, rootStateKey),
runRecurse: () => runBase(true, project, tsAnalyzer, logger, rootStateKey)
}
}
function runBase(applyRecurse: boolean, project: g.IGenerationProject, tsAnalyzer: tsa.ITsAnalyzer, logger: log.ILogger, rootStateKey: string): Promise<any> {
const writeCallback = (fn, c) => { project.writeFileCallback(fn, new Buffer(c, 'utf-8')); }
function writeCursors(params: g.ILoadedParams, currentStateName: string, rootStateKey: string) {
let stateFilePath = params.stateFilePath;
let mainState = g.resolveState(params.data.states, currentStateName);
if (!mainState)
return;
const stateAlias = g.createUnusedAlias(g.stateImportKey, params.data.imports);
const bobfluxPrefix = g.resolveBobfluxPrefix(mainState);
logger.info('Generating has been started for: ', stateFilePath);
writeCallback(
createCursorsFilePath(stateFilePath),
g.createAutogeneratedHeader(project.version)
+ g.createFullImports(stateAlias, `./${params.data.fileName}`, params.data.imports)
+ createCursorsForStateFields(params, rootStateKey, params.data, `${stateAlias}.${mainState.typeName}`, mainState, bobfluxPrefix, stateAlias)
);
logger.info('Generating ended for: ', stateFilePath);
}
function createCursorsForStateFields(params: g.ILoadedParams, parentStateKey: string, data: tsa.IStateSourceData, mainStateTypeName: string, state: tsa.IStateData, bobfluxPrefix: string, stateAlias: string, prefix: string = null): string {
let nexts: INextIteration[] = [];
let inner = state.fields.map(f => {
let key = parentStateKey === null ? g.composeCursorKey(parentStateKey, prefix, f.name) : g.composeCursorKey(prefix, f.name);
if (applyRecurse && f.type.length == 1 && !f.type[0].isArray && g.isExternalState(f.type[0].name, data)) {
let alias = g.getExternalAlias(f.type[0].name, data);
let innerFilePath = path.join(path.dirname(data.filePath), alias.relativePath + '.ts');
let innerSourceFile = g.resolveSourceFile(params.sourceFiles, innerFilePath);
if (innerSourceFile) {
let innerData = tsAnalyzer.getSourceData(innerSourceFile, params.typeChecker);
let innerResolvedState = g.resolveState(innerData.states, alias.sourceType);
if (innerResolvedState)
writeCursors({
stateFilePath: innerSourceFile.path,
data: tsAnalyzer.getSourceData(innerSourceFile, params.typeChecker),
sourceFiles: params.sourceFiles,
typeChecker: params.typeChecker
}, alias.sourceType, g.composeCursorKey(parentStateKey, key));
}
}
let fieldType = g.getFullType(f.type, data, stateAlias);
let indexedCursor = '';
f.type.forEach(t => {
let states = data.states.filter(s => s.typeName === t.name);
if (f.type.length == 1 && t.isArray) {
indexedCursor = createIndexedFieldCursor(prefix, key, bobfluxPrefix, states.length > 0 ? `${stateAlias}.${t.name}` : t.name, mainStateTypeName);
return;
}
if (states.length > 0)
nexts.push({ state: states[0], data: data, externalFileAlias: stateAlias, prefix: key });
if (states.length > 1)
throw 'Two states with same name could not be parsed. It\'s compilation error.';
});
return createFieldCursor(prefix, key, bobfluxPrefix, fieldType, mainStateTypeName) + indexedCursor;
}).join('\n');
return inner + (nexts.length > 0 ? '\n' : '') + nexts.map(n => createCursorsForStateFields(params, parentStateKey, n.data, mainStateTypeName, n.state, bobfluxPrefix, n.externalFileAlias, n.prefix)).join('\n');
}
return new Promise((f, r) => {
g.loadSourceFiles(project, tsAnalyzer, logger)
.then(p => {
try {
writeCursors(p, project.appStateName, rootStateKey);
} catch (e) {
logger.error('Error on cursors writing.', e);
}
f();
})
.catch(e => r(e));
})
}
function createFieldCursor(prefix: string, key: string, bobfluxPrefix: string, typeName: string, baseStateTypeName: string): string {
return `export function ${prefix === null ? key : nameUnifier.getStatePrefixFromKeyPrefix(prefix, nameUnifier.getStatePrefixFromKeyPrefix(key, ""))}(cursor: ${bobfluxPrefix}.ICursor<${baseStateTypeName}>): ${bobfluxPrefix}.ICursor<${typeName}> {
return { key: cursor.key + '.${key}' };
}
`;
}
function createIndexedFieldCursor(prefix: string, key: string, bobfluxPrefix: string, typeName: string, baseStateTypeName: string): string {
return `export function ${prefix === null ? key : nameUnifier.getStatePrefixFromKeyPrefix(prefix, nameUnifier.getStatePrefixFromKeyPrefix(key, ""))}Item(cursor: ${bobfluxPrefix}.ICursor<${baseStateTypeName}>, index: number): ${bobfluxPrefix}.ICursor<${typeName}> {
return { key: cursor.key + '.${key}.' + index };
}
`;
}
export function createCursorsFilePath(stateFilePath: string): string {
return `${path.join(path.dirname(stateFilePath), path.basename(stateFilePath).replace(path.extname(stateFilePath), ''))}.f.cursors.ts`;
}
interface INextIteration {
externalFileAlias: string
state: tsa.IStateData
data: tsa.IStateSourceData
prefix: string
}
interface IExternalChildState {
state: string
import: tsa.IImportData
prefix: string
parentFullPath: string
}