@knightly/vitepress
Version:
Vite & Vue powered static site generator
128 lines • 5.31 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderPage = void 0;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const config_1 = require("../config");
const vite_1 = require("vite");
const escape = require('escape-html');
async function renderPage(config, page, // foo.md
result, appChunk, cssChunk, pageToHashMap, hashMapString) {
const { createApp } = require(path_1.default.join(config.tempDir, `app.js`));
const { app, router } = createApp();
const routePath = `/${page.replace(/\.md$/, '')}`;
const siteData = config_1.resolveSiteDataByRoute(config.site, routePath);
router.go(routePath);
// lazy require server-renderer for production build
const content = await require('@vue/server-renderer').renderToString(app);
const pageName = page.replace(/\//g, '_');
// server build doesn't need hash
const pageServerJsFileName = pageName + '.js';
// for any initial page load, we only need the lean version of the page js
// since the static content is already on the page!
const pageHash = pageToHashMap[pageName.toLowerCase()];
const pageClientJsFileName = `assets/${pageName}.${pageHash}.lean.js`;
// resolve page data so we can render head tags
const { __pageData } = require(path_1.default.join(config.tempDir, pageServerJsFileName));
const pageData = JSON.parse(__pageData);
const preloadLinks = [
// resolve imports for index.js + page.md.js and inject script tags for
// them as well so we fetch everything as early as possible without having
// to wait for entry chunks to parse
...resolvePageImports(config, page, result, appChunk),
pageClientJsFileName,
appChunk.fileName
]
.map((file) => {
return `<link rel="modulepreload" href="${siteData.base}${file}">`;
})
.join('\n ');
const stylesheetLink = cssChunk
? `<link rel="stylesheet" href="${siteData.base}${cssChunk.fileName}">`
: '';
const title = pageData.title && pageData.title !== 'Home'
? `${pageData.title} | ${siteData.title}`
: siteData.title;
const head = addSocialTags(title, ...siteData.head, ...filterOutHeadDescription(pageData.frontmatter.head));
const html = `
<!DOCTYPE html>
<html lang="${siteData.lang}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>${title}</title>
<meta name="description" content="${pageData.description || siteData.description}">
${stylesheetLink}
${preloadLinks}
${renderHead(head)}
</head>
<body>
<div id="app">${content}</div>
<script>__VP_HASH_MAP__ = JSON.parse(${hashMapString})</script>
<script type="module" async src="${siteData.base}${appChunk.fileName}"></script>
</body>
</html>`.trim();
const htmlFileName = path_1.default.join(config.outDir, page.replace(/\.md$/, '.html'));
await fs_extra_1.default.ensureDir(path_1.default.dirname(htmlFileName));
await fs_extra_1.default.writeFile(htmlFileName, html);
}
exports.renderPage = renderPage;
function resolvePageImports(config, page, result, indexChunk) {
// find the page's js chunk and inject script tags for its imports so that
// they are start fetching as early as possible
const srcPath = vite_1.normalizePath(fs_extra_1.default.realpathSync(path_1.default.resolve(config.srcDir, page)));
const pageChunk = result.output.find((chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === srcPath);
return Array.from(new Set([
...indexChunk.imports,
...indexChunk.dynamicImports,
...pageChunk.imports,
...pageChunk.dynamicImports
]));
}
function renderHead(head) {
return head
.map(([tag, attrs = {}, innerHTML = '']) => {
const openTag = `<${tag}${renderAttrs(attrs)}>`;
if (tag !== 'link' && tag !== 'meta') {
return `${openTag}${innerHTML}</${tag}>`;
}
else {
return openTag;
}
})
.join('\n ');
}
function renderAttrs(attrs) {
return Object.keys(attrs)
.map((key) => {
return ` ${key}="${escape(attrs[key])}"`;
})
.join('');
}
function isMetaDescription(headConfig) {
const [type, attrs] = headConfig;
return type === 'meta' && (attrs === null || attrs === void 0 ? void 0 : attrs.name) === 'description';
}
function filterOutHeadDescription(head) {
return head ? head.filter((h) => !isMetaDescription(h)) : [];
}
function hasTag(head, tag) {
const [tagType, tagAttrs] = tag;
const [attr, value] = Object.entries(tagAttrs)[0]; // First key
return head.some(([type, attrs]) => type === tagType && attrs[attr] === value);
}
function addSocialTags(title, ...head) {
const tags = [
['meta', { name: 'twitter:title', content: title }],
['meta', { property: 'og:title', content: title }]
];
tags.filter((tagAttrs) => {
if (!hasTag(head, tagAttrs))
head.push(tagAttrs);
});
return head;
}
//# sourceMappingURL=render.js.map