cogear
Version:
Cogear.JS – modern static websites generator (Node.JS/Webpack)
149 lines (147 loc) • 5.28 kB
JavaScript
const path = require('path');
const fs = require('fs');
const mkdirp = require('mkdirp');
const chalk = require('chalk');
const prettyMs = require('pretty-ms');
cogear.parser = require('../utils/parser')();
const {forEach} = require('p-iteration');
module.exports = {
apply(){
cogear.on('build',()=>{
this.build();
});
cogear.on('rebuild',()=>{
this.rebuild();
});
cogear.on('build.page',async(page)=>{
if(typeof page == 'string'){ // If it's a path
let results = await cogear.emit('preload.page',[page]);
page = results.pop();
}
let result = await this.page(page);
return result;
});
},
// When layouts changes rebuild
async rebuild(){
return new Promise(async(resolve)=>{
await forEach(Object.keys(cogear.pages),async(file) => {
await cogear.emit('build.page',cogear.pages[file]);
});
resolve();
});
},
// First time build
async build(){
return new Promise(async(resolve)=>{
const obs = new PerformanceObserver((list, observer) => {
const {duration} = list.getEntriesByName('Build time')[0];
cogear.loader.succeed(`${chalk.whiteBright.bold('Built')} in ${prettyMs(duration)}.`);
cogear.loader.succeed(`${chalk.whiteBright.bold('Finished')} in ${prettyMs(performance.now())}. ${chalk.gray('Preload + Webpack + Build')}`);
performance.clearMarks();
observer.disconnect();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('buildStart');
cogear.loader.start('Building pages…');
// Have to remove per page time benchmarks, because it's all done in parallel now
await forEach(Object.keys(cogear.pages),async(file) => {
await cogear.emit('build.page',cogear.pages[file]);
});
performance.mark('buildEnd');
performance.measure('Build time','buildStart','buildEnd');
cogear.emit('build.done');
resolve();
});
},
async page(page){
return new Promise(async(resolve,reject)=>{
// this.loader.start(`Building ${chalk.bold(page.file)}…`);
/**
* Compile layout
*
* If page layout is set to `false`, it won't be compiled
* If page layout is `undefined`, default "index" layout will be used
* If page layout doesn't have extension, default ".pug" will be used
*/
page.layout = typeof page.layout != 'undefined' ? page.layout : 'index';
let html = ''; // Output HTML code
if(page.layout){
// Pug is default layout template engine
if (!page.layout.match(/\.(html|ejs|pug|hbs)$/)) {
page.layout += '.pug';
}
let layout = '';
/**
* Searching layout
*
* Loop:
* 1. Source folder `layout` dir
* 2. Theme folder `layout` dir
*
* If one is found loop breaks.
*/
let themeSearchPaths = [path.join(cogear.options.src, 'layouts')];
if(cogear.themeDir){
themeSearchPaths.push(path.join(cogear.themeDir,'layouts'));
}
try {
layout = require.resolve(page.layout, {
paths: themeSearchPaths
});
} catch (err) {
cogear.loader.fail(`Page ${chalk.bold(page.file)} layout '${chalk.bold(page.layout)}' doesn't exists: ${chalk.bold(err)}`);
reject(err);
// process.exit()
}
page.layoutPath = layout;
page.basedir = path.dirname(page.layoutPath);
// page.title = page.title || cogear.config.title
// page.cogear = {
// config: cogear.config
// }
// page.cogear = cogear
await cogear.emit('build.page.layout',[page,layout]);
try{
html = await cogear.parser.renderFile(layout, page);
} catch (e){
console.error(`\n ${chalk.red(e)} \n`);
reject(e);
// process.exit()
}
}
else { // If layout is set to `false`, directly page content will be written to the output
html = page.content;
}
await cogear.emit('build.page.chunks',page);
page.chunks.forEach(chunk=>{
let injectElement = page.inject || 'head';
let namedChunk = cogear.compilation.namedChunks.get(chunk);
let files = namedChunk.files;
let scripts = '';
let styles = '';
if(files){
files.forEach(file => {
switch(path.parse(file).ext.replace(/(\?.+)/,'')){
case '.css':
styles += `<link rel="stylesheet" href="/${file}"/>`;
break;
case '.js':
scripts += `<script src="/${file}"></script>`;
break;
}
});
}
html = html.replace(`</${injectElement}>`,`${styles}${scripts}</${injectElement}>`);
});
await cogear.emit('build.page.write',[page,html]);
page.writePath = path.join(cogear.options.output,page.path);
mkdirp.sync(path.dirname(page.writePath));
fs.writeFileSync(page.writePath, html);
await cogear.emit('build.page.writeAfter',[page,html]);
// this.bar.update(this.pagesDoneCount+=1)
// cogear.loader.succeed(`${chalk.bold(page.file)} => ${chalk.bold(page.path)}`);
resolve(page);
});
}
};