Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: when ssg build, init only one rsbuild instance #1169

Merged
merged 4 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 9 additions & 25 deletions packages/core/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,15 @@ export async function bundle(
enableSSG: boolean,
) {
try {
if (enableSSG) {
const [clientBuilder, ssrBuilder] = await Promise.all([
initRsbuild(docDirectory, config, pluginDriver, false),
initRsbuild(docDirectory, config, pluginDriver, true, {
output: {
minify: false,
},
tools: {
rspack(options) {
options.output.filename = 'main.cjs';
},
},
}),
]);
await Promise.all([clientBuilder.build(), ssrBuilder.build()]);
} else {
// Only build client bundle
const clientBuilder = await initRsbuild(
docDirectory,
config,
pluginDriver,
false,
);
await clientBuilder.build();
}
// if enableSSG, build both client and server bundle
// else only build client bundle
const rsbuild = await initRsbuild(
docDirectory,
config,
pluginDriver,
enableSSG,
);
await rsbuild.build();
} finally {
await writeSearchIndex(config);
}
Expand Down
100 changes: 75 additions & 25 deletions packages/core/src/node/initRsbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function isPluginIncluded(config: UserConfig, pluginName: string): boolean {
async function createInternalBuildConfig(
userDocRoot: string,
config: UserConfig,
isSSR: boolean,
enableSSG: boolean,
routeService: RouteService,
pluginDriver: PluginDriver,
runtimeTempDir: string,
Expand All @@ -61,10 +61,10 @@ async function createInternalBuildConfig(
const CUSTOM_THEME_DIR =
config?.themeDir ?? path.join(process.cwd(), 'theme');
const baseOutDir = config?.outDir ?? OUTPUT_DIR;
const outDir = isSSR ? path.join(baseOutDir, 'ssr') : baseOutDir;
const csrOutDir = baseOutDir;
const ssrOutDir = path.join(baseOutDir, 'ssr');

const DEFAULT_THEME = require.resolve('@rspress/theme-default');
const checkDeadLinks = (config?.markdown?.checkDeadLinks && !isSSR) ?? false;
const base = config?.base ?? '';

// In production, we need to add assetPrefix in asset path
Expand Down Expand Up @@ -95,6 +95,10 @@ async function createInternalBuildConfig(
];
const ssrBrowserslist = ['node >= 14'];

const detectCustomIconAlias = await detectCustomIcon(CUSTOM_THEME_DIR);
const reactSSRAlias = await resolveReactAlias(reactVersion, true);
const reactCSRAlias = await resolveReactAlias(reactVersion, false);

return {
plugins: [
...(isPluginIncluded(config, PLUGIN_REACT_NAME) ? [] : [pluginReact()]),
Expand All @@ -103,7 +107,6 @@ async function createInternalBuildConfig(
rsbuildPluginDocVM({
userDocRoot,
config,
isSSR,
runtimeTempDir,
routeService,
pluginDriver,
Expand All @@ -117,11 +120,9 @@ async function createInternalBuildConfig(
printUrls: ({ urls }) => {
return urls.map(url => `${url}/${removeLeadingSlash(base)}`);
},
publicDir: isSSR
? false
: {
name: path.join(userDocRoot, PUBLIC_DIR),
},
publicDir: {
name: path.join(userDocRoot, PUBLIC_DIR),
},
},
dev: {
progressBar: false,
Expand All @@ -145,19 +146,15 @@ async function createInternalBuildConfig(
].filter(Boolean),
},
output: {
target: isSSR ? 'node' : 'web',
assetPrefix,
distPath: {
// `root` must be a relative path in Rsbuild
root: path.isAbsolute(outDir) ? path.relative(cwd, outDir) : outDir,
// just for rsbuild preview
root: csrOutDir,
},
overrideBrowserslist: isSSR ? ssrBrowserslist : webBrowserslist,
assetPrefix,
},
source: {
entry: {
index: isSSR ? SSR_ENTRY : CLIENT_ENTRY,
},
alias: {
...detectCustomIconAlias,
'@mdx-js/react': require.resolve('@mdx-js/react'),
'@theme': [CUSTOM_THEME_DIR, DEFAULT_THEME],
'@/theme-default': DEFAULT_THEME,
Expand All @@ -166,21 +163,16 @@ async function createInternalBuildConfig(
'react-syntax-highlighter': path.dirname(
require.resolve('react-syntax-highlighter/package.json'),
),
...(await resolveReactAlias(reactVersion, isSSR)),
...(await detectCustomIcon(CUSTOM_THEME_DIR)),
'@theme-assets': path.join(DEFAULT_THEME, '../assets'),
},
include: [PACKAGE_ROOT, path.join(cwd, 'node_modules', RSPRESS_TEMP_DIR)],
define: {
'process.env.__ASSET_PREFIX__': JSON.stringify(assetPrefix),
'process.env.__SSR__': JSON.stringify(isSSR),
'process.env.__IS_REACT_18__': JSON.stringify(reactVersion === 18),
'process.env.TEST': JSON.stringify(process.env.TEST),
},
},
performance: {
// No need to print the server bundles size
printFileSize: !isSSR,
chunkSplit: {
override: {
cacheGroups: {
Expand All @@ -198,13 +190,17 @@ async function createInternalBuildConfig(
},
},
tools: {
bundlerChain(chain, { CHAIN_ID }) {
bundlerChain(chain, { CHAIN_ID, target }) {
const isServer = target === 'node';
const jsModuleRule = chain.module.rule(CHAIN_ID.RULE.JS);

const swcLoaderOptions = jsModuleRule
.use(CHAIN_ID.USE.SWC)
.get('options');

const checkDeadLinks =
(config?.markdown?.checkDeadLinks && !isServer) ?? false;

chain.module
.rule('MDX')
.type('javascript/auto')
Expand Down Expand Up @@ -244,16 +240,70 @@ async function createInternalBuildConfig(
.rule('css-virtual-module')
.test(/\.rspress[\\/]runtime[\\/]virtual-global-styles/)
.merge({ sideEffects: true });

if (isServer) {
chain.output.filename('main.cjs');
}
},
},
environments: {
web: {
source: {
entry: {
index: CLIENT_ENTRY,
},
alias: {
...reactCSRAlias,
},
define: {
'process.env.__SSR__': JSON.stringify(false),
},
},
output: {
target: 'web',
overrideBrowserslist: webBrowserslist,
distPath: {
root: csrOutDir,
},
},
},
...(enableSSG
? {
node: {
source: {
entry: {
index: SSR_ENTRY,
},
alias: {
...reactSSRAlias,
},
define: {
'process.env.__SSR__': JSON.stringify(true),
},
},
performance: {
printFileSize: false,
},
output: {
target: 'node',
overrideBrowserslist: ssrBrowserslist,
distPath: {
root: ssrOutDir,
},
minify: false,
},
},
}
: {}),
},
};
}

export async function initRsbuild(
rootDir: string,
config: UserConfig,
pluginDriver: PluginDriver,
isSSR = false,
enableSSG: boolean,
extraRsbuildConfig?: RsbuildConfig,
): Promise<RsbuildInstance> {
const cwd = process.cwd();
Expand All @@ -276,7 +326,7 @@ export async function initRsbuild(
const internalRsbuildConfig = await createInternalBuildConfig(
userDocRoot,
config,
isSSR,
enableSSG,
routeService,
pluginDriver,
runtimeTempDir,
Expand Down
21 changes: 12 additions & 9 deletions packages/core/src/node/runtimeModule/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,22 @@ export const runtimeModuleFactory: RuntimeModuleFactory[] = [

// We will use this plugin to generate runtime module in browser, which is important to ensure the client have access to some compile-time data
export function rsbuildPluginDocVM(
factoryContext: Omit<FactoryContext, 'alias'>,
factoryContext: Omit<FactoryContext, 'alias' | 'isSSR'>,
): RsbuildPlugin {
const { pluginDriver } = factoryContext;
return {
name: 'rsbuild-plugin-doc-vm',
setup(api) {
api.modifyBundlerChain(async bundlerChain => {
api.modifyBundlerChain(async (bundlerChain, { target }) => {
const isServer = target === 'node';
// The order should be sync
const alias = bundlerChain.resolve.alias.entries();
const runtimeModule: Record<string, string> = {};
// Add internal runtime module
for (const factory of runtimeModuleFactory) {
const moduleResult = await factory({
...factoryContext,
isSSR: isServer,
alias: alias as Record<string, string>,
});
Object.assign(runtimeModule, moduleResult);
Expand All @@ -82,13 +84,14 @@ export function rsbuildPluginDocVM(
}
runtimeModule[key] = modulesByPlugin[key];
});
bundlerChain.plugin('rspress-runtime-module').use(
// @ts-expect-error
new RspackVirtualModulePlugin(
runtimeModule,
factoryContext.runtimeTempDir,
),
);
bundlerChain
.plugin('rspress-runtime-module')
.use(
new RspackVirtualModulePlugin(
runtimeModule,
factoryContext.runtimeTempDir,
),
);
});
},
};
Expand Down
9 changes: 8 additions & 1 deletion packages/shared/src/runtime-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ export const DEFAULT_HIGHLIGHT_LANGUAGES = [
['mdx', 'tsx'],
];

// TODO: these utils should be divided into node and runtime
chenjiahan marked this conversation as resolved.
Show resolved Hide resolved
export const isSCM = () => Boolean(process.env.BUILD_VERSION);

export const isProduction = () => process.env.NODE_ENV === 'production';

export const isDebugMode = () => Boolean(process.env.DOC_DEBUG);
export const isDebugMode = () => {
if (!process.env.DEBUG) {
return false;
}
const values = process.env.DEBUG?.toLocaleLowerCase().split(',') ?? [];
SoonIter marked this conversation as resolved.
Show resolved Hide resolved
return ['rsbuild', 'builder', '*'].some(key => values.includes(key));
};

export const cleanUrl = (url: string): string =>
url.replace(HASH_REGEXP, '').replace(QUERY_REGEXP, '');
Expand Down