@modern-js/builder 3.0.0-alpha.0 → 3.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/cjs/createBuilder.js +96 -0
  2. package/dist/cjs/index.js +59 -0
  3. package/dist/cjs/plugins/devtools.js +47 -0
  4. package/dist/cjs/plugins/emitRouteFile.js +75 -0
  5. package/dist/cjs/plugins/environmentDefaults.js +100 -0
  6. package/dist/cjs/plugins/globalVars.js +55 -0
  7. package/dist/cjs/plugins/htmlMinify.js +115 -0
  8. package/dist/cjs/plugins/manifest.js +52 -0
  9. package/dist/cjs/plugins/postcss.js +118 -0
  10. package/dist/cjs/plugins/runtimeChunk.js +54 -0
  11. package/dist/cjs/rsc/common.js +157 -0
  12. package/dist/cjs/rsc/plugins/rsbuild-rsc-plugin.js +169 -0
  13. package/dist/cjs/rsc/plugins/rspack-rsc-client-plugin.js +187 -0
  14. package/dist/cjs/rsc/plugins/rspack-rsc-server-plugin.js +245 -0
  15. package/dist/cjs/rsc/rsc-client-loader.js +71 -0
  16. package/dist/cjs/rsc/rsc-css-loader.js +42 -0
  17. package/dist/cjs/rsc/rsc-server-loader.js +102 -0
  18. package/dist/cjs/rsc/rsc-ssr-loader.js +60 -0
  19. package/dist/cjs/shared/devServer.js +87 -0
  20. package/dist/cjs/shared/getCssSupport.js +121 -0
  21. package/dist/cjs/shared/manifest.js +46 -0
  22. package/dist/cjs/shared/parseCommonConfig.js +209 -0
  23. package/dist/cjs/shared/rsc/rscClientBrowserFallback.js +64 -0
  24. package/dist/cjs/shared/rsc/rscEmptyModule.js +36 -0
  25. package/dist/cjs/shared/utils.js +115 -0
  26. package/dist/cjs/types.js +18 -0
  27. package/dist/compiled/postcss-load-config/index.d.ts +1 -0
  28. package/dist/compiled/postcss-load-config/index.js +608 -0
  29. package/dist/compiled/postcss-load-config/index.js.LICENSE.txt +13 -0
  30. package/dist/compiled/postcss-load-config/license +20 -0
  31. package/dist/compiled/postcss-load-config/package.json +1 -0
  32. package/dist/esm/createBuilder.mjs +59 -0
  33. package/dist/esm/index.mjs +4 -0
  34. package/dist/esm/plugins/devtools.mjs +13 -0
  35. package/dist/esm/plugins/emitRouteFile.mjs +28 -0
  36. package/dist/esm/plugins/environmentDefaults.mjs +66 -0
  37. package/dist/esm/plugins/globalVars.mjs +21 -0
  38. package/dist/esm/plugins/htmlMinify.mjs +81 -0
  39. package/dist/esm/plugins/manifest.mjs +18 -0
  40. package/dist/esm/plugins/postcss.mjs +81 -0
  41. package/dist/esm/plugins/runtimeChunk.mjs +20 -0
  42. package/dist/esm/rsc/common.mjs +87 -0
  43. package/dist/esm/rsc/plugins/rsbuild-rsc-plugin.mjs +124 -0
  44. package/dist/esm/rsc/plugins/rspack-rsc-client-plugin.mjs +143 -0
  45. package/dist/esm/rsc/plugins/rspack-rsc-server-plugin.mjs +211 -0
  46. package/dist/esm/rsc/rsc-client-loader.mjs +37 -0
  47. package/dist/esm/rsc/rsc-css-loader.mjs +8 -0
  48. package/dist/esm/rsc/rsc-server-loader.mjs +58 -0
  49. package/dist/esm/rsc/rsc-ssr-loader.mjs +26 -0
  50. package/dist/esm/shared/devServer.mjs +53 -0
  51. package/dist/esm/shared/getCssSupport.mjs +77 -0
  52. package/dist/esm/shared/manifest.mjs +12 -0
  53. package/dist/esm/shared/parseCommonConfig.mjs +172 -0
  54. package/dist/esm/shared/rsc/rscClientBrowserFallback.mjs +20 -0
  55. package/dist/esm/shared/rsc/rscEmptyModule.mjs +2 -0
  56. package/dist/esm/shared/utils.mjs +53 -0
  57. package/dist/esm/types.mjs +0 -0
  58. package/dist/esm-node/createBuilder.mjs +61 -0
  59. package/dist/esm-node/index.mjs +6 -0
  60. package/dist/esm-node/plugins/devtools.mjs +15 -0
  61. package/dist/esm-node/plugins/emitRouteFile.mjs +30 -0
  62. package/dist/esm-node/plugins/environmentDefaults.mjs +68 -0
  63. package/dist/esm-node/plugins/globalVars.mjs +23 -0
  64. package/dist/esm-node/plugins/htmlMinify.mjs +83 -0
  65. package/dist/esm-node/plugins/manifest.mjs +20 -0
  66. package/dist/esm-node/plugins/postcss.mjs +83 -0
  67. package/dist/esm-node/plugins/runtimeChunk.mjs +22 -0
  68. package/dist/esm-node/rsc/common.mjs +89 -0
  69. package/dist/esm-node/rsc/plugins/rsbuild-rsc-plugin.mjs +126 -0
  70. package/dist/esm-node/rsc/plugins/rspack-rsc-client-plugin.mjs +145 -0
  71. package/dist/esm-node/rsc/plugins/rspack-rsc-server-plugin.mjs +213 -0
  72. package/dist/esm-node/rsc/rsc-client-loader.mjs +39 -0
  73. package/dist/esm-node/rsc/rsc-css-loader.mjs +10 -0
  74. package/dist/esm-node/rsc/rsc-server-loader.mjs +60 -0
  75. package/dist/esm-node/rsc/rsc-ssr-loader.mjs +28 -0
  76. package/dist/esm-node/shared/devServer.mjs +55 -0
  77. package/dist/esm-node/shared/getCssSupport.mjs +79 -0
  78. package/dist/esm-node/shared/manifest.mjs +14 -0
  79. package/dist/esm-node/shared/parseCommonConfig.mjs +174 -0
  80. package/dist/esm-node/shared/rsc/rscClientBrowserFallback.mjs +25 -0
  81. package/dist/esm-node/shared/rsc/rscEmptyModule.mjs +4 -0
  82. package/dist/esm-node/shared/utils.mjs +55 -0
  83. package/dist/esm-node/types.mjs +2 -0
  84. package/dist/types/shared/rsc/rscClientBrowserFallback.d.ts +2 -0
  85. package/dist/types/shared/rsc/rscEmptyModule.d.ts +2 -0
  86. package/dist/{types.d.ts → types/types.d.ts} +2 -16
  87. package/package.json +36 -31
  88. package/dist/createBuilder.js +0 -153
  89. package/dist/index.js +0 -45
  90. package/dist/plugins/babel-post.d.ts +0 -5
  91. package/dist/plugins/babel-post.js +0 -57
  92. package/dist/plugins/devtools.js +0 -42
  93. package/dist/plugins/emitRouteFile.js +0 -70
  94. package/dist/plugins/environmentDefaults.js +0 -99
  95. package/dist/plugins/globalVars.js +0 -50
  96. package/dist/plugins/htmlMinify.js +0 -126
  97. package/dist/plugins/manifest.js +0 -54
  98. package/dist/plugins/postcss.js +0 -133
  99. package/dist/plugins/runtimeChunk.js +0 -55
  100. package/dist/rsc/common.js +0 -174
  101. package/dist/rsc/plugins/rsbuild-rsc-plugin.js +0 -166
  102. package/dist/rsc/plugins/rspack-rsc-client-plugin.js +0 -233
  103. package/dist/rsc/plugins/rspack-rsc-server-plugin.js +0 -290
  104. package/dist/rsc/rsc-client-loader.js +0 -70
  105. package/dist/rsc/rsc-css-loader.js +0 -30
  106. package/dist/rsc/rsc-server-loader.js +0 -95
  107. package/dist/rsc/rsc-ssr-loader.js +0 -58
  108. package/dist/shared/devServer.js +0 -83
  109. package/dist/shared/getCssSupport.js +0 -113
  110. package/dist/shared/manifest.js +0 -38
  111. package/dist/shared/parseCommonConfig.js +0 -232
  112. package/dist/shared/utils.js +0 -109
  113. package/dist/types.js +0 -16
  114. /package/dist/{createBuilder.d.ts → types/createBuilder.d.ts} +0 -0
  115. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  116. /package/dist/{plugins → types/plugins}/devtools.d.ts +0 -0
  117. /package/dist/{plugins → types/plugins}/emitRouteFile.d.ts +0 -0
  118. /package/dist/{plugins → types/plugins}/environmentDefaults.d.ts +0 -0
  119. /package/dist/{plugins → types/plugins}/globalVars.d.ts +0 -0
  120. /package/dist/{plugins → types/plugins}/htmlMinify.d.ts +0 -0
  121. /package/dist/{plugins → types/plugins}/manifest.d.ts +0 -0
  122. /package/dist/{plugins → types/plugins}/postcss.d.ts +0 -0
  123. /package/dist/{plugins → types/plugins}/runtimeChunk.d.ts +0 -0
  124. /package/dist/{rsc → types/rsc}/common.d.ts +0 -0
  125. /package/dist/{rsc → types/rsc}/plugins/rsbuild-rsc-plugin.d.ts +0 -0
  126. /package/dist/{rsc → types/rsc}/plugins/rspack-rsc-client-plugin.d.ts +0 -0
  127. /package/dist/{rsc → types/rsc}/plugins/rspack-rsc-server-plugin.d.ts +0 -0
  128. /package/dist/{rsc → types/rsc}/rsc-client-loader.d.ts +0 -0
  129. /package/dist/{rsc → types/rsc}/rsc-css-loader.d.ts +0 -0
  130. /package/dist/{rsc → types/rsc}/rsc-server-loader.d.ts +0 -0
  131. /package/dist/{rsc → types/rsc}/rsc-ssr-loader.d.ts +0 -0
  132. /package/dist/{shared → types/shared}/devServer.d.ts +0 -0
  133. /package/dist/{shared → types/shared}/getCssSupport.d.ts +0 -0
  134. /package/dist/{shared → types/shared}/manifest.d.ts +0 -0
  135. /package/dist/{shared → types/shared}/parseCommonConfig.d.ts +0 -0
  136. /package/dist/{shared → types/shared}/utils.d.ts +0 -0
@@ -0,0 +1,59 @@
1
+ import { createRsbuild } from "@rsbuild/core";
2
+ import { rsbuildRscPlugin } from "./rsc/plugins/rsbuild-rsc-plugin.mjs";
3
+ import { parseCommonConfig } from "./shared/parseCommonConfig.mjs";
4
+ import { rscClientBrowserFallbackPlugin } from "./shared/rsc/rscClientBrowserFallback.mjs";
5
+ async function parseConfig(builderConfig, options) {
6
+ builderConfig.performance ??= {};
7
+ builderConfig.performance.buildCache ??= true;
8
+ const { rsbuildConfig, rsbuildPlugins } = await parseCommonConfig(builderConfig, options);
9
+ const { sri } = builderConfig.security || {};
10
+ if (sri) if (true === sri) rsbuildConfig.security.sri = {
11
+ enable: 'auto'
12
+ };
13
+ else {
14
+ const algorithm = Array.isArray(sri.hashFuncNames) ? sri.hashFuncNames[0] : void 0;
15
+ rsbuildConfig.security.sri = {
16
+ enable: sri.enabled,
17
+ algorithm
18
+ };
19
+ }
20
+ if (false === Boolean(rsbuildConfig.tools.lightningcssLoader)) {
21
+ const { pluginPostcss } = await import("./plugins/postcss.mjs");
22
+ rsbuildPlugins.push(pluginPostcss({
23
+ autoprefixer: builderConfig.tools?.autoprefixer
24
+ }));
25
+ }
26
+ const enableRsc = builderConfig.server?.rsc ?? false;
27
+ if (enableRsc) {
28
+ const { rscClientRuntimePath, rscServerRuntimePath, internalDirectory } = options;
29
+ rsbuildPlugins.push(rsbuildRscPlugin({
30
+ appDir: options.cwd,
31
+ rscClientRuntimePath,
32
+ rscServerRuntimePath,
33
+ internalDirectory
34
+ }));
35
+ } else rsbuildPlugins.push(rscClientBrowserFallbackPlugin());
36
+ return {
37
+ rsbuildConfig,
38
+ rsbuildPlugins
39
+ };
40
+ }
41
+ async function createRspackBuilder(options) {
42
+ const { cwd = process.cwd(), config, ...rest } = options;
43
+ const { rsbuildConfig, rsbuildPlugins } = await parseConfig(config, {
44
+ ...rest,
45
+ cwd
46
+ });
47
+ rsbuildConfig.plugins = [
48
+ ...rsbuildPlugins,
49
+ ...rsbuildConfig.plugins || []
50
+ ];
51
+ const rsbuild = await createRsbuild({
52
+ cwd,
53
+ rsbuildConfig
54
+ });
55
+ return {
56
+ ...rsbuild
57
+ };
58
+ }
59
+ export { createRspackBuilder, parseConfig };
@@ -0,0 +1,4 @@
1
+ import { createRspackBuilder, parseConfig } from "./createBuilder.mjs";
2
+ import { logger } from "@rsbuild/core";
3
+ import { RUNTIME_CHUNK_NAME, RUNTIME_CHUNK_REGEX, SERVICE_WORKER_ENVIRONMENT_NAME, castArray, isHtmlDisabled } from "./shared/utils.mjs";
4
+ export { RUNTIME_CHUNK_NAME, RUNTIME_CHUNK_REGEX, SERVICE_WORKER_ENVIRONMENT_NAME, castArray, createRspackBuilder as createBuilder, isHtmlDisabled, logger, parseConfig as parseRspackConfig };
@@ -0,0 +1,13 @@
1
+ const pluginDevtool = (options)=>({
2
+ name: 'builder:devtool',
3
+ setup (api) {
4
+ const devtoolJs = 'boolean' == typeof options.sourceMap || options.sourceMap?.js !== void 0;
5
+ if (devtoolJs) return;
6
+ api.modifyBundlerChain((chain, { isProd, isServer })=>{
7
+ const prodDevTool = isServer ? 'source-map' : 'hidden-source-map';
8
+ const devtool = isProd ? prodDevTool : 'cheap-module-source-map';
9
+ chain.devtool(devtool);
10
+ });
11
+ }
12
+ });
13
+ export { pluginDevtool };
@@ -0,0 +1,28 @@
1
+ import node_fs from "node:fs";
2
+ import { join } from "node:path";
3
+ async function isFileExists(file) {
4
+ return node_fs.promises.access(file, node_fs.constants.F_OK).then(()=>true).catch(()=>false);
5
+ }
6
+ const pluginEmitRouteFile = ()=>({
7
+ name: 'builder:emit-route-file',
8
+ setup (api) {
9
+ api.onBeforeStartDevServer(async ({ environments })=>{
10
+ const { fs, ROUTE_SPEC_FILE } = await import("@modern-js/utils");
11
+ const routeFilePath = join(api.context.distPath, ROUTE_SPEC_FILE);
12
+ const htmlPaths = Object.values(environments).reduce((prev, curr)=>({
13
+ ...prev,
14
+ ...curr.htmlPaths
15
+ }), {});
16
+ const routesInfo = Object.entries(htmlPaths).map(([entryName, filename], index)=>({
17
+ urlPath: 0 === index ? '/' : `/${entryName}`,
18
+ entryName,
19
+ entryPath: filename,
20
+ isSPA: true
21
+ }));
22
+ if (!await isFileExists(routeFilePath) && routesInfo.length) await fs.outputFile(routeFilePath, JSON.stringify({
23
+ routes: routesInfo
24
+ }, null, 2));
25
+ });
26
+ }
27
+ });
28
+ export { isFileExists, pluginEmitRouteFile };
@@ -0,0 +1,66 @@
1
+ import { join } from "node:path";
2
+ import { SERVICE_WORKER_ENVIRONMENT_NAME, getBrowserslistWithDefault } from "../shared/utils.mjs";
3
+ const pluginEnvironmentDefaults = (distPath = {})=>({
4
+ name: 'builder:environment-defaults-plugin',
5
+ setup (api) {
6
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>{
7
+ const compatConfig = {};
8
+ if (config.environments?.[SERVICE_WORKER_ENVIRONMENT_NAME]) {
9
+ compatConfig.environments ??= {};
10
+ compatConfig.environments[SERVICE_WORKER_ENVIRONMENT_NAME] = {
11
+ output: {
12
+ polyfill: 'off',
13
+ distPath: {
14
+ root: join(distPath.root || 'dist', distPath.worker || 'worker'),
15
+ js: '',
16
+ css: '',
17
+ jsAsync: '',
18
+ cssAsync: ''
19
+ },
20
+ filename: {
21
+ js: '[name].js'
22
+ }
23
+ }
24
+ };
25
+ }
26
+ if (config.environments?.node) {
27
+ compatConfig.environments ??= {};
28
+ compatConfig.environments.node = {
29
+ output: {
30
+ emitAssets: false,
31
+ distPath: {
32
+ root: join(distPath.root || 'dist', distPath.server || 'bundles'),
33
+ js: '',
34
+ css: '',
35
+ jsAsync: '',
36
+ cssAsync: ''
37
+ }
38
+ }
39
+ };
40
+ }
41
+ return compatConfig.environments ? mergeRsbuildConfig(compatConfig, config) : config;
42
+ });
43
+ api.modifyRsbuildConfig({
44
+ handler: (config)=>{
45
+ const environmentNameOrder = [
46
+ 'web',
47
+ 'node',
48
+ 'workerSSR'
49
+ ];
50
+ config.environments = Object.fromEntries(Object.entries(config.environments).sort((a1, a2)=>environmentNameOrder.includes(a1[0]) ? environmentNameOrder.indexOf(a1[0]) - environmentNameOrder.indexOf(a2[0]) : 1));
51
+ },
52
+ order: 'post'
53
+ });
54
+ api.modifyEnvironmentConfig(async (config, { name })=>{
55
+ config.output.overrideBrowserslist ??= await getBrowserslistWithDefault(api.context.rootPath, config, name === SERVICE_WORKER_ENVIRONMENT_NAME ? 'node' : config.output.target);
56
+ });
57
+ api.modifyBundlerChain(async (chain, { environment })=>{
58
+ const isServiceWorker = environment.name === SERVICE_WORKER_ENVIRONMENT_NAME;
59
+ if (isServiceWorker) chain.output.library({
60
+ ...chain.output.get('library') || {},
61
+ type: 'commonjs2'
62
+ });
63
+ });
64
+ }
65
+ });
66
+ export { pluginEnvironmentDefaults };
@@ -0,0 +1,21 @@
1
+ import { applyOptionsChain } from "@modern-js/utils";
2
+ const pluginGlobalVars = (options)=>({
3
+ name: 'builder:global-vars',
4
+ setup (api) {
5
+ api.modifyBundlerChain((chain, { env, target, bundler })=>{
6
+ if (!options) return;
7
+ const globalVars = applyOptionsChain({}, options, {
8
+ env,
9
+ target
10
+ });
11
+ const serializedVars = {};
12
+ Object.entries(globalVars).forEach(([key, value])=>{
13
+ serializedVars[key] = JSON.stringify(value) ?? 'undefined';
14
+ });
15
+ chain.plugin('globalVars').use(bundler.DefinePlugin, [
16
+ serializedVars
17
+ ]);
18
+ });
19
+ }
20
+ });
21
+ export { pluginGlobalVars };
@@ -0,0 +1,81 @@
1
+ import { merge } from "ts-deepmerge";
2
+ function applyRemoveConsole(options, config) {
3
+ const { removeConsole } = config.performance;
4
+ const compressOptions = 'boolean' == typeof options.compress ? {} : options.compress || {};
5
+ if (true === removeConsole) options.compress = {
6
+ ...compressOptions,
7
+ drop_console: true
8
+ };
9
+ else if (Array.isArray(removeConsole)) {
10
+ const pureFuncs = removeConsole.map((method)=>`console.${method}`);
11
+ options.compress = {
12
+ ...compressOptions,
13
+ pure_funcs: pureFuncs
14
+ };
15
+ }
16
+ return options;
17
+ }
18
+ function getTerserMinifyOptions(config) {
19
+ const options = {
20
+ mangle: {
21
+ safari10: true
22
+ },
23
+ format: {
24
+ ascii_only: 'ascii' === config.output.charset
25
+ }
26
+ };
27
+ if ('none' === config.output.legalComments) {
28
+ options.format ||= {};
29
+ options.format.comments = false;
30
+ }
31
+ const finalOptions = applyRemoveConsole(options, config);
32
+ return finalOptions;
33
+ }
34
+ function getMinifyOptions(config) {
35
+ const minifyJS = getTerserMinifyOptions(config);
36
+ return {
37
+ removeComments: false,
38
+ useShortDoctype: true,
39
+ keepClosingSlash: true,
40
+ collapseWhitespace: true,
41
+ removeRedundantAttributes: true,
42
+ removeScriptTypeAttributes: true,
43
+ removeStyleLinkTypeAttributes: true,
44
+ removeEmptyAttributes: true,
45
+ minifyJS,
46
+ minifyCSS: true,
47
+ minifyURLs: true
48
+ };
49
+ }
50
+ const pluginHtmlMinifierTerser = ()=>({
51
+ name: 'builder:plugin-html-minifier-terser',
52
+ setup (api) {
53
+ api.modifyBundlerChain(async (chain, { isProd, environment })=>{
54
+ const { output, tools: { htmlPlugin } } = environment.config;
55
+ const disableHtmlMinify = !isProd || false === output.minify || false === htmlPlugin;
56
+ const { minify } = await import("html-minifier-terser");
57
+ const pluginRecord = chain.plugins.entries();
58
+ const minifyOptions = getMinifyOptions(environment.config);
59
+ for (const id of Object.keys(pluginRecord)){
60
+ if (!id.startsWith('html-')) continue;
61
+ const values = pluginRecord[id].values();
62
+ const isHtmlRspackPlugin = values.some((item)=>{
63
+ const name = item?.name || item.constructor?.name;
64
+ return 'HtmlRspackPlugin' === name;
65
+ });
66
+ if (isHtmlRspackPlugin && !disableHtmlMinify) {
67
+ chain.plugin(id).tap((options)=>{
68
+ if (!options.length) return options;
69
+ const userMinifyOption = options[0].minify;
70
+ if (false === userMinifyOption) return options;
71
+ const minifyFn = (html)=>minify(html, userMinifyOption ? merge(minifyOptions, userMinifyOption) : minifyOptions);
72
+ options[0].minify = minifyFn;
73
+ return options;
74
+ });
75
+ continue;
76
+ }
77
+ }
78
+ });
79
+ }
80
+ });
81
+ export { pluginHtmlMinifierTerser };
@@ -0,0 +1,18 @@
1
+ import { generateManifest } from "../shared/manifest.mjs";
2
+ const pluginManifest = ()=>({
3
+ name: 'builder:manifest',
4
+ setup (api) {
5
+ api.modifyBundlerChain(async (chain, { target, CHAIN_ID })=>{
6
+ const { RspackManifestPlugin } = await import("rspack-manifest-plugin");
7
+ const publicPath = chain.output.get('publicPath');
8
+ chain.plugin(CHAIN_ID.PLUGIN.MANIFEST).use(RspackManifestPlugin, [
9
+ {
10
+ fileName: 'web' === target ? 'asset-manifest.json' : `asset-manifest-${target}.json`,
11
+ publicPath,
12
+ generate: generateManifest
13
+ }
14
+ ]);
15
+ });
16
+ }
17
+ });
18
+ export { pluginManifest };
@@ -0,0 +1,81 @@
1
+ import { createRequire } from "node:module";
2
+ import { pathToFileURL } from "node:url";
3
+ import { applyOptionsChain, isProd } from "@modern-js/utils";
4
+ import { getCssSupport } from "../shared/getCssSupport.mjs";
5
+ const postcss_require = createRequire(import.meta.url);
6
+ const importPostcssPlugin = (name)=>Promise.resolve(postcss_require(name));
7
+ const userPostcssrcCache = new Map();
8
+ const clonePostCSSConfig = (config)=>({
9
+ ...config,
10
+ plugins: config.plugins ? [
11
+ ...config.plugins
12
+ ] : void 0
13
+ });
14
+ async function loadUserPostcssrc(root) {
15
+ const cached = userPostcssrcCache.get(root);
16
+ if (cached) return clonePostCSSConfig(await cached);
17
+ const compiledPath = postcss_require.resolve('../../compiled/postcss-load-config');
18
+ const { default: postcssrc } = await import(pathToFileURL(compiledPath).href);
19
+ const promise = postcssrc({}, root).catch((err)=>{
20
+ if (err.message?.includes('No PostCSS Config found')) return {};
21
+ throw err;
22
+ });
23
+ userPostcssrcCache.set(root, promise);
24
+ return promise.then((config)=>{
25
+ userPostcssrcCache.set(root, config);
26
+ return clonePostCSSConfig(config);
27
+ });
28
+ }
29
+ const pluginPostcss = (options = {})=>({
30
+ name: 'builder:postcss-plugins',
31
+ pre: [
32
+ 'builder:environment-defaults-plugin'
33
+ ],
34
+ setup (api) {
35
+ const { autoprefixer } = options;
36
+ api.modifyEnvironmentConfig(async (config, { mergeEnvironmentConfig })=>{
37
+ if ('web' !== config.output.target) return config;
38
+ const cssSupport = getCssSupport(config.output.overrideBrowserslist);
39
+ const enableExtractCSS = !config.output?.injectStyles;
40
+ const enableCssMinify = !enableExtractCSS && isProd();
41
+ const cssnanoOptions = {
42
+ preset: [
43
+ 'default',
44
+ {
45
+ mergeLonghand: false,
46
+ normalizeUrl: false
47
+ }
48
+ ]
49
+ };
50
+ const plugins = await Promise.all([
51
+ importPostcssPlugin('postcss-flexbugs-fixes'),
52
+ !cssSupport.customProperties && importPostcssPlugin('postcss-custom-properties'),
53
+ !cssSupport.initial && importPostcssPlugin('postcss-initial'),
54
+ !cssSupport.pageBreak && importPostcssPlugin('postcss-page-break'),
55
+ !cssSupport.fontVariant && importPostcssPlugin('postcss-font-variant'),
56
+ !cssSupport.mediaMinmax && importPostcssPlugin('postcss-media-minmax'),
57
+ importPostcssPlugin('postcss-nesting'),
58
+ enableCssMinify && importPostcssPlugin('cssnano').then((cssnano)=>cssnano(cssnanoOptions)),
59
+ importPostcssPlugin('autoprefixer').then((autoprefixerPlugin)=>autoprefixerPlugin(applyOptionsChain({
60
+ flexbox: 'no-2009',
61
+ overrideBrowserslist: config.output.overrideBrowserslist
62
+ }, autoprefixer)))
63
+ ]).then((results)=>results.filter(Boolean));
64
+ const userOptions = await loadUserPostcssrc(api.context.rootPath);
65
+ return mergeEnvironmentConfig({
66
+ tools: {
67
+ postcss: {
68
+ postcssOptions: {
69
+ ...userOptions,
70
+ plugins: [
71
+ ...userOptions.plugins || [],
72
+ ...plugins
73
+ ]
74
+ }
75
+ }
76
+ }
77
+ }, config);
78
+ });
79
+ }
80
+ });
81
+ export { pluginPostcss };
@@ -0,0 +1,20 @@
1
+ import { RUNTIME_CHUNK_NAME, RUNTIME_CHUNK_REGEX } from "../shared/utils.mjs";
2
+ const pluginRuntimeChunk = (disableInlineRuntimeChunk)=>({
3
+ name: 'builder:runtime-chunk',
4
+ setup (api) {
5
+ api.modifyBundlerChain(async (chain, { target, environment })=>{
6
+ if ('web' !== target) return;
7
+ const { config } = environment;
8
+ const { chunkSplit } = config.performance;
9
+ if (chunkSplit?.strategy !== 'all-in-one' && false !== config.splitChunks) chain.optimization.runtimeChunk({
10
+ name: RUNTIME_CHUNK_NAME
11
+ });
12
+ });
13
+ api.modifyRsbuildConfig((config)=>{
14
+ config.output ||= {};
15
+ if (disableInlineRuntimeChunk) return;
16
+ if (!config.output.inlineScripts) config.output.inlineScripts = RUNTIME_CHUNK_REGEX;
17
+ });
18
+ }
19
+ });
20
+ export { pluginRuntimeChunk };
@@ -0,0 +1,87 @@
1
+ import { logger } from "@modern-js/utils";
2
+ import { parse } from "@swc/core";
3
+ const rspackRscLayerName = "react-server";
4
+ const MODERN_RSC_INFO = 'modernRscInfo';
5
+ const sharedData = {
6
+ store: new Map(),
7
+ get (key) {
8
+ return this.store.get(key);
9
+ },
10
+ set (key, value) {
11
+ this.store.set(key, value);
12
+ },
13
+ clear () {
14
+ this.store.clear();
15
+ }
16
+ };
17
+ function setBuildInfo(mod, property) {
18
+ if (!mod.buildInfo) mod.buildInfo = {};
19
+ Object.assign(mod.buildInfo, property);
20
+ }
21
+ function setRscBuildInfo(mod, property) {
22
+ if (!mod.buildInfo) mod.buildInfo = {};
23
+ const rscBuildInfo = mod.buildInfo[MODERN_RSC_INFO] || {};
24
+ Object.assign(rscBuildInfo, property);
25
+ setBuildInfo(mod, {
26
+ [MODERN_RSC_INFO]: rscBuildInfo
27
+ });
28
+ }
29
+ function removeRscBuildInfo(mod) {
30
+ delete mod.buildInfo?.[MODERN_RSC_INFO];
31
+ }
32
+ function getRscBuildInfo(mod) {
33
+ return mod.buildInfo?.[MODERN_RSC_INFO];
34
+ }
35
+ function isCssModule(mod) {
36
+ if (!mod) return false;
37
+ return getRscBuildInfo(mod)?.isCssModule;
38
+ }
39
+ const parseSource = async (source)=>await parse(source, {
40
+ syntax: "typescript",
41
+ tsx: true,
42
+ dynamicImport: true
43
+ });
44
+ const getExportNames = async (ast, collectFuncOnly = false)=>{
45
+ const exportNames = [];
46
+ ast.body.forEach((node)=>{
47
+ if ('ExportNamedDeclaration' === node.type) {
48
+ const namedExport = node;
49
+ namedExport.specifiers.forEach((specifier)=>{
50
+ if ('ExportSpecifier' === specifier.type) exportNames.push(specifier.exported?.value || specifier.orig.value);
51
+ });
52
+ }
53
+ if ('ExportDeclaration' === node.type) {
54
+ if ('VariableDeclaration' === node.declaration.type) node.declaration.declarations.forEach((decl)=>{
55
+ if ('Identifier' === decl.id.type) if (collectFuncOnly) {
56
+ if (decl.init?.type === 'FunctionExpression' || decl.init?.type === 'ArrowFunctionExpression') exportNames.push(decl.id.value);
57
+ } else exportNames.push(decl.id.value);
58
+ });
59
+ if ('ClassDeclaration' === node.declaration.type || 'FunctionDeclaration' === node.declaration.type) {
60
+ if (node.declaration.identifier) exportNames.push(node.declaration.identifier.value);
61
+ }
62
+ }
63
+ if ('ExportDefaultExpression' === node.type || 'ExportDefaultDeclaration' === node.type) exportNames.push('default');
64
+ });
65
+ return exportNames;
66
+ };
67
+ const checkDirective = async (ast, directive)=>{
68
+ try {
69
+ for(let i = 0; i < ast.body.length; i++){
70
+ const node = ast.body[i];
71
+ if ('ExpressionStatement' !== node.type) break;
72
+ if ('StringLiteral' === node.expression.type && node.expression.value === directive) return true;
73
+ }
74
+ } catch (e) {
75
+ logger.error(e);
76
+ }
77
+ return false;
78
+ };
79
+ const isServerModule = async (ast)=>checkDirective(ast, 'use server');
80
+ const isClientModule = async (ast)=>checkDirective(ast, 'use client');
81
+ function findRootIssuer(modulegraph, module) {
82
+ const currentModule = module;
83
+ const issuer = modulegraph.getIssuer(currentModule);
84
+ if (!issuer) return currentModule;
85
+ return findRootIssuer(modulegraph, issuer);
86
+ }
87
+ export { MODERN_RSC_INFO, findRootIssuer, getExportNames, getRscBuildInfo, isClientModule, isCssModule, isServerModule, parseSource, removeRscBuildInfo, rspackRscLayerName, setBuildInfo, setRscBuildInfo, sharedData };
@@ -0,0 +1,124 @@
1
+ import node_path from "node:path";
2
+ import fs_extra from "@modern-js/utils/fs-extra";
3
+ import { logger } from "@rsbuild/core";
4
+ import { rspackRscLayerName } from "../common.mjs";
5
+ import { RspackRscClientPlugin } from "./rspack-rsc-client-plugin.mjs";
6
+ import { RscServerPlugin } from "./rspack-rsc-server-plugin.mjs";
7
+ const CSS_RULE_NAMES = [
8
+ 'less',
9
+ 'css',
10
+ 'scss',
11
+ 'sass'
12
+ ];
13
+ const createVirtualModule = (content)=>`data:text/javascript,${encodeURIComponent(content)}`;
14
+ const checkReactVersionAtLeast19 = async (appDir)=>{
15
+ const packageJsonPath = node_path.resolve(appDir, 'package.json');
16
+ const packageJson = await fs_extra.readJSON(packageJsonPath);
17
+ if (!packageJson.dependencies) return false;
18
+ const { dependencies } = packageJson;
19
+ const reactVersion = dependencies.react;
20
+ const reactDomVersion = dependencies['react-dom'];
21
+ if (!reactVersion || !reactDomVersion) return false;
22
+ const cleanVersion = (version)=>version.replace(/[\^~]/g, '');
23
+ const reactVersionParts = cleanVersion(reactVersion).split('.');
24
+ const reactDomVersionParts = cleanVersion(reactDomVersion).split('.');
25
+ const reactMajor = parseInt(reactVersionParts[0], 10);
26
+ const reactDomMajor = parseInt(reactDomVersionParts[0], 10);
27
+ if (Number.isNaN(reactMajor) || Number.isNaN(reactDomMajor)) return false;
28
+ return reactMajor >= 19 && reactDomMajor >= 19;
29
+ };
30
+ const rsbuildRscPlugin = ({ appDir, rscClientRuntimePath, rscServerRuntimePath, internalDirectory })=>({
31
+ name: 'builder:rsc-rsbuild-plugin',
32
+ setup (api) {
33
+ api.modifyBundlerChain({
34
+ handler: async (chain, { isServer, CHAIN_ID, isWebWorker })=>{
35
+ if (!await checkReactVersionAtLeast19(appDir)) {
36
+ logger.error('Enable react server component, please make sure the react and react-dom versions are greater than or equal to 19.0.0');
37
+ process.exit(1);
38
+ }
39
+ const entryPath2Name = new Map();
40
+ for (const [name, entry] of Object.entries(chain.entryPoints.entries()))entry.values().forEach((value)=>{
41
+ entryPath2Name.set(value, name);
42
+ });
43
+ const jsHandler = ()=>{
44
+ const originalJsRule = chain.module.rules.get(CHAIN_ID.RULE.JS);
45
+ if (!originalJsRule) throw new Error('Original JS rule not found when setup RSC plugin.');
46
+ const originalJsMainRule = originalJsRule.oneOfs.get(CHAIN_ID.ONE_OF.JS_MAIN);
47
+ if (!originalJsMainRule) throw new Error('Original JS main rule not found when setup RSC plugin.');
48
+ const useBabel = originalJsMainRule.uses.has(CHAIN_ID.USE.BABEL);
49
+ const jsLoader = useBabel ? CHAIN_ID.USE.BABEL : CHAIN_ID.USE.SWC;
50
+ const jsLoaderOptions = originalJsMainRule.use(jsLoader).get('options');
51
+ const jsLoaderPath = originalJsMainRule.use(jsLoader).get('loader');
52
+ originalJsRule.oneOfs.delete(CHAIN_ID.ONE_OF.JS_MAIN);
53
+ chain.module.rule(CHAIN_ID.RULE.JS).oneOf('rsc-server').issuerLayer(rspackRscLayerName).exclude.add(/universal[/\\]async_storage/).end().use('rsc-server-loader').loader(require.resolve('../rsc-server-loader')).options({
54
+ entryPath2Name,
55
+ appDir,
56
+ runtimePath: rscServerRuntimePath,
57
+ internalDirectory
58
+ }).end().use(jsLoader).loader(jsLoaderPath).options(jsLoaderOptions).end().end().oneOf('rsc-ssr').exclude.add(/universal[/\\]async_storage/).end().use('rsc-ssr-loader').loader(require.resolve('../rsc-ssr-loader')).options({
59
+ entryPath2Name,
60
+ internalDirectory
61
+ }).end().use(jsLoader).loader(jsLoaderPath).options(jsLoaderOptions).end().end();
62
+ };
63
+ const layerHandler = ()=>{
64
+ const routesFileReg = new RegExp(`${internalDirectory.replace(/[/\\]/g, '[/\\\\]')}[/\\\\][^/\\\\]*[/\\\\]routes`);
65
+ chain.module.rule('server-module').resource([
66
+ /render[/\\].*[/\\]server[/\\]rsc/,
67
+ /AppProxy/,
68
+ routesFileReg
69
+ ]).layer(rspackRscLayerName).end();
70
+ chain.module.rule(rspackRscLayerName).issuerLayer(rspackRscLayerName).resolve.conditionNames.add(rspackRscLayerName).add('...');
71
+ chain.module.rule('rsc-common').resource([
72
+ /universal[/\\]async_storage/
73
+ ]).layer('rsc-common');
74
+ };
75
+ const flightCssHandler = ()=>{
76
+ CSS_RULE_NAMES.forEach((ruleName)=>{
77
+ const rule = chain.module.rules.get(ruleName);
78
+ if (rule) chain.module.rule(ruleName).use('custom-loader').before('ignore-css').loader(require.resolve('../rsc-css-loader'));
79
+ });
80
+ };
81
+ const addServerRscPlugin = ()=>{
82
+ const ServerPlugin = RscServerPlugin;
83
+ chain.plugin('rsc-server-plugin').use(ServerPlugin, [
84
+ {
85
+ entryPath2Name
86
+ }
87
+ ]);
88
+ };
89
+ const addRscClientLoader = ()=>{
90
+ chain.module.rule('js').use('rsc-client-loader').loader(require.resolve('../rsc-client-loader')).before('babel').options({
91
+ callServerImport: rscClientRuntimePath,
92
+ registerImport: rscClientRuntimePath
93
+ }).end();
94
+ };
95
+ const addRscClientPlugin = ()=>{
96
+ const ClientPlugin = RspackRscClientPlugin;
97
+ chain.plugin('rsc-client-plugin').use(ClientPlugin);
98
+ };
99
+ if (isServer) {
100
+ chain.name('server');
101
+ layerHandler();
102
+ flightCssHandler();
103
+ jsHandler();
104
+ addServerRscPlugin();
105
+ } else if (!isWebWorker) {
106
+ chain.name('client');
107
+ chain.dependencies([
108
+ 'server'
109
+ ]);
110
+ const entries = chain.entryPoints.entries();
111
+ for (const entryName of Object.keys(entries)){
112
+ const entryPoint = chain.entry(entryName);
113
+ const code = `window.__MODERN_JS_ENTRY_NAME="${entryName}";`;
114
+ entryPoint.add(createVirtualModule(code));
115
+ }
116
+ addRscClientLoader();
117
+ addRscClientPlugin();
118
+ }
119
+ },
120
+ order: 'post'
121
+ });
122
+ }
123
+ });
124
+ export { rsbuildRscPlugin };