@knightly/vitepress
Version:
Vite & Vue powered static site generator
199 lines • 8.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createVitePressPlugin = void 0;
const path_1 = __importDefault(require("path"));
const vite_1 = require("vite");
const config_1 = require("./config");
const markdownToVue_1 = require("./markdownToVue");
const alias_1 = require("./alias");
const plugin_vue_1 = __importDefault(require("@vitejs/plugin-vue"));
const slash_1 = require("./utils/slash");
const hashRE = /\.(\w+)\.js$/;
const staticInjectMarkerRE = /\b(const _hoisted_\d+ = \/\*#__PURE__\*\/createStaticVNode)\("(.*)", (\d+)\)/g;
const staticStripRE = /__VP_STATIC_START__.*?__VP_STATIC_END__/g;
const staticRestoreRE = /__VP_STATIC_(START|END)__/g;
const isPageChunk = (chunk) => !!(chunk.type === 'chunk' &&
chunk.isEntry &&
chunk.facadeModuleId &&
chunk.facadeModuleId.endsWith('.md'));
function createVitePressPlugin(root, { srcDir, configPath, alias, markdown, site, vue: userVuePluginOptions, vite: userViteConfig, pages }, ssr = false, pageToHashMap) {
let markdownToVue;
const vuePlugin = plugin_vue_1.default({
include: [/\.vue$/, /\.md$/],
...userVuePluginOptions
});
let siteData = site;
let hasDeadLinks = false;
let config;
const vitePressPlugin = {
name: 'vitepress',
configResolved(resolvedConfig) {
config = resolvedConfig;
markdownToVue = markdownToVue_1.createMarkdownToVueRenderFn(srcDir, markdown, pages, config.define, config.command === 'build');
},
config() {
var _a, _b;
const baseConfig = vite_1.defineConfig({
resolve: {
alias
},
define: {
__CARBON__: !!((_a = site.themeConfig.carbonAds) === null || _a === void 0 ? void 0 : _a.carbon),
__BSA__: !!((_b = site.themeConfig.carbonAds) === null || _b === void 0 ? void 0 : _b.custom),
__ALGOLIA__: !!site.themeConfig.algolia
},
optimizeDeps: {
// force include vue to avoid duplicated copies when linked + optimized
include: ['vue'],
exclude: ['@docsearch/js']
},
server: {
fs: {
allow: [alias_1.APP_PATH, srcDir]
}
}
});
return userViteConfig
? vite_1.mergeConfig(userViteConfig, baseConfig)
: baseConfig;
},
resolveId(id) {
if (id === alias_1.SITE_DATA_REQUEST_PATH) {
return alias_1.SITE_DATA_REQUEST_PATH;
}
},
load(id) {
if (id === alias_1.SITE_DATA_REQUEST_PATH) {
return `export default ${JSON.stringify(JSON.stringify(siteData))}`;
}
},
transform(code, id) {
if (id.endsWith('.md')) {
// transform .md files into vueSrc so plugin-vue can handle it
const { vueSrc, deadLinks, includes } = markdownToVue(code, id, config.publicDir);
if (deadLinks.length) {
hasDeadLinks = true;
}
if (includes.length) {
includes.forEach((i) => {
this.addWatchFile(i);
});
}
return vueSrc;
}
},
renderStart() {
if (hasDeadLinks) {
throw new Error(`One or more pages contain dead links.`);
}
},
configureServer(server) {
server.watcher.add(configPath);
// serve our index.html after vite history fallback
return () => {
server.middlewares.use((req, res, next) => {
if (req.url.endsWith('.html')) {
res.statusCode = 200;
res.end(`
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="">
</head>
<body>
<div id="app"></div>
<script type="module" src="/@fs/${alias_1.APP_PATH}/index.js"></script>
</body>
</html>`);
return;
}
next();
});
};
},
renderChunk(code, chunk) {
if (!ssr && isPageChunk(chunk)) {
// For each page chunk, inject marker for start/end of static strings.
// we do this here because in generateBundle the chunks would have been
// minified and we won't be able to safely locate the strings.
// Using a regexp relies on specific output from Vue compiler core,
// which is a reasonable trade-off considering the massive perf win over
// a full AST parse.
code = code.replace(staticInjectMarkerRE, '$1("__VP_STATIC_START__$2__VP_STATIC_END__", $3)');
return code;
}
return null;
},
generateBundle(_options, bundle) {
if (ssr) {
// ssr build:
// delete all asset chunks
for (const name in bundle) {
if (bundle[name].type === 'asset') {
delete bundle[name];
}
}
}
else {
// client build:
// for each .md entry chunk, adjust its name to its correct path.
for (const name in bundle) {
const chunk = bundle[name];
if (isPageChunk(chunk)) {
// record page -> hash relations
const hash = chunk.fileName.match(hashRE)[1];
pageToHashMap[chunk.name.toLowerCase()] = hash;
// inject another chunk with the content stripped
bundle[name + '-lean'] = {
...chunk,
fileName: chunk.fileName.replace(/\.js$/, '.lean.js'),
code: chunk.code.replace(staticStripRE, ``)
};
// remove static markers from original code
chunk.code = chunk.code.replace(staticRestoreRE, '');
}
}
}
},
async handleHotUpdate(ctx) {
// handle config hmr
const { file, read, server } = ctx;
if (file === configPath) {
const newData = await config_1.resolveSiteData(root);
if (newData.base !== siteData.base) {
console.warn(`[vitepress]: config.base has changed. Please restart the dev server.`);
}
siteData = newData;
return [server.moduleGraph.getModuleById(alias_1.SITE_DATA_REQUEST_PATH)];
}
// hot reload .md files as .vue files
if (file.endsWith('.md')) {
const content = await read();
const { pageData, vueSrc } = markdownToVue(content, file, config.publicDir);
// notify the client to update page data
server.ws.send({
type: 'custom',
event: 'vitepress:pageData',
data: {
path: `/${slash_1.slash(path_1.default.relative(srcDir, file))}`,
pageData
}
});
// reload the content component
return vuePlugin.handleHotUpdate({
...ctx,
read: () => vueSrc
});
}
}
};
return [vitePressPlugin, vuePlugin];
}
exports.createVitePressPlugin = createVitePressPlugin;
//# sourceMappingURL=plugin.js.map