From 388353dc5af4accfa35eadc5d6323da1da5a66e9 Mon Sep 17 00:00:00 2001 From: chenos Date: Thu, 7 Nov 2024 10:06:41 +0800 Subject: [PATCH] chore: use rspack as the client-side build tool for the plugin (#5575) * fix: change plugin build tool to rspack * fix: bug * fix: bug * fix: bug * feat: upgrade rspack * feat: upgrade rspack * fix: rspack config * fix: some plugins can't be loaded * chore: update rspack * chore: revert rspack version due to tests failing * chore: update rspack to 1.0.14 * fix: missing tsconfig in rspack config * fix: pro plugins build error * fix: incorrect dependecies docs link --------- Co-authored-by: dream2023 <1098626505@qq.com> Co-authored-by: gchust --- packages/core/build/package.json | 9 + packages/core/build/src/buildPlugin.ts | 274 ++++++++++++++---- .../core/build/src/utils/buildPluginUtils.ts | 2 +- .../src/application/utils/globalDeps.ts | 2 + 4 files changed, 231 insertions(+), 56 deletions(-) diff --git a/packages/core/build/package.json b/packages/core/build/package.json index 5403ec425c..ad860045fa 100644 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -9,6 +9,15 @@ }, "typings": "./index.d.ts", "dependencies": { + "@svgr/webpack": "^8.1.0", + "@rspack/core": "1.0.14", + "css-loader": "^6.8.1", + "less": "^4.1.3", + "postcss": "^8.4.29", + "less-loader": "11.1.0", + "style-loader": "^3.3.3", + "postcss-loader": "^7.3.3", + "postcss-preset-env": "^9.1.2", "@babel/core": "7.25.2", "@babel/plugin-transform-modules-amd": "7.24.7", "@babel/preset-env": "7.25.4", diff --git a/packages/core/build/src/buildPlugin.ts b/packages/core/build/src/buildPlugin.ts index 20b01bcd46..0d0bdc3ae7 100644 --- a/packages/core/build/src/buildPlugin.ts +++ b/packages/core/build/src/buildPlugin.ts @@ -16,9 +16,10 @@ import path from 'path'; import { build as tsupBuild } from 'tsup'; import { build as viteBuild } from 'vite'; import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; +import { rspack } from '@rspack/core'; import { EsbuildSupportExts, globExcludeFiles } from './constant'; -import { PkgLog, UserConfig, getEnvDefine, getPackageJson } from './utils'; +import { PkgLog, UserConfig, getPackageJson } from './utils'; import { buildCheck, checkFileSize, @@ -130,6 +131,7 @@ const external = [ 'ahooks', 'lodash', 'china-division', + 'file-saver', ]; const pluginPrefix = ( process.env.PLUGIN_PACKAGE_PREFIX || '@nocobase/plugin-,@nocobase/preset-,@nocobase/plugin-pro-' @@ -158,7 +160,9 @@ export function deleteServerFiles(cwd: string, log: PkgLog) { export function writeExternalPackageVersion(cwd: string, log: PkgLog) { log('write external version'); - const sourceFiles = fg.globSync(sourceGlobalFiles, { cwd, absolute: true }).map((item) => fs.readFileSync(item, 'utf-8')); + const sourceFiles = fg + .globSync(sourceGlobalFiles, { cwd, absolute: true }) + .map((item) => fs.readFileSync(item, 'utf-8')); const sourcePackages = getSourcePackages(sourceFiles); const excludePackages = getExcludePackages(sourcePackages, external, pluginPrefix); const data = excludePackages.reduce>((prev, packageName) => { @@ -174,7 +178,9 @@ export function writeExternalPackageVersion(cwd: string, log: PkgLog) { export async function buildServerDeps(cwd: string, serverFiles: string[], log: PkgLog) { log('build plugin server dependencies'); const outDir = path.join(cwd, target_dir, 'node_modules'); - const serverFileSource = serverFiles.filter(item => validExts.includes(path.extname(item))).map((item) => fs.readFileSync(item, 'utf-8')); + const serverFileSource = serverFiles + .filter((item) => validExts.includes(path.extname(item))) + .map((item) => fs.readFileSync(item, 'utf-8')); const sourcePackages = getSourcePackages(serverFileSource); const includePackages = getIncludePackages(sourcePackages, external, pluginPrefix); const excludePackages = getExcludePackages(sourcePackages, external, pluginPrefix); @@ -190,7 +196,9 @@ export async function buildServerDeps(cwd: string, serverFiles: string[], log: P if (excludePackages.length) { tips.push(`These packages ${chalk.yellow(excludePackages.join(', '))} will be ${chalk.italic('exclude')}.`); } - tips.push(`For more information, please refer to: ${chalk.blue('https://docs.nocobase.com/development/deps')}.`); + tips.push( + `For more information, please refer to: ${chalk.blue('https://docs.nocobase.com/development/others/deps')}.`, + ); log(tips.join(' ')); if (!includePackages.length) return; @@ -268,30 +276,34 @@ export async function buildPluginServer(cwd: string, userConfig: UserConfig, sou const packageJson = getPackageJson(cwd); const serverFiles = fg.globSync(serverGlobalFiles, { cwd, absolute: true }); buildCheck({ cwd, packageJson, entry: 'server', files: serverFiles, log }); - const otherExts = Array.from(new Set(serverFiles.map((item) => path.extname(item)).filter((item) => !EsbuildSupportExts.includes(item)))); + const otherExts = Array.from( + new Set(serverFiles.map((item) => path.extname(item)).filter((item) => !EsbuildSupportExts.includes(item))), + ); if (otherExts.length) { log('%s will not be processed, only be copied to the dist directory.', chalk.yellow(otherExts.join(','))); } deleteServerFiles(cwd, log); - await tsupBuild(userConfig.modifyTsupConfig({ - entry: serverFiles, - splitting: false, - clean: false, - bundle: false, - silent: true, - treeshake: false, - target: 'node16', - sourcemap, - outDir: path.join(cwd, target_dir), - format: 'cjs', - skipNodeModulesBundle: true, - loader: { - ...otherExts.reduce((prev, cur) => ({ ...prev, [cur]: 'copy' }), {}), - '.json': 'copy', - }, - })); + await tsupBuild( + userConfig.modifyTsupConfig({ + entry: serverFiles, + splitting: false, + clean: false, + bundle: false, + silent: true, + treeshake: false, + target: 'node16', + sourcemap, + outDir: path.join(cwd, target_dir), + format: 'cjs', + skipNodeModulesBundle: true, + loader: { + ...otherExts.reduce((prev, cur) => ({ ...prev, [cur]: 'copy' }), {}), + '.json': 'copy', + }, + }), + ); await buildServerDeps(cwd, serverFiles, log); } @@ -316,46 +328,198 @@ export async function buildPluginClient(cwd: string, userConfig: UserConfig, sou return prev; }, {}); - const entry = fg.globSync('src/client/index.{ts,tsx,js,jsx}', { absolute: true, cwd }); + const entry = fg.globSync('index.{ts,tsx,js,jsx}', { absolute: false, cwd: path.join(cwd, 'src/client') }); const outputFileName = 'index.js'; - - await viteBuild(userConfig.modifyViteConfig({ - mode: process.env.NODE_ENV || 'production', - define: getEnvDefine(), - logLevel: 'warn', - build: { - minify: process.env.NODE_ENV === 'production', - outDir, - cssCodeSplit: false, - emptyOutDir: true, - sourcemap, - lib: { - entry, - formats: ['umd'], + const compiler = rspack({ + mode: 'production', + // mode: "development", + context: cwd, + entry: './src/client/' + entry[0], + target: ['web', 'es5'], + output: { + path: outDir, + filename: outputFileName, + publicPath: `/static/plugins/${packageJson.name}/dist/client/`, + clean: true, + library: { name: packageJson.name, - fileName: () => outputFileName, - }, - target: ['es2015', 'edge88', 'firefox78', 'chrome87', 'safari14'], - rollupOptions: { - cache: true, - external: [...Object.keys(globals), 'react', 'react/jsx-runtime'], - output: { - exports: 'named', - globals: { - react: 'React', - 'react/jsx-runtime': 'jsxRuntime', - ...globals, - }, - }, + type: 'umd', + umdNamedDefine: true, }, }, + resolve: { + tsConfig: path.join(process.cwd(), 'tsconfig.json'), + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.less', '.css'], + }, + module: { + rules: [ + { + test: /.less$/, + use: [ + { loader: 'style-loader' }, + { loader: 'css-loader' }, + { loader: 'less-loader' }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: { + 'postcss-preset-env': { + browsers: ['last 2 versions', '> 1%', 'cover 99.5%', 'not dead'], + }, + autoprefixer: {}, + }, + }, + }, + }, + ], + type: 'javascript/auto', + }, + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: { + 'postcss-preset-env': { + browsers: ['last 2 versions', '> 1%', 'cover 99.5%', 'not dead'], + }, + autoprefixer: {}, + }, + }, + }, + }, + ], + type: 'javascript/auto', + }, + { + test: /\.(png|jpe?g|gif)$/i, + type: 'asset', + }, + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + use: ['@svgr/webpack'], + }, + { + test: /\.jsx$/, + exclude: /[\\/]node_modules[\\/]/, + loader: 'builtin:swc-loader', + options: { + sourceMap: true, + jsc: { + parser: { + syntax: 'ecmascript', + jsx: true, + }, + target: 'es5', + }, + }, + }, + { + test: /\.tsx$/, + exclude: /[\\/]node_modules[\\/]/, + loader: 'builtin:swc-loader', + options: { + sourceMap: true, + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + }, + target: 'es5', + }, + }, + }, + { + test: /\.ts$/, + exclude: /[\\/]node_modules[\\/]/, + loader: 'builtin:swc-loader', + options: { + sourceMap: true, + jsc: { + parser: { + syntax: 'typescript', + }, + target: 'es5', + }, + }, + }, + ], + }, plugins: [ - react(), - cssInjectedByJsPlugin({ styleId: packageJson.name }), + new rspack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + }), ], - })); + node: { + global: true, + }, + externals: { + react: 'React', + // 'react/jsx-runtime': 'jsxRuntime', + ...globals, + }, + stats: 'errors-warnings', + }); - checkFileSize(outDir, log); + return new Promise((resolve, reject) => { + compiler.run((err, stats) => { + if (err) { + reject(err); + return; + } + console.log( + stats.toString({ + colors: true, + }), + ); + resolve(null); + }); + }); + // await viteBuild(userConfig.modifyViteConfig({ + // mode: 'production', + // define: { + // 'process.env.NODE_ENV': JSON.stringify('production'), + // }, + // logLevel: 'warn', + // build: { + // minify: true, + // outDir, + // cssCodeSplit: false, + // emptyOutDir: true, + // sourcemap, + // lib: { + // entry, + // formats: ['umd'], + // name: packageJson.name, + // fileName: () => outputFileName, + // }, + // target: ['es2015', 'edge88', 'firefox78', 'chrome87', 'safari14'], + // rollupOptions: { + // cache: true, + // external: [...Object.keys(globals), 'react', 'react/jsx-runtime'], + // output: { + // exports: 'named', + // globals: { + // react: 'React', + // 'react/jsx-runtime': 'jsxRuntime', + // ...globals, + // }, + // }, + // }, + // }, + // plugins: [ + // react(), + // cssInjectedByJsPlugin({ styleId: packageJson.name }), + // ], + // })); + + // checkFileSize(outDir, log); } export async function buildPlugin(cwd: string, userConfig: UserConfig, sourcemap: boolean, log: PkgLog) { diff --git a/packages/core/build/src/utils/buildPluginUtils.ts b/packages/core/build/src/utils/buildPluginUtils.ts index 7c71418d9c..e36be7eb5c 100644 --- a/packages/core/build/src/utils/buildPluginUtils.ts +++ b/packages/core/build/src/utils/buildPluginUtils.ts @@ -112,7 +112,7 @@ export function checkDependencies(packageJson: Record, log: Log) { chalk.yellow(packages.join(', ')), chalk.yellow('dependencies'), chalk.yellow('devDependencies'), - chalk.blue(chalk.blue('https://docs.nocobase.com/development/deps')), + chalk.blue(chalk.blue('https://docs.nocobase.com/development/others/deps')), ); } diff --git a/packages/core/client/src/application/utils/globalDeps.ts b/packages/core/client/src/application/utils/globalDeps.ts index 1f1e0f1bd6..e427e0ed38 100644 --- a/packages/core/client/src/application/utils/globalDeps.ts +++ b/packages/core/client/src/application/utils/globalDeps.ts @@ -41,6 +41,7 @@ import * as ReactRouter from 'react-router'; import * as ReactRouterDom from 'react-router-dom'; import jsxRuntime from 'react/jsx-runtime'; import * as nocobaseClient from '../../index'; +import * as FileSaver from 'file-saver'; import type { RequireJS } from './requirejs'; @@ -101,4 +102,5 @@ export function defineGlobalDeps(requirejs: RequireJS) { requirejs.define('ahooks', () => ahooks); requirejs.define('@emotion/css', () => emotionCss); requirejs.define('dayjs', () => dayjs); + requirejs.define('file-saver', () => FileSaver); }