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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1132 additions and 304 deletions

View File

@ -1,11 +1,7 @@
import type { Plugin } from 'vite'; const hljs = require('highlight.js');
import hljs from 'highlight.js'; const MarkdownIt = require('markdown-it');
import MarkdownIt from 'markdown-it';
import { createRequire } from 'node:module';
const isMd = (id: string) => /\.md$/.test(id); function markdownCardWrapper(htmlCode) {
function markdownCardWrapper(htmlCode: string) {
const group = htmlCode const group = htmlCode
.replace(/<h3/g, ':::<h3') .replace(/<h3/g, ':::<h3')
.replace(/<h2/g, ':::<h2') .replace(/<h2/g, ':::<h2')
@ -22,7 +18,7 @@ function markdownCardWrapper(htmlCode: string) {
.join(''); .join('');
} }
function markdownHighlight(str: string, lang: string) { function markdownHighlight(str, lang) {
if (lang && hljs.getLanguage(lang)) { if (lang && hljs.getLanguage(lang)) {
// https://github.com/highlightjs/highlight.js/issues/2277 // https://github.com/highlightjs/highlight.js/issues/2277
return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value; return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value;
@ -38,7 +34,6 @@ const initMarkdownIt = () => {
highlight: markdownHighlight, highlight: markdownHighlight,
}); });
const require = createRequire(import.meta.url);
const { slugify } = require('transliteration'); const { slugify } = require('transliteration');
const markdownItAnchor = require('markdown-it-anchor'); const markdownItAnchor = require('markdown-it-anchor');
@ -52,16 +47,10 @@ const initMarkdownIt = () => {
return md; return md;
}; };
const markdownToVue = ({ const md = initMarkdownIt();
id,
raw, const markdownToVue = (raw) => {
md, let html = md.render(raw);
}: {
id: string;
raw: string;
md: MarkdownIt;
}) => {
let html = md.render(raw, { id });
html = `<div class="van-doc-markdown-body">${html}</div>`; html = `<div class="van-doc-markdown-body">${html}</div>`;
html = markdownCardWrapper(html); html = markdownCardWrapper(html);
// escape curly brackets // escape curly brackets
@ -70,7 +59,7 @@ const markdownToVue = ({
}; };
// add target="_blank" to all links // add target="_blank" to all links
function markdownLinkOpen(md: MarkdownIt) { function markdownLinkOpen(md) {
const defaultRender = md.renderer.rules.link_open; const defaultRender = md.renderer.rules.link_open;
md.renderer.rules.link_open = (tokens, idx, options, env, self) => { md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
@ -88,41 +77,6 @@ function markdownLinkOpen(md: MarkdownIt) {
}; };
} }
export function vitePluginMd(): Plugin { module.exports = function (raw) {
const md = initMarkdownIt(); return markdownToVue(raw);
};
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,
});
};
},
};
}

View File

@ -48,6 +48,10 @@
"dependencies": { "dependencies": {
"@babel/core": "^7.23.2", "@babel/core": "^7.23.2",
"@babel/preset-typescript": "^7.23.2", "@babel/preset-typescript": "^7.23.2",
"@rsbuild/core": "0.1.8",
"@rsbuild/plugin-babel": "0.1.8",
"@rsbuild/plugin-vue": "0.1.8",
"@rsbuild/plugin-vue-jsx": "0.1.8",
"@vant/eslint-config": "workspace:^", "@vant/eslint-config": "workspace:^",
"@vant/touch-emulator": "workspace:^", "@vant/touch-emulator": "workspace:^",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
@ -73,6 +77,7 @@
"postcss": "^8.4.31", "postcss": "^8.4.31",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"rspack-plugin-virtual-module": "^0.1.12",
"terser": "^5.19.2", "terser": "^5.19.2",
"transliteration": "^2.3.5", "transliteration": "^2.3.5",
"typescript": "^5.0.4", "typescript": "^5.0.4",

View File

@ -41,6 +41,8 @@ export const STYLE_DEPS_JSON_FILE = join(DIST_DIR, 'style-deps.json');
// Config files // Config files
export const POSTCSS_CONFIG_FILE = join(CJS_DIR, 'postcss.config.cjs'); export const POSTCSS_CONFIG_FILE = join(CJS_DIR, 'postcss.config.cjs');
export const MD_LOADER = join(CJS_DIR, 'md-loader.cjs');
export const SCRIPT_EXTS = [ export const SCRIPT_EXTS = [
'.js', '.js',
'.jsx', '.jsx',

View File

@ -1,12 +1,20 @@
import { createServer, build } from 'vite'; import { join } from 'path';
import { import { getVantConfig, setBuildTarget } from '../common/index.js';
getViteConfigForSiteDev, import { getTemplateParams } from './get-template-params.js';
getViteConfigForSiteProd,
} from '../config/vite.site.js';
import { mergeCustomViteConfig } from '../common/index.js';
import { genPackageEntry } from './gen-package-entry.js'; import { genPackageEntry } from './gen-package-entry.js';
import { genStyleDepsMap } from './gen-style-deps-map.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> { export function genSiteEntry(): Promise<void> {
return new Promise((resolve, reject) => { 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(); await genSiteEntry();
if (production) {
const config = await mergeCustomViteConfig( const vantConfig = getVantConfig();
getViteConfigForSiteProd(), const assetPrefix = vantConfig.build?.site?.publicPath || '/';
'production',
); const rsbuildConfig: RsbuildConfig = {
await build(config); 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 { } else {
const config = await mergeCustomViteConfig( await rsbuild.startDevServer();
getViteConfigForSiteDev(),
'development',
);
const server = await createServer(config);
await server.listen(config.server?.port);
server.printUrls();
} }
} }

View File

@ -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),
};
}

View File

@ -1,170 +0,0 @@
import { join } from 'node:path';
import vitePluginVue from '@vitejs/plugin-vue';
import vitePluginJsx from '@vitejs/plugin-vue-jsx';
import { vitePluginMd } from '../compiler/vite-plugin-md.js';
import { setBuildTarget, getVantConfig, isDev } from '../common/index.js';
import { SITE_DIST_DIR, SITE_SRC_DIR } from '../common/constant.js';
import lodash from 'lodash';
import type { InlineConfig, PluginOption } from 'vite';
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 { CSS_LANG } from '../common/css.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 '';
}
function vitePluginGenVantBaseCode(): PluginOption {
const virtualMobileModuleId = 'site-mobile-shared';
const resolvedMobileVirtualModuleId = `vant-cli:${virtualMobileModuleId}`;
const virtualDesktopModuleId = 'site-desktop-shared';
const resolvedDesktopVirtualModuleId = `vant-cli:${virtualDesktopModuleId}`;
const virtualPackageStyleModuleId = /package-style/;
const resolvedPackageStyleVirtualModuleId = `vant-cli${virtualPackageStyleModuleId}index.${CSS_LANG}`;
return {
name: 'vite-plugin(vant-cli):gen-site-base-code',
resolveId(id) {
if (id === virtualMobileModuleId) {
return resolvedMobileVirtualModuleId;
}
if (id === virtualDesktopModuleId) {
return resolvedDesktopVirtualModuleId;
}
if (virtualPackageStyleModuleId.test(id)) {
return resolvedPackageStyleVirtualModuleId;
}
},
load(id) {
switch (id) {
case resolvedMobileVirtualModuleId:
return genSiteMobileShared();
case resolvedDesktopVirtualModuleId:
return genSiteDesktopShared();
case resolvedPackageStyleVirtualModuleId:
return genPackageStyle();
default:
break;
}
},
};
}
function vitePluginHTML(data: object): PluginOption {
return {
name: 'vite-plugin-html',
transformIndexHtml: {
enforce: 'pre',
transform(html) {
return lodash.template(html)(data);
},
},
};
}
export function getViteConfigForSiteDev(): InlineConfig {
setBuildTarget('site');
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 {
root: SITE_SRC_DIR,
optimizeDeps: {
// https://github.com/youzan/vant/issues/10930
include: ['vue', 'vue-router'],
},
plugins: [
vitePluginGenVantBaseCode(),
vitePluginVue({
include: [/\.vue$/, /\.md$/],
}),
vitePluginMd(),
vitePluginJsx(),
vitePluginHTML({
...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),
}),
],
server: {
host: '0.0.0.0',
},
};
}
export function getViteConfigForSiteProd(): InlineConfig {
const devConfig = getViteConfigForSiteDev();
const vantConfig = getVantConfig();
const outDir = vantConfig.build?.site?.outputDir || SITE_DIST_DIR;
const publicPath = vantConfig.build?.site?.publicPath || '/';
return {
...devConfig,
base: publicPath,
build: {
outDir,
reportCompressedSize: false,
emptyOutDir: true,
// https://github.com/vant-ui/vant/issues/9703
cssTarget: ['chrome53'],
rollupOptions: {
input: {
main: join(SITE_SRC_DIR, 'index.html'),
mobile: join(SITE_SRC_DIR, 'mobile.html'),
},
output: {
manualChunks: {
'vue-libs': ['vue', 'vue-router'],
},
},
},
},
};
}

View File

@ -2,11 +2,11 @@ import { logger } from 'rslog';
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
const { version: viteVersion } = require('vite/package.json'); const { version: rsbuildVersion } = require('@rsbuild/core/package.json');
const { version: cliVersion } = require('../package.json'); const { version: cliVersion } = require('../package.json');
export { cliVersion }; export { cliVersion };
logger.greet(` Vant CLI v${cliVersion} / Vite v${viteVersion}\n`); logger.greet(` Vant CLI v${cliVersion} / Rsbuild v${rsbuildVersion}\n`);
process.env.VANT_CLI_VERSION = cliVersion; process.env.VANT_CLI_VERSION = cliVersion;

View File

@ -22,7 +22,7 @@ Contains color specifications, font specifications, and component design specifi
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/tab_202009101415.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/tab_202009101415.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant/blob/main/packages/vant/docs/assets/design.sketch?raw=true">Download</a> <a href="https://github.com/vant-ui/vant/blob/main/packages/vant/docs/assets/design.sketch?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">Download</a>
### Icons (Sketch) ### Icons (Sketch)
@ -30,31 +30,10 @@ Contains icon library resources.
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/design-icons-0321.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/design-icons-0321.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant/blob/main/packages/vant-icons/assets/icons.sketch?raw=true">Download</a> <a href="https://github.com/vant-ui/vant/blob/main/packages/vant-icons/assets/icons.sketch?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">Download</a>
### Axure ### Axure
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/vant-axure-0905.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/vant-axure-0905.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant-assets/blob/main/design/axure-20200905.zip?raw=true">Download</a> <a href="https://github.com/vant-ui/vant-assets/blob/main/design/axure-20200905.zip?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">Download</a>
<style>
a.design-download {
display: inline-block;
width: 100px;
color: #fff !important;
line-height: 40px;
text-align: center;
background-color: #38f;
border-radius: 3px;
}
a.design-download:hover {
color: #fff;
opacity: .9;
}
a.design-download:active {
opacity: .7;
}
</style>

View File

@ -22,7 +22,7 @@
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/tab_202009101415.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/tab_202009101415.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant/blob/main/packages/vant/docs/assets/design.sketch?raw=true">下载</a> <a href="https://github.com/vant-ui/vant/blob/main/packages/vant/docs/assets/design.sketch?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">下载</a>
### 图标设计稿Sketch ### 图标设计稿Sketch
@ -30,7 +30,7 @@
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/design-icons-0321.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/design-icons-0321.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant/blob/main/packages/vant-icons/assets/icons.sketch?raw=true">下载</a> <a href="https://github.com/vant-ui/vant/blob/main/packages/vant-icons/assets/icons.sketch?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">下载</a>
#### 在线资源 #### 在线资源
@ -42,25 +42,4 @@ Axure 元件库,由社区的 [@axure-tczy](https://github.com/axure-tczy) 同
<img src="https://fastly.jsdelivr.net/npm/@vant/assets/vant-axure-0905.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)"> <img src="https://fastly.jsdelivr.net/npm/@vant/assets/vant-axure-0905.png" style="width: 80%; box-shadow: 0 1px 2px rgba(0,0,0,.2)">
<a class="design-download" href="https://github.com/vant-ui/vant-assets/blob/main/design/axure-20200905.zip?raw=true">下载</a> <a href="https://github.com/vant-ui/vant-assets/blob/main/design/axure-20200905.zip?raw=true" style="display: inline-block; width: 100px; color: #fff; line-height: 40px; text-align: center; background-color: #38f; border-radius: 3px;">下载</a>
<style>
a.design-download {
display: inline-block;
width: 100px;
color: #fff !important;
line-height: 40px;
text-align: center;
background-color: #38f;
border-radius: 3px;
}
a.design-download:hover {
color: #fff;
opacity: .9;
}
a.design-download:active {
opacity: .7;
}
</style>

970
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff