UNPKG

@jeli/compiler-cli

Version:

jeli compiler for frontend development

246 lines (222 loc) 7.34 kB
const fs = require('fs-extra'); const glob = require('glob'); const { generateAstSource } = require('./ast.generator'); const path = require('path'); const supportedFiles = ['.js', '.jeli']; /** * cache for holding externalMetaData */ const _metaDataCache = {}; let __compilerInstance = null; const removeExtPath = p => { const ext = path.extname(p); return (ext || supportedFiles.includes(ext)) ? p.substring(0, p.lastIndexOf(ext)) : p; }; /** * * @param {*} fileName * @param {*} filePath */ const generateOutPutFileName = (fileName, filePath, entryFile) => { const match = filePath.split(/\//g); const entryPath = entryFile.split(/\//g); return fileName.split(/\//g).map(key => key === '*' ? match[entryPath.indexOf(key)] : key).join('/'); }; exports.expandFilePath = (entryFile, sourceRoot) => { return glob.sync(entryFile, { cwd: sourceRoot, filter: 'isFile' }); } /** * * @param {*} options * @param {*} buildOptions * @param {*} resolvers * @returns */ async function CompilerObject(options, buildOptions, resolvers) { options = Object.assign({ sourceRoot: '', type: 'library', output: { separator: '\n', header: '', footer: '', entryFile: 'main.js', generateMeta: false, patterns: ['MODULE'], folder: 'dist/' }, }, options); /** * check resolve paths */ options.resolve = options.resolve || {}; options.resolve.paths = (options.resolve.paths || ['./node_modules']); if (resolvers) { if (resolvers.paths) { options.resolve.paths.push.apply(options.resolve.paths, resolvers.paths); } /** * extend alias */ if (resolvers.alias) { options.resolve.alias = Object.assign(resolvers.alias, options.resolve.alias); } } /** * alias definition is used for resolving local dependencies * { * "@jeli/*": "./PATH_TO_RESOLVE/" * } */ if (options.resolve.alias) { Object.keys(options.resolve.alias).forEach(key => { /** * check if alias contains any wildcard * read the diectory path and write the folder names to the alias object */ if (key.indexOf('/*') > -1) { try { fs.readdirSync(options.resolve.alias[key]) .forEach(name => options.resolve.alias[removeExtPath(key.replace('*', name))] = `${options.resolve.alias[key]}${name}`); delete options.resolve.alias[key]; } catch (e) { throw new Error(`Unable to resolve "${options.resolve.alias[key]}" defined in resolve.alias configuration`); } } }); } let outputFiles = {}; function outPutObject(fileEntry) { Object.defineProperties(this, { options: { get: () => options }, buildOptions: { get: () => buildOptions || {} } }); this.files = {}; this.globalImports = {}; this.Directive = {}; this.Element = {}; this.jModule = {}; this.Service = {}; this.queries = {}; this.output = { modules: {}, global: [], templates: {}, styles: {}, tokens: {}, lazyLoads: [] }; this.required = {}; this.exports = []; this.entryFile = fileEntry; } /** * generate a compilerObject for each file to be output */ if (options.output.files) { for (var fileName in options.output.files) { const entryFile = options.output.files[fileName]; if (entryFile.indexOf('*') > -1) { exports.expandFilePath(entryFile, options.sourceRoot) .forEach(filePath => outputFiles[generateOutPutFileName(fileName, filePath, entryFile)] = new outPutObject(filePath)); } else { outputFiles[fileName] = new outPutObject(entryFile); } } } else if (options.output.entryFile) { outputFiles[options.output.entryFile] = new outPutObject(options.output.entryFile); } // clear output if (fs.existsSync(options.output.folder)) { fs.removeSync(options.output.folder); } return outputFiles; } exports.CompilerObject = CompilerObject; /** * * @param {*} tokenName * @param {*} compilerObjectExports */ exports.isExportedToken = (tokenName, compilerObject) => { const found = compilerObject.exports.some(token => (token.exported === tokenName)); if (!found) { const libExported = exports.findTokenInGlobalImports(tokenName, compilerObject, 'exports'); return libExported && libExported.some(token => (token.exported === tokenName)); } return found; } /** * * @param {*} tokenName * @param {*} compilerObject */ exports.findTokenInGlobalImports = (tokenName, compilerObject, propName) => { const libExported = Object.keys(compilerObject.globalImports) .find(lib => compilerObject.globalImports[lib].specifiers.includes(tokenName)); if (libExported && _metaDataCache.hasOwnProperty(libExported)) { return _metaDataCache[libExported][propName] || _metaDataCache[libExported]; } }; exports.findNotExported = (moduleName, specifiers, source) => { const exported = this.getMetaData(moduleName).exports.map(item => item.exported); return specifiers.filter(item => !exported.includes(item.imported)); } exports.getPipeProvider = (pipeName, compilerObject) => { let foundPipe = find(compilerObject.Service); /** * find in imported modules */ if (!foundPipe) { for (const lib in _metaDataCache) { foundPipe = find(_metaDataCache[lib].Service); if (foundPipe) { return { fn: foundPipe, module: lib }; } } } else { return { fn: foundPipe, module: compilerObject.Service[foundPipe].module || 'root' } } return null; function find(services) { return services && Object.keys(services) .find(providerName => (services[providerName].name === pipeName)) } } /** * * @param {*} metaDataPath * @param {*} moduleName */ exports.resolveMetaData = async(deps, importItem) => { if (!_metaDataCache.hasOwnProperty(importItem.source)) { if (deps.metadata && fs.existsSync(deps.metadata)) { _metaDataCache[importItem.source] = JSON.parse(fs.readFileSync(deps.metadata)); } else if (!deps.metadata && fs.existsSync(deps.source)) { _metaDataCache[importItem.source] = ({ imports: [], exports: [] }); generateAstSource(fs.readFileSync(deps.source, 'utf8'), _metaDataCache[importItem.source]); } } }; exports.getMetaData = moduleName => _metaDataCache[moduleName]; exports.session = { save: compilerObject => __compilerInstance = compilerObject, get: () => __compilerInstance, clear: () => __compilerInstance = null };