UNPKG

never-write

Version:
354 lines (353 loc) 15.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generator = void 0; const path_1 = __importDefault(require("path")); const parse_markdown_1 = require("../parse-markdown"); const each_file_1 = require("../utils/each-file"); const lodash_1 = require("lodash"); const dayjs_1 = __importDefault(require("dayjs")); const fs_extra_1 = __importDefault(require("fs-extra")); const ejs_1 = __importDefault(require("ejs")); const cheerio_1 = __importDefault(require("cheerio")); const config_1 = require("../get-config/config"); const html_minifier_terser_1 = require("html-minifier-terser"); const htmlMini = (html, needMini) => { if (!needMini) { return html; } return (0, html_minifier_terser_1.minify)(html, { minifyCSS: true, minifyJS: true, collapseWhitespace: true, removeComments: true, }); }; // ------------------------------------------------------------------------- const presetTemplates = { plain: { index: '../../../templates/theme/plain/index.html', tagIndexes: '../../../templates/theme/plain/index.html', post: '../../../templates/theme/plain/post.html', }, 'plain-dark': { index: '../../../templates/theme/plain-dark/index.html', post: '../../../templates/theme/plain-dark/post.html', tagIndexes: '../../../templates/theme/plain-dark/index.html', }, 'cactus-dark': { index: '../../../templates/theme/cactus-dark/index.html', post: '../../../templates/theme/cactus-dark/post.html', tagIndexes: '../../../templates/theme/cactus-dark/index.html', }, }; // ------------------------------------------------------------------------- // generate posts const generator = (root, config) => __awaiter(void 0, void 0, void 0, function* () { const { build, site, render: renderConfig } = config; const { outDir } = build; const { pageSize } = renderConfig; const distDir = path_1.default.resolve(root, outDir); const postsRoot = path_1.default.resolve(root, build.postsRootPath); const home = `${build.publicPath}index.html`; const postTemplate = (() => { let filePath = ''; if ((0, lodash_1.isString)(renderConfig.template)) { filePath = path_1.default.resolve(__dirname, `${presetTemplates[renderConfig.template].post}`); } else if (renderConfig.template.post) { filePath = path_1.default.join(root, renderConfig.template.post); } else { filePath = path_1.default.resolve(__dirname, `${presetTemplates[config_1.DEFAULT_EJS_TEMPLATE].post}`); } try { return fs_extra_1.default.readFileSync(filePath, 'utf-8'); } catch (err) { console.log(`[INFO] ❌ missing template: ${filePath}`); throw err; } })(); const indexedTemplate = (() => { let filePath = ''; if ((0, lodash_1.isString)(renderConfig.template)) { filePath = path_1.default.resolve(__dirname, `${presetTemplates[renderConfig.template].index}`); } else if (renderConfig.template.index) { filePath = path_1.default.join(root, renderConfig.template.index); } else { filePath = path_1.default.resolve(__dirname, `${presetTemplates[config_1.DEFAULT_EJS_TEMPLATE].index}`); } try { return fs_extra_1.default.readFileSync(filePath, 'utf-8'); } catch (err) { console.log(`[INFO] ❌ missing template: ${filePath}`); throw err; } })(); const tagIndexesTemplate = (() => { let filePath = ''; if ((0, lodash_1.isString)(renderConfig.template)) { filePath = path_1.default.resolve(__dirname, `${presetTemplates[renderConfig.template].tagIndexes}`); } else if (renderConfig.template.tagIndexes) { filePath = path_1.default.join(root, renderConfig.template.tagIndexes); } else { filePath = path_1.default.resolve(__dirname, `${presetTemplates[config_1.DEFAULT_EJS_TEMPLATE].tagIndexes}`); } try { return fs_extra_1.default.readFileSync(filePath, 'utf-8'); } catch (err) { console.log(`[INFO] ❌ missing template: ${filePath}`); throw err; } })(); const hook = (() => { if (build.hook) { try { return require(path_1.default.join(root, build.hook)); } catch (err) { console.log(`[INFO] ❌ missing hook file: ${build.hook}`); throw err; } } return {}; })(); // let outs: Out[] = []; // ------------------------------------------------------------------------- // got each post meta const sourceMarkdownFiles = (0, each_file_1.getEachFilePath)(postsRoot) .filter(({ dir }) => { return dir.endsWith('.md'); }); let outs = sourceMarkdownFiles // ------------------------------------------------------------------------- // make each post configuration // if some day its slow, can use multi thread .map(({ dir }) => { var _a; const sourceStat = fs_extra_1.default.statSync(dir); const str = fs_extra_1.default.readFileSync(dir, 'utf-8'); const { content: md, meta, } = (0, parse_markdown_1.parseMarkdown)(str); const relativePath = path_1.default.relative(postsRoot, dir); const dirChunk = relativePath.split('/'); const [filename] = (_a = dirChunk.pop()) === null || _a === void 0 ? void 0 : _a.split('.'); const relativeDir = dirChunk.join('/'); const postSavedDir = path_1.default.resolve(distDir, relativeDir); const savedFilename = `${filename}.html`; const $ = cheerio_1.default.load(md); const textContent = (0, lodash_1.trim)($.text()); const postRelativePathInSite = path_1.default.join(relativeDir, savedFilename); const out = { meta, relativePath: postRelativePathInSite, sourceFileStat: sourceStat, filename: savedFilename, filenameWithoutExt: filename, site: config.site, html: md, dateFormat: { mtime: (0, dayjs_1.default)(sourceStat.mtime).format(renderConfig.dateFormatter), }, text: textContent, summary: textContent.length > renderConfig.summaryLength ? (`${textContent.substring(0, renderConfig.summaryLength)}...`) : textContent, postSavedDir, postSavedFullPath: path_1.default.resolve(postSavedDir, savedFilename), staticPath: `${build.publicPath}${postRelativePathInSite}`, namespace: 'post', home, render: renderConfig, }; return out; }); // ------------------------------------------------------------------------- // default is sort by modify time outs = hook.sortBy ? outs.sort(hook.sortBy) : outs.sort((a, b) => { const mTimeA = a.sourceFileStat.mtime.getTime(); const mTimeB = b.sourceFileStat.mtime.getTime(); return mTimeB - mTimeA; }); ; const tags = {}; for (let idx = 0, len = outs.length; idx < len; idx++) { const post = outs[idx]; const html = yield htmlMini(ejs_1.default.render(postTemplate, (0, lodash_1.assign)(post, { prevPost: outs[idx - 1], nextPost: outs[idx + 1], })), build.htmlMinify); if (hook.eachBeforeRenderPost) { yield hook.eachBeforeRenderPost(post); } console.log(`[info] saving ${post.relativePath}`); // make sure has dir yield fs_extra_1.default.mkdirp(post.postSavedDir); fs_extra_1.default.writeFile(post.postSavedFullPath, html); if (hook.eachAfterRenderPost) { yield hook.eachAfterRenderPost(post); } // got tag info let tag = post.meta.tag || []; tag.forEach((tagName) => { if (!tags[tagName]) { tags[tagName] = { posts: [], staticPath: `${build.publicPath}tags/${tagName}/1.html`, }; } tags[tagName].posts.push(post); }); } // ------------------------------------------------------------------------- // generate tags { const allTagStoreRoot = path_1.default.join(distDir, '/tags/'); for (let i of Object.entries(tags)) { const [tagName, tagInfo] = i; const { posts } = tagInfo; const storeRoot = path_1.default.join(allTagStoreRoot, tagName); const chunks = (0, lodash_1.chunk)(posts, pageSize); const totalCount = posts.length; for (let pageIndex = 0, len = chunks.length; pageIndex < len; pageIndex++) { const chunk = chunks[pageIndex]; const page = pageIndex + 1; const options = { posts: chunk, page, pageTotal: len, totalCount, selfPath: `/tags/${tagName}/${page}.html`, prevPagePath: page === 1 ? '' : `/tags/${tagName}/${page - 1}.html`, nextPagePath: page === len ? '' : `/tags/${tagName}/${page + 1}.html`, selfStaticPath: `${build.publicPath}tags/${tagName}/${page}.html`, prevPageStaticPath: page === 1 ? '' : `${build.publicPath}tags/${tagName}/${page - 1}.html`, nextPageStaticPath: page === len ? '' : `${build.publicPath}tags/${tagName}/${page + 1}.html`, isFirstPage: page === 1, isEndPage: page === len, site: config.site, namespace: 'tag', tag: tagName, tags, home, render: renderConfig, }; const postListHtml = yield htmlMini(ejs_1.default.render(tagIndexesTemplate, options), build.htmlMinify); const filename = `${page}.html`; const pathPrefix = path_1.default.join('/tags/', tagName); const hookOptions = Object.assign(Object.assign({}, options), { html: postListHtml, storeRoot, filename, pathPrefix }); yield fs_extra_1.default.mkdirp(storeRoot); if (hook.eachBeforeRenderTagsIndexes) { hook.eachBeforeRenderTagsIndexes(hookOptions); } console.log(`[info] saving ${path_1.default.join(pathPrefix, filename)}`); fs_extra_1.default.writeFileSync(path_1.default.join(storeRoot, filename), postListHtml); if (hook.eachAfterRenderTagsIndexes) { hook.eachAfterRenderTagsIndexes(hookOptions); } } } } // ------------------------------------------------------------------------- // generate indexes { const chunks = (0, lodash_1.chunk)(outs, pageSize); const totalCount = outs.length; for (let pageIndex = 0, len = chunks.length; pageIndex < len; pageIndex++) { const chunk = chunks[pageIndex]; const page = pageIndex + 1; const options = { posts: chunk, page, pageTotal: len, totalCount, selfPath: `/page/${page}.html`, prevPagePath: page === 2 ? '/index.html' : `/page/${page - 1}.html`, nextPagePath: `/page/${page + 1}.html`, selfStaticPath: `${build.publicPath}page/${page}.html`, prevPageStaticPath: page === 2 ? `${build.publicPath}index.html` : `${build.publicPath}page/${page - 1}.html`, nextPageStaticPath: `${build.publicPath}page/${page + 1}.html`, isFirstPage: page === 1, isEndPage: page === len, site: config.site, namespace: 'indexes', tags, tag: '', home, render: renderConfig, }; const postListHtml = yield htmlMini(ejs_1.default.render(indexedTemplate, options), build.htmlMinify); let filename = 'index.html'; let pathPrefix = '/'; if (pageIndex !== 0) { filename = `${page}.html`; pathPrefix = '/page/'; } const storeRoot = path_1.default.join(distDir, pathPrefix); const hookOptions = Object.assign(Object.assign({}, options), { html: postListHtml, storeRoot, filename, pathPrefix }); if (hook.eachBeforeRenderIndexes) { hook.eachBeforeRenderIndexes(hookOptions); } yield fs_extra_1.default.mkdirp(storeRoot); console.log(`[info] saving ${path_1.default.join(pathPrefix, filename)}`); fs_extra_1.default.writeFileSync(path_1.default.join(storeRoot, filename), postListHtml); if (hook.eachAfterRenderIndexes) { hook.eachAfterRenderIndexes(hookOptions); } } } // ------------------------------------------------------------------------- // copy public resource to dist root if (build.publicResourcePath) { try { const publicResourcePath = path_1.default.resolve(root, build.publicResourcePath); fs_extra_1.default.statSync(publicResourcePath); fs_extra_1.default.copySync(publicResourcePath, path_1.default.resolve(distDir, build.publicResourcePath)); } catch (err) { throw err; } } else { // ------------------------------------------------------------------------- // default copy `${root}/public` to `${dist}/public` const defaultPublicResourcePath = './public'; try { const publicResourcePath = path_1.default.resolve(root, defaultPublicResourcePath); fs_extra_1.default.copySync(publicResourcePath, path_1.default.resolve(distDir, defaultPublicResourcePath)); } catch (_a) { } } }); exports.generator = generator;