meblog
Version:
A simple blog engine for personal blogging
323 lines (322 loc) • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const gulp_1 = tslib_1.__importDefault(require("gulp"));
const path_1 = tslib_1.__importDefault(require("path"));
const del_1 = tslib_1.__importDefault(require("del"));
const browser_sync_1 = tslib_1.__importDefault(require("browser-sync"));
const gulp_clean_css_1 = tslib_1.__importDefault(require("gulp-clean-css"));
const gulp_autoprefixer_1 = tslib_1.__importDefault(require("gulp-autoprefixer"));
const gulp_sass_1 = tslib_1.__importDefault(require("gulp-sass"));
const gulp_sourcemaps_1 = tslib_1.__importDefault(require("gulp-sourcemaps"));
const gulplog_1 = tslib_1.__importDefault(require("gulplog"));
const ansi_colors_1 = tslib_1.__importDefault(require("ansi-colors"));
const i18n_1 = tslib_1.__importDefault(require("i18n"));
const FilesSource_1 = tslib_1.__importDefault(require("./source/FilesSource"));
const TemplateRenderer_1 = tslib_1.__importDefault(require("./template/TemplateRenderer"));
const SampleGenerator_1 = tslib_1.__importDefault(require("./SampleGenerator"));
const RssGenerator_1 = tslib_1.__importDefault(require("./RssGenerator"));
const ConfigHolder_1 = tslib_1.__importDefault(require("./ConfigHolder"));
const emittery_1 = tslib_1.__importDefault(require("emittery"));
const StringUtils_1 = tslib_1.__importDefault(require("./util/StringUtils"));
const GulpUtils_1 = tslib_1.__importDefault(require("./util/GulpUtils"));
const DEV_PORT = 3000;
const DEFAULT_LOCALE = 'en';
class SiteGenerator extends ConfigHolder_1.default {
dataSource;
renderer;
generator;
eventEmitter;
browserSync;
args;
constructor(config, args) {
super(config);
this.args = args;
this.generator = new SampleGenerator_1.default();
this.eventEmitter = new emittery_1.default();
this.browserSync = browser_sync_1.default.create('meblog');
this.registerEvents();
this.setupI18n();
}
setupI18n() {
if (!this.config.defaultLocale) {
this.config.defaultLocale = DEFAULT_LOCALE;
}
let { locales = [] } = this.config;
if (typeof locales === 'string') {
locales = [locales];
}
if (!locales.includes(this.config.defaultLocale)) {
locales.push(this.config.defaultLocale);
}
this.config.locales = locales;
i18n_1.default.configure({
locales,
directory: path_1.default.join(this.config.rootDir, './i18n'),
defaultLocale: this.config.defaultLocale,
autoReload: false,
updateFiles: !!this.args['auto-update-i18n-files'],
});
}
registerEvents() {
if (typeof this.config.eventRegister === 'function') {
this.eventEmitter.clearListeners();
this.config.eventRegister.call(this, this.eventEmitter);
}
}
initRenderer() {
this.dataSource = new FilesSource_1.default(this.config, this.postsDirPath);
this.renderer = new TemplateRenderer_1.default(this.dataSource);
}
get outputRelativeDirectory() {
const outDir = this.args['outdir'];
if (outDir) {
return outDir;
}
return this.config.devMode ? './dev' : './docs';
}
get outputDirectory() {
return path_1.default.resolve(this.config.rootDir, this.outputRelativeDirectory);
}
getOutputDirectory(locale) {
const outdir = this.outputDirectory;
if (this.isDefaultLocale(locale)) {
return outdir;
}
return path_1.default.join(this.outputDirectory, locale);
}
get postsDirPath() {
return path_1.default.join(this.config.rootDir, 'posts');
}
logOutputDir() {
gulplog_1.default.info('Output directory:', ansi_colors_1.default.blue(this.outputRelativeDirectory));
}
async clean() {
del_1.default.sync([this.outputDirectory]);
}
async cleanCache() {
gulplog_1.default.info(ansi_colors_1.default.green('Cleaning cache'));
del_1.default.sync(['./cache']);
}
async cleanPosts() {
del_1.default.sync(`${this.postsDirPath}/*`);
}
loadData() {
gulplog_1.default.info(ansi_colors_1.default.green('Loading posts'));
this.initRenderer();
this.dataSource.loadData();
}
_renderTemplates(templateGlob, renderFn, locale) {
return new Promise((resolve) => {
gulp_1.default.src(templateGlob, { allowEmpty: true })
.pipe(GulpUtils_1.default.handleStreamError())
.pipe(renderFn(locale))
.pipe(gulp_1.default.dest(this.getOutputDirectory(locale)))
.on('end', resolve);
});
}
async renderTemplates(templateGlob, renderFn) {
for (const locale of this.config.locales) {
i18n_1.default.setLocale(locale);
await this._renderTemplates(templateGlob, renderFn, locale);
}
}
async generatePages() {
gulplog_1.default.info(ansi_colors_1.default.green('Rendering pages'));
await this.renderTemplates('./templates/pages/**/*.pug', this.renderer.renderPages.bind(this.renderer));
}
async generatePosts() {
gulplog_1.default.info(ansi_colors_1.default.green('Rendering posts'));
await this.renderTemplates('./templates/posts/*.pug', this.renderer.renderPosts.bind(this.renderer));
}
async generateTags() {
gulplog_1.default.info(ansi_colors_1.default.green('Rendering tags'));
await this.renderTemplates('./templates/tags/tag.pug', this.renderer.renderTags.bind(this.renderer));
}
async generateTemplates() {
await this.runSeries([
'generatePages',
'generatePosts',
'generateTags',
'reloadBrowser',
]);
}
async generateRssFeed() {
gulplog_1.default.info(ansi_colors_1.default.green('Generating RSS feed'));
const rssGenerator = new RssGenerator_1.default(this.dataSource);
for (const locale of this.config.locales) {
i18n_1.default.setLocale(locale);
rssGenerator.generate(this.getOutputDirectory(locale), locale);
}
}
async generateCss() {
gulplog_1.default.info(ansi_colors_1.default.green('Generating CSS'));
return new Promise((resolve) => {
let stream = gulp_1.default.src('./scss/main.scss', {
allowEmpty: true,
});
if (this.config.devMode) {
stream = stream.pipe(gulp_sourcemaps_1.default.init());
}
stream = stream.pipe(gulp_sass_1.default().on('error', gulp_sass_1.default.logError));
if (this.config.devMode) {
stream = stream.pipe(gulp_sourcemaps_1.default.write());
}
else {
stream = stream.pipe(gulp_autoprefixer_1.default()).pipe(gulp_clean_css_1.default());
}
return stream
.pipe(gulp_1.default.dest(this.outputDirectory))
.on('end', resolve)
.pipe(this.browserSync.stream());
});
}
copyAssets() {
gulplog_1.default.info(ansi_colors_1.default.green('Copying assets'));
return new Promise((resolve) => {
gulp_1.default.src('./assets/**/*', { allowEmpty: true })
.pipe(gulp_1.default.dest(this.outputDirectory))
.on('end', resolve);
});
}
reloadConfig() {
gulplog_1.default.info(ansi_colors_1.default.green('Reloading config'));
const configFilePath = this.args['configFilePath'];
delete require.cache[configFilePath];
const newConfig = require(configFilePath);
Object.assign(this.config, newConfig);
this.registerEvents();
}
reloadBrowser() {
this.browserSync.reload();
}
async onServe() {
gulplog_1.default.info(ansi_colors_1.default.green('Starting local development server'));
this.browserSync.init({
server: {
baseDir: this.outputRelativeDirectory,
},
port: this.args['port'] || DEV_PORT,
open: this.args['no-open'] ? false : 'local',
});
gulp_1.default.watch(this.args['configFilePath'], gulp_1.default.series('reloadConfig', 'dev', 'generateTemplates'));
gulp_1.default.watch('./scss/**/*.@(scss|sass)', gulp_1.default.series('generateCss'));
gulp_1.default.watch('./templates/pages/**/*.pug', gulp_1.default.series('generatePages', 'reloadBrowser'));
gulp_1.default.watch('./templates/posts/**/*.pug', gulp_1.default.series('generatePosts', 'reloadBrowser'));
gulp_1.default.watch('./templates/tags/**/*.pug', gulp_1.default.series('generateTags', 'reloadBrowser'));
gulp_1.default.watch([
'./templates/**/*.pug',
'!./templates/pages/**/*.pug',
'!./templates/posts/**/*.pug',
'!./templates/tags/**/*.pug',
], gulp_1.default.series('generateTemplates', 'reloadBrowser'));
gulp_1.default.watch('./assets/**/*', gulp_1.default.series('copyAssets', 'reloadBrowser'));
const watcher = gulp_1.default.watch(this.postsDirPath + '/**/*.md');
watcher
.on('change', this.onUpdateMarkdownPost.bind(this))
.on('add', this.onUpdateMarkdownPost.bind(this));
}
onUpdateMarkdownPost(path) {
if (!(this.dataSource instanceof FilesSource_1.default)) {
gulplog_1.default.info('Not support compile posts from local files');
return;
}
const posts = this.dataSource.parsePostsFromPaths([path]);
gulp_1.default.src('./templates/posts/*.pug', { allowEmpty: true })
.pipe(GulpUtils_1.default.handleStreamError())
.pipe(this.renderer.renderSpecifiedPosts(posts))
.pipe(gulp_1.default.dest(this.outputDirectory))
.pipe(this.browserSync.stream());
const postUrls = posts.map((p) => this.postRootUrl(p));
gulplog_1.default.info(`[POST UPDATED] ${postUrls.join(', ')}`);
}
async generateSamplePosts() {
gulplog_1.default.info(ansi_colors_1.default.green('Generating sample posts'));
const generator = new SampleGenerator_1.default();
const numberOfPosts = Number(this.args['number-of-posts']) || 10;
generator.generateMarkdownPostsAndSave(numberOfPosts, this.postsDirPath);
}
async prod() {
this.config.devMode = false;
}
async dev() {
this.config.devMode = true;
this.config.baseUrl = `http://localhost:${DEV_PORT}`;
this.config.baseContext = '';
}
async newDraft() {
gulplog_1.default.info(ansi_colors_1.default.green('Generating a draft'));
const generator = new SampleGenerator_1.default();
generator.generateEmptyMarkdownPostAndSave(this.postsDirPath);
}
runSeries(tasks) {
return new Promise((resolve, reject) => {
gulp_1.default.series(tasks)(function (err) {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
async build() {
gulplog_1.default.info(ansi_colors_1.default.green('Start building'));
this.logOutputDir();
await this.runSeries([
'clean',
'copyAssets',
'loadData',
'generateTemplates',
'generateRssFeed',
'generateCss',
]);
gulplog_1.default.info(ansi_colors_1.default.green("Build's completed"));
}
async serve() {
await this.runSeries(['dev', 'build', 'onServe']);
}
wrap(func) {
return async function () {
const funcCapitalized = StringUtils_1.default.capitalize(func.name);
gulplog_1.default.debug(`[BEFORE] ${funcCapitalized}`);
await this.eventEmitter.emitSerial(`BEFORE:${funcCapitalized}`);
await func.call(this);
gulplog_1.default.debug(`[AFTER] ${funcCapitalized}`);
await this.eventEmitter.emitSerial(`AFTER:${funcCapitalized}`);
};
}
registerTask(func) {
gulp_1.default.task(func.name, this.wrap(func).bind(this));
}
initTasks() {
const tasks = [
this.prod,
this.dev,
this.clean,
this.cleanPosts,
this.cleanCache,
this.copyAssets,
this.loadData,
this.generatePages,
this.generatePosts,
this.generateTags,
this.generateTemplates,
this.generateRssFeed,
this.generateCss,
this.generateSamplePosts,
this.newDraft,
this.onServe,
this.reloadConfig,
this.reloadBrowser,
this.build,
this.serve,
];
tasks.forEach((t) => this.registerTask(t));
}
async run(tasks) {
await this.runSeries(tasks);
}
}
exports.default = SiteGenerator;