refactor(CLI): integrate Rsbuild to build website (#12481)

This commit is contained in:
neverland
2023-12-02 21:04:39 +08:00
committed by GitHub
parent 17a65c1471
commit b09b807fe0
10 changed files with 1132 additions and 304 deletions
+86 -21
View File
@@ -1,12 +1,20 @@
import { createServer, build } from 'vite';
import {
getViteConfigForSiteDev,
getViteConfigForSiteProd,
} from '../config/vite.site.js';
import { mergeCustomViteConfig } from '../common/index.js';
import { join } from 'path';
import { getVantConfig, setBuildTarget } from '../common/index.js';
import { getTemplateParams } from './get-template-params.js';
import { genPackageEntry } from './gen-package-entry.js';
import { genStyleDepsMap } from './gen-style-deps-map.js';
import { PACKAGE_ENTRY_FILE } from '../common/constant.js';
import type { RsbuildConfig } from '@rsbuild/core';
import { RspackVirtualModulePlugin } from 'rspack-plugin-virtual-module';
import { CSS_LANG } from '../common/css.js';
import { genSiteMobileShared } from '../compiler/gen-site-mobile-shared.js';
import { genSiteDesktopShared } from '../compiler/gen-site-desktop-shared.js';
import { genPackageStyle } from '../compiler/gen-package-style.js';
import {
MD_LOADER,
SITE_SRC_DIR,
SITE_DIST_DIR,
PACKAGE_ENTRY_FILE,
} from '../common/constant.js';
export function genSiteEntry(): Promise<void> {
return new Promise((resolve, reject) => {
@@ -24,21 +32,78 @@ export function genSiteEntry(): Promise<void> {
});
}
export async function compileSite(production = false) {
export async function compileSite(isProd = false) {
setBuildTarget('site');
const { createRsbuild } = await import('@rsbuild/core');
const { pluginVue } = await import('@rsbuild/plugin-vue');
const { pluginVueJsx } = await import('@rsbuild/plugin-vue-jsx');
const { pluginBabel } = await import('@rsbuild/plugin-babel');
await genSiteEntry();
if (production) {
const config = await mergeCustomViteConfig(
getViteConfigForSiteProd(),
'production',
);
await build(config);
const vantConfig = getVantConfig();
const assetPrefix = vantConfig.build?.site?.publicPath || '/';
const rsbuildConfig: RsbuildConfig = {
plugins: [pluginBabel(), pluginVue(), pluginVueJsx()],
source: {
entry: {
index: join(SITE_SRC_DIR, 'desktop/main.js'),
mobile: join(SITE_SRC_DIR, 'mobile/main.js'),
},
},
dev: {
assetPrefix,
},
output: {
assetPrefix,
distPath: {
root: vantConfig.build?.site?.outputDir || SITE_DIST_DIR,
},
},
html: {
template: ({ entryName }) => join(SITE_SRC_DIR, `${entryName}.html`),
templateParameters: getTemplateParams(),
},
tools: {
bundlerChain(chain, { CHAIN_ID }) {
const vueRule = chain.module.rules
.get(CHAIN_ID.RULE.VUE)
.use(CHAIN_ID.USE.VUE);
const vueLoader = vueRule.get('loader');
const vueOptions = vueRule.get('options');
chain.module
.rule('md')
.test(/\.md$/)
.use('vue')
.loader(vueLoader)
.options(vueOptions)
.end()
.use('md')
.loader(MD_LOADER);
},
rspack: {
plugins: [
new RspackVirtualModulePlugin({
'site-mobile-shared': genSiteMobileShared(),
'site-desktop-shared': genSiteDesktopShared(),
[`package-style.${CSS_LANG}`]: genPackageStyle() || '',
}),
],
},
},
};
const rsbuild = await createRsbuild({
cwd: SITE_SRC_DIR,
rsbuildConfig,
});
if (isProd) {
await rsbuild.build();
} else {
const config = await mergeCustomViteConfig(
getViteConfigForSiteDev(),
'development',
);
const server = await createServer(config);
await server.listen(config.server?.port);
server.printUrls();
await rsbuild.startDevServer();
}
}
@@ -0,0 +1,54 @@
import { getVantConfig, isDev } from '../common/index.js';
function getSiteConfig(vantConfig: any) {
const siteConfig = vantConfig.site;
if (siteConfig.locales) {
return siteConfig.locales[siteConfig.defaultLang || 'en-US'];
}
return siteConfig;
}
function getTitle(config: { title: string; description?: string }) {
let { title } = config;
if (config.description) {
title += ` - ${config.description}`;
}
return title;
}
function getHTMLMeta(vantConfig: any) {
const meta = vantConfig.site?.htmlMeta;
if (meta) {
return Object.keys(meta)
.map((key) => `<meta name="${key}" content="${meta[key]}">`)
.join('\n');
}
return '';
}
export function getTemplateParams() {
const vantConfig = getVantConfig();
const siteConfig = getSiteConfig(vantConfig);
const title = getTitle(siteConfig);
const headHtml = vantConfig.site?.headHtml;
const baiduAnalytics = vantConfig.site?.baiduAnalytics;
const enableVConsole = isDev() && vantConfig.site?.enableVConsole;
return {
...siteConfig,
title,
// `description` is used by the HTML ejs template,
// so it needs to be written explicitly here to avoid error: description is not defined
description: siteConfig.description,
headHtml,
baiduAnalytics,
enableVConsole,
meta: getHTMLMeta(vantConfig),
};
}
@@ -1,128 +0,0 @@
import type { Plugin } from 'vite';
import hljs from 'highlight.js';
import MarkdownIt from 'markdown-it';
import { createRequire } from 'node:module';
const isMd = (id: string) => /\.md$/.test(id);
function markdownCardWrapper(htmlCode: string) {
const group = htmlCode
.replace(/<h3/g, ':::<h3')
.replace(/<h2/g, ':::<h2')
.split(':::');
return group
.map((fragment) => {
if (fragment.indexOf('<h3') !== -1) {
return `<div class="van-doc-card">${fragment}</div>`;
}
return fragment;
})
.join('');
}
function markdownHighlight(str: string, lang: string) {
if (lang && hljs.getLanguage(lang)) {
// https://github.com/highlightjs/highlight.js/issues/2277
return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value;
}
return '';
}
const initMarkdownIt = () => {
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: false,
highlight: markdownHighlight,
});
const require = createRequire(import.meta.url);
const { slugify } = require('transliteration');
const markdownItAnchor = require('markdown-it-anchor');
markdownLinkOpen(md);
md.use(markdownItAnchor, {
level: 2,
slugify,
});
return md;
};
const markdownToVue = ({
id,
raw,
md,
}: {
id: string;
raw: string;
md: MarkdownIt;
}) => {
let html = md.render(raw, { id });
html = `<div class="van-doc-markdown-body">${html}</div>`;
html = markdownCardWrapper(html);
// escape curly brackets
html = html.replace(/<code(.*?)>/g, '<code$1 v-pre>');
return `<template>${html}</template>`;
};
// add target="_blank" to all links
function markdownLinkOpen(md: MarkdownIt) {
const defaultRender = md.renderer.rules.link_open;
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const aIndex = tokens[idx].attrIndex('target');
if (aIndex < 0) {
tokens[idx].attrPush(['target', '_blank']); // add new attribute
}
if (defaultRender) {
return defaultRender(tokens, idx, options, env, self);
}
return self.renderToken(tokens, idx, options);
};
}
export function vitePluginMd(): Plugin {
const md = initMarkdownIt();
return {
name: 'vite-plugin-md',
enforce: 'pre',
transform(raw, id) {
if (!isMd(id)) {
return;
}
try {
return markdownToVue({ id, raw, md });
} catch (e: any) {
this.error(e);
}
},
async handleHotUpdate(ctx) {
if (!isMd(ctx.file)) {
return;
}
const defaultRead = ctx.read;
ctx.read = async function () {
const raw = await defaultRead();
return markdownToVue({
id: ctx.file,
raw,
md,
});
};
},
};
}