lookml-parser
Version:
114 lines (107 loc) • 3.7 kB
JavaScript
const lookmlParser = {parse: require ('../parse')}
const maybeYamlParser = {parse: require('../maybe-yaml-parse')}
const glob = require("glob")
const Promise = require('bluebird')
const fs = require("fs")
const path = require("path")
const readp = Promise.promisify(fs.readFile)
const globp = Promise.promisify(glob)
const defaultConsole = console
const defaultSource = "**/{*.model,*.explore,*.view,manifest}.lkml"
const xf = require("../transformations/index.js")
const indexBy = require("../common/index-by.js")
exports = module.exports = async function lookmlParser_parseFiles({
source
,cwd
,globOptions = {}
,readFileOptions = {encoding:'utf-8'}
,readFileConcurrency = 4
,conditionalCommentString
,console = defaultConsole
,trace = {}
,fileOutput = 'by-name'
,transformations = {
applyExtensionsRefinements = true,
removeAbstract = true,
}={}
}={}){
if(Array.isArray(console)){console = mockConsole(console)}
let fileObjects;
if(Array.isArray(source)){
fileObjects = source.map(s=>({path:s.path, read:()=>Promise.resolve(s.content) }))
}else{
const inputFilePaths = await globp(source||defaultSource, {
...cwd?{cwd}:{},
...globOptions
})
if(!inputFilePaths.length){
if(source){console.warn("Warning: No input files were matched for pattern "+source)}
else{console.warn("Warning: No input files were matched. (Use argument --input=... or source)")}
}
fileObjects = inputFilePaths.map($file_path=>({path:$file_path, read:()=>readp(path.resolve(cwd||process.cwd(),$file_path),readFileOptions) }))
}
const files = await Promise.map(fileObjects, async (fileObject,fp)=>{
const $file_path = fileObject.path
let typeRegex = /\.?([-_a-zA-Z0-9]+)(\.lkml|\.lookml)?$/i
const $file_name = path.basename($file_path).replace(typeRegex,'')
const [match,$file_type,$file_supertype] = path.basename($file_path).match(typeRegex)||[]
const $file_rel = $file_path.replace(typeRegex,'')
var file,result;
try{
file = await fileObject.read()
if($file_supertype == '.lookml'){
result = maybeYamlParser.parse(file)
if($file_type==="dashboard"){
result = {dashboard: {[$file_name]: result}}
}
}
else{
result = lookmlParser.parse(file,{
conditionalCommentString
})
}
}catch(e){result = {error:e}}
return {...result,
$file_path, $file_rel, $file_name, $file_type
}
},{concurrency: readFileConcurrency})
const manifest = files.find(f=>f.$file_type=="manifest")
const project = {
...(files.some(f=>f.error)?{
errors:files.filter(f=>f.error)
}:{}),
file: files.reduce(indexBy(f=>[f.$file_rel,f.$file_type].filter(Boolean).join('.')), {}),
manifest
}
// For now, we always assemble models, but in the future, this could be controllable via a flag
xf.assembleModels(project, {trace})
if(transformations.applyExtensionsRefinements){
xf.applyExtensionsRefinements(project)
}
if(transformations.removeAbstract){
xf.removeAbstract(project)
}
switch(fileOutput){
case 'by-name':
// This is now the internally produced default representation. No-op
break;
case 'none':
delete project.file
break;
case 'array':
delete project.file;
project.files = files;
break;
case 'by-type':
delete project.file;
project.file = {
model: files.filter(f=>f.$file_type=="model" ).reduce(indexBy("$file_rel"),{})
,view: files.filter(f=>f.$file_type=="view" ).reduce(indexBy("$file_rel"),{})
,explore: files.filter(f=>f.$file_type=="explore").reduce(indexBy("$file_rel"),{})
,manifest
};
break;
default: throw new Error("Unrecognized file output argument: "+fileOutput);
}
return project
}