flamingo-carotene-pug
Version:
Provide the pug compiler used in a setup using the flamingo.me/pugtemplate render engine with integration into the flamingo-carotene-dev-server module
192 lines (162 loc) • 5.28 kB
JavaScript
const mkdirp = require('mkdirp')
const fs = require('fs')
const path = require('path')
const pug = require('pug')
const walk = require('pug-walk')
const arguments = process.argv;
const runner = arguments.shift();
const selfName = arguments.shift();
const baseDir = path.resolve(process.cwd());
const sourceDir = path.resolve(baseDir, arguments.shift());
const targetDir = path.resolve(baseDir, arguments.shift());
const nodeDir = path.resolve(baseDir, arguments.shift());
const fileMode = arguments.shift();
switch(fileMode) {
case 'ast':
case 'html':
break;
default:
throw 'Unknown target file type to compile from pug. Only "ast" or "html" supported.';
}
const relativeRoot = arguments.shift();
const pugFilesToCompile = arguments
function StopCompileException(message) {
this.message = 'Enough work for Flamingo Carotene. AST Received. Save time. Quit Compile! '+ message;
}
StopCompileException.prototype = new Error()
let error
for(const pugFile of pugFilesToCompile) {
const pugFilePath = path.resolve(sourceDir, pugFile)
const fileNameFragment = path.relative(relativeRoot, pugFilePath)
let compiledFilePath = path.resolve(targetDir, fileNameFragment)
switch(fileMode) {
case 'ast':
compiledFilePath = compiledFilePath.split('.pug').join('.ast.json')
break;
case 'html':
compiledFilePath = compiledFilePath.split('.pug').join('.html')
break;
}
error = compilePugFile(pugFilePath, compiledFilePath, path.basename(pugFilePath), sourceDir, nodeDir)
if (error) {
break
}
}
if (error) {
console.log(error)
process.exit(1);
}
process.exit(0);
function cleanFilename(filename, basedir) {
// make it relative
filename = filename.split(basedir).join('')
// convert \ to / (to avoid windows paths)
filename = filename.split('\\').join('/')
return filename
}
function optimizeAst(ast, basedir) {
let knownMixins = {}, calledMixins = {}
// pass 1: remove duplicate mixins and identify called mixins
ast = walk(ast, function before(node, replace) {
let cleanUpProperties = false
// make filename relative
if (node.hasOwnProperty('filename')) {
node.filename = cleanFilename(node.filename, basedir)
}
if (node.attrs && node.attrs.hasOwnProperty('filename')) {
node.attrs.filename = cleanFilename(node.attrs.filename, basedir)
}
// drop "line"
if (node.hasOwnProperty('line')) {
delete node.line
}
if (node.hasOwnProperty('attrs') && node.attrs.hasOwnProperty('line')) {
delete node.attrs.line
}
// drop "column"
if (node.hasOwnProperty('column')) {
delete node.column
}
if (node.hasOwnProperty('attrs') && node.attrs.hasOwnProperty('column')) {
delete node.attrs.column
}
// ignore non-mixins
if (node.type === 'Mixin') {
// mark called mixins and continue
if (node.call === true) {
calledMixins[node.name] = true
return true
}
// remove current mixin if already known
if (node.name in knownMixins && replace.arrayAllowed) {
replace([])
return false
}
// mark mixin as known
knownMixins[node.name] = true
}
return true
}, null)
// pass 2: filter unused mixins and flatten empty blocks
ast = walk(ast, function before(node, replace) {
// remove mixin if not called at all
if (node.type === 'Mixin' && !(node.name in calledMixins) && replace.arrayAllowed) {
replace([])
return false
}
return true
}, function after(node, replace) {
// remove Blocks without nodes, as the previous flattening leaves lots of empty leaf blocks
if (node.type === 'Block' && node.nodes.length === 0 && replace.arrayAllowed) {
replace([])
return false
}
return true
})
return ast
}
function compilePugFile(sourcePugFilePath, compiledFilePath, filename, basedir, nodeDir, done) {
let error = null
try {
const content = fs.readFileSync(sourcePugFilePath, 'utf8')
const compiledFn = pug.compile(content, {
filename,
basedir: basedir,
compileDebug: false,
plugins: [
{
resolve(filename, source, options) {
if (filename[0] === '~') {
return path.join(path.join(nodeDir), filename.slice(1))
}
return path.join(filename[0] === '/' ? options.basedir : path.dirname(source.trim()), filename)
},
preCodeGen(ast, options) {
if (fileMode === 'ast') {
ast = optimizeAst(ast, basedir)
const astJson = JSON.stringify(ast, null, ' ')
mkdirp.sync(path.dirname(compiledFilePath))
fs.writeFileSync(compiledFilePath, astJson)
throw new StopCompileException()
}
else {
return ast
}
}
}
]
})
if (fileMode === 'html') {
mkdirp.sync(path.dirname(compiledFilePath))
fs.writeFileSync(compiledFilePath, compiledFn())
}
} catch (e) {
if (e instanceof StopCompileException) {
} else {
error = console.error(`ERROR Compiling ${sourcePugFilePath} to ${compiledFilePath}`)
error+= e;
}
}
return error;
}