UNPKG

sardines-compile-time-tools

Version:

sardines.compile-time-tools.js is part of the sardines.io project

233 lines (210 loc) 10.1 kB
/** * @author Robin Sun * @email robin@naturewake.com * @create date 2019-06-21 14:19:18 * @modify date 2019-06-21 14:19:18 * @desc [description] */ // Compiler // Reference: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API import * as fs from "fs" import * as path from 'path' import { gatherExports } from './parser' import { transform, Service, getServiceName } from './transformer' export const compile = async (compilerSettings: any, targetFiles: string[]) => { const processedFiles: {[key: string]: boolean} = {} const sardineExtName = `.sardine.ts` interface JobResult { services: Service[] error: any|null filePath: string } const processFile = async (targetFilePath: string): Promise<JobResult> => { let services: Service[] = [] let filePath = targetFilePath let error = null if (processedFiles[filePath]) return {services,error,filePath} // recursively process directory if (fs.lstatSync(filePath).isDirectory()) { for(let item of fs.readdirSync(filePath)) { const subFilePath = path.join(filePath, `./${item}`) let subjobResult = await processFile(subFilePath) if (subjobResult.error) { if (compilerSettings.validate || compilerSettings.only_validate) { error = subjobResult.error filePath = subjobResult.filePath break } if (compilerSettings.verbose) { console.error(`ERROR for file ${subjobResult.filePath}:`, subjobResult.error, '\n') } } else { Array.prototype.push.apply(services, subjobResult.services) } } processedFiles[filePath] = true return {services,error,filePath} } // only process files, excluding socket/fifo/device files if (!fs.lstatSync(filePath).isFile()) return {services,error,filePath} // Prepare the file names const dir = path.dirname(filePath) let baseName = path.basename(filePath) const extName = path.extname(baseName) if (extName.toLowerCase() !== '.ts') { error = `unsupported file type '${extName}' for file ${filePath}` return {services,error,filePath} } // check if the file is source file let fileName = '', // the pure file base name WITHOUT any extname nor the prefix dir path sardineFileName = '', // ${fileName}.sardine sardineBaseName = '', // ${sardineFileName}.ts sardineFilePath = '', // file path like ${dir}/${sardineFileName}.ts intermediateFilePath = '' // ${dir}/${sardineFileName}.tmp if (baseName.indexOf(sardineExtName) < 0) { fileName = path.basename(baseName, extName) sardineBaseName = `${fileName}${sardineExtName}` sardineFilePath = `${dir}/${sardineBaseName}` } else { sardineFilePath = filePath sardineBaseName = baseName fileName = path.basename(baseName, sardineExtName) filePath = `${dir}/${fileName}${extName}` } sardineFileName = path.basename(sardineBaseName, extName) intermediateFilePath = `${dir}/${sardineFileName}.tmp` // make sure no duplicated processing if (!compilerSettings.recompile && !compilerSettings.reverse) { if (fs.existsSync(sardineFilePath)) return {services,error,filePath} } else if (fs.existsSync(sardineFilePath)) { if (compilerSettings.verbose) { console.log(`restoring source file ${filePath} from sardine file ${sardineFilePath}`) } fs.renameSync(sardineFilePath, filePath) } if (compilerSettings.reverse) return {services,error,filePath} if (targetFilePath === sardineFilePath) return {services,error,filePath} let sourceFilePath = filePath if (processedFiles[filePath]) return {services,error,filePath} processedFiles[filePath] = true if (compilerSettings.verbose) { console.log(`processing file: ${sourceFilePath}`) } // compile it try { // process the source file // Parse a file const [identifiers, referencedTypes, importedIds, proxyIds] = gatherExports(sourceFilePath); const appName = compilerSettings.application // console.log('parsed application:', appName) // let utils = require('sardines-core').utils // console.log('identifiers:') // utils.inspectedLog(identifiers) // console.log('referencedTypes:') // utils.inspectedLog(referencedTypes) // console.log('importedIds:') // utils.inspectedLog(importedIds) // console.log('proxyIds:') // utils.inspectedLog(proxyIds) services = await transform(appName, fileName, sardineFileName, sourceFilePath, identifiers, referencedTypes, importedIds, proxyIds, (line:string, lineIndex:number) => { if (lineIndex === 0) { if (!compilerSettings.only_validate) { fs.writeFileSync(intermediateFilePath, line) } if (compilerSettings.print) { console.log(line) } } else if (line && !compilerSettings.only_validate) { if (compilerSettings.print) { console.log(line) } fs.appendFileSync(intermediateFilePath, line) } }) // if (!compilerSettings.only_validate && fs.existsSync(intermediateFilePath)) { if (compilerSettings.verbose) { console.log(`renaming source file ${filePath} to sardine file ${sardineFilePath}`) } fs.renameSync(filePath, sardineFilePath) if (compilerSettings.verbose) { console.log(`moving intermediate file ${intermediateFilePath} to replace source file ${filePath}`) } fs.renameSync(intermediateFilePath, filePath) } if (compilerSettings.verbose || compilerSettings.only_validate) { console.log(`successfully processed source file: ${sourceFilePath}\n`) } return {services,error,filePath} } catch (err) { error = err } finally { if (fs.existsSync(intermediateFilePath)) { fs.unlinkSync(intermediateFilePath) } return {services,error,filePath} } } // Prepare service definition file let service_definition_file_content: any = {} let sardineServices: Map<string, Service>|null = null if (compilerSettings.gen_services) { if (fs.existsSync(compilerSettings.gen_services)) { try { service_definition_file_content = JSON.parse(fs.readFileSync(compilerSettings.gen_services).toString()) if (service_definition_file_content.services) { sardineServices = new Map() for (let s of service_definition_file_content.services) { const name = getServiceName(s) if (!sardineServices.has(name)) { sardineServices.set(name, s) } } service_definition_file_content.services = [] } } catch(e) { if (compilerSettings.verbose) { console.error(`error when loading service definition file at [${compilerSettings.gen_services}]:`, e, '\n') } } } else { sardineServices = new Map() service_definition_file_content.services = [] } } Promise.all(targetFiles.map(filePath => processFile(filePath))).then(results => { let hasError = false for (let {services, error, filePath} of <JobResult[]>results) { if (error) { hasError = true if (compilerSettings.verbose || compilerSettings.only_validate || compilerSettings.validate) { console.error(`ERROR while processing ${filePath}:`, error, '\n') } } else if(sardineServices) { for (let s of services) { const name = getServiceName(s) sardineServices.set(name, s) } } } if (!hasError && !compilerSettings.only_validate && sardineServices) { if (compilerSettings.gen_services && Array.isArray(service_definition_file_content.services)) { service_definition_file_content.services = Array.from(sardineServices.values()) if (compilerSettings.application) { service_definition_file_content.application = compilerSettings.application } try { fs.writeFileSync(compilerSettings.gen_services, JSON.stringify(service_definition_file_content, null, 4)) if (compilerSettings.verbose) { console.log(`${service_definition_file_content.services.length} services stored in the sardine definition file at [${compilerSettings.gen_services}]`) } } catch (e) { console.error(`ERROR when writing sardine service definition file at [${compilerSettings.gen_services}]`, e, '\n') } } } return sardineServices }).catch(e => { console.error('UNKNOW ERROR:', e, '\n') }) }