UNPKG

bobflux-gen

Version:

Generator for monkey files in bobflux application.

193 lines (174 loc) 9.26 kB
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); const bobfluxImport = params.data.sourceDeps[bobfluxPrefix]; let imports: { [fullPath: string]: tsa.IImportData } = bobfluxImport ? { [bobfluxImport.fullPath]: bobfluxImport } : {}; let stateFieldCursors = createCursorsForStateFields(params, imports, rootStateKey, params.data, mainState, bobfluxPrefix, stateAlias); writeCallback( createCursorsFilePath(stateFilePath), g.createAutogeneratedHeader(project.version) + g.createFullImports(stateAlias, `./${params.data.fileName}`, Object.keys(imports).map(key => repairRelativePath(imports[key], stateFilePath))) + createRootKey(rootStateKey, bobfluxPrefix) + createRootCursor(rootStateKey, bobfluxPrefix, stateAlias, mainState.typeName) + stateFieldCursors ); logger.info('Generating ended for: ', stateFilePath); } function createCursorsForStateFields( params: g.ILoadedParams, topLevelImports: { [fullPath: string]: tsa.IImportData }, parentStateKey: string, data: tsa.IStateSourceData, state: tsa.IStateData, bobfluxPrefix: string, stateAlias: string, prefix: string = null ): string { let nexts: INextIteration[] = []; function queue(p: { state: tsa.IStateData, data: tsa.IStateSourceData, externalFileAlias: string, prefix: string }) { nexts.push(p); p.state.heritages.forEach(heritageName => { let heritage = p.data.states.find(s => s.typeName === heritageName); // cross-file heritage is not implemented yet if (heritage) nexts.push(Object.assign({}, p, { state: heritage })); }); } 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.forEach(t => { if (t.isArray || !g.isExternalState(t.name, data)) return; let alias = g.getExternalAlias(t.name, data, topLevelImports); 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) if (g.isRouteComponentState(...innerResolvedState.heritages) && f.type.length === 1) writeCursors({ stateFilePath: innerSourceFile.path, data: tsAnalyzer.getSourceData(innerSourceFile, params.typeChecker), sourceFiles: params.sourceFiles, typeChecker: params.typeChecker }, alias.sourceType, g.composeCursorKey(parentStateKey, key)); else queue({ state: innerResolvedState, data: innerData, externalFileAlias: alias.prefix, prefix: key }); } }); } const fieldType = g.getFullType(f.type, data, stateAlias, topLevelImports); f.type.forEach(t => { if (t.isArray) return; let states = data.states.filter(s => s.typeName === t.name); if (states.length > 0 && !t.arguments /* not implemented yet*/) queue({ 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.'; }); // if (f.type.map(t => t.name).indexOf('undefined') >= 0) // return ''; return createFieldCursor(prefix, key, f.name, bobfluxPrefix, fieldType, parentStateKey !== null, f); }).join('\n'); return inner + (nexts.length > 0 ? '\n' : '') + nexts.map(n => createCursorsForStateFields(params, topLevelImports, parentStateKey, n.data, n.state, bobfluxPrefix, n.externalFileAlias, n.prefix)).filter(n => n).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 createRootKey(key: string, bobfluxPrefix: string): string { return `export const rootKey = ${key ? `'${key}'` : `${bobfluxPrefix}.rootCursor.key`}; `; } function createFieldCursor(prefix: string, key: string, fieldName: string, bobfluxPrefix: string, typeName: string, withRoot: boolean, f: tsa.IStateFieldData): string { let undefinedDef = f.type.some(t => t.name === 'undefined') ? `, isUndefinable: true` : ''; return `export const ${prefix === null ? fieldName : nameUnifier.getStatePrefixFromKeyPrefix(prefix, fieldName)}Cursor: ${bobfluxPrefix}.ICursor<${typeName}> = { key: ${withRoot ? `rootKey + '.${key}'` : `'${key}'`}${undefinedDef} }; `; } function createArrayIndexFactoryCursor(prefix: string, key: string, fieldName: string, bobfluxPrefix: string, typeName: string, withRoot: boolean): string { return ` export const ${prefix === null ? fieldName : nameUnifier.getStatePrefixFromKeyPrefix(prefix, fieldName)}IndexFactoryCursor: (index: number) => ${bobfluxPrefix}.IIndexCursorReslut = (index: number) => { return { cursor: <${bobfluxPrefix}.ICursor<${typeName}>>{ key: ${withRoot ? `rootKey + '.${key}.'` : `'${key}.'`} + index } }; }; `; } function createRootCursor(key: string, bobfluxPrefix: string, stateAlias: string, typeName: string): string { return `${key ? `export const rootCursor: ${bobfluxPrefix}.ICursor<${stateAlias}.${typeName}> = { key: rootKey }; ` : `export const rootCursor: ${bobfluxPrefix}.ICursor<${stateAlias}.${typeName}> = ${bobfluxPrefix}.rootCursor; `} export default rootCursor; `; } export function createCursorsFilePath(stateFilePath: string): string { return `${path.join(path.dirname(stateFilePath), path.basename(stateFilePath).replace(path.extname(stateFilePath), ''))}.cursors.ts`; } function repairRelativePath(imp: tsa.IImportData, stateFilePath: string): tsa.IImportData { if (!imp.relativePath.startsWith('.')) // modules return imp; let dir = path.dirname(stateFilePath); if (!path.isAbsolute(dir)) dir = path.join(process.cwd(), dir); let fullPath = imp.fullPath; if (!path.isAbsolute(fullPath)) fullPath = path.join(process.cwd(), fullPath); let relativePath = path.relative(dir, fullPath); if (!relativePath.startsWith('.')) relativePath = `.${path.sep}${relativePath}`; return Object.assign({}, imp, { relativePath }); } interface INextIteration { externalFileAlias: string state: tsa.IStateData data: tsa.IStateSourceData prefix: string } interface IExternalChildState { state: string import: tsa.IImportData prefix: string parentFullPath: string }