From 9e5e96b9e4fabe048ae2740d2f125e2c67c3d7ce Mon Sep 17 00:00:00 2001 From: jack zhang <1098626505@qq.com> Date: Fri, 15 Sep 2023 08:51:20 +0800 Subject: [PATCH] fix: improve build (#2643) * fix: client lib require wrapper * fix: bug * fix: add tsconfig.paths.json * fix: collection dir not exists * fix: improve... * fix: update yarn.lock * fix: db.sync * fix: bugs * fix: bugs * fix: bugs * fix: bugs && allow user custom build config * docs: user custom config docs * refactor: custom user build config * fix: bugs * fix: build plugin-client bug --------- Co-authored-by: chenos --- .gitignore | 1 + docs/en-US/development/plugin-ds.md | 28 ++++++ docs/zh-CN/development/plugin-ds.md | 28 ++++++ jest.config.js | 2 +- package.json | 2 +- packages/core/build/package.json | 4 +- packages/core/build/src/build.ts | 23 ++++- packages/core/build/src/buildCjs.ts | 9 +- packages/core/build/src/buildClient.ts | 89 ++++++++++++++----- packages/core/build/src/buildPlugin.ts | 79 +++------------- packages/core/build/src/index.d.ts | 14 +++ packages/core/build/src/index.ts | 1 + packages/core/build/src/utils/utils.ts | 35 ++++++++ packages/core/build/tsup.config.ts | 4 + packages/core/cli/bin/index.js | 3 + packages/core/cli/src/commands/build.js | 1 + packages/core/cli/src/commands/clean.js | 1 + packages/core/cli/src/commands/test.js | 2 +- packages/core/cli/src/index.js | 5 ++ packages/core/cli/src/plugin-generator.js | 13 +-- packages/core/cli/src/util.js | 34 ++++++- .../templates/app/jest.config.js | 2 +- .../templates/app/package.json.tpl | 3 + .../core/database/src/collection-importer.ts | 4 + packages/core/devtools/umiConfig.js | 2 +- .../plugins/@nocobase/plugin-acl/package.json | 2 +- .../plugin-china-region/build.config.ts | 23 +++++ .../@nocobase/plugin-china-region/build.js | 21 ----- .../@nocobase/plugin-client/build.config.ts | 42 +++++++++ .../plugins/@nocobase/plugin-client/build.js | 41 --------- tsconfig.json | 13 +-- vitest.config.ts | 23 +++-- yarn.lock | 38 ++++---- 33 files changed, 376 insertions(+), 216 deletions(-) create mode 100644 packages/core/build/src/index.d.ts create mode 100644 packages/plugins/@nocobase/plugin-china-region/build.config.ts delete mode 100644 packages/plugins/@nocobase/plugin-china-region/build.js create mode 100644 packages/plugins/@nocobase/plugin-client/build.config.ts delete mode 100644 packages/plugins/@nocobase/plugin-client/build.js diff --git a/.gitignore b/.gitignore index 8f543e584..49af298ae 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ storage/plugins storage/tar storage/tmp storage/app.watch.ts +tsconfig.paths.json diff --git a/docs/en-US/development/plugin-ds.md b/docs/en-US/development/plugin-ds.md index 798996069..6c05646a9 100644 --- a/docs/en-US/development/plugin-ds.md +++ b/docs/en-US/development/plugin-ds.md @@ -12,6 +12,34 @@ An empty plugin can be created quickly with `yarn pm create my-plugin`, with the |- package.json # plugin package information |- server.d.ts |- server.js + |- build.config.ts # or `build.config.js`, modify configuration ``` The tutorial for `/src/server` refers to the [server](./server) section, and the tutorial for `/src/client` refers to the [client](./client) section. + +If you want to customize the packaging configuration, you can create a `config.js` file in the root directory, with the following content: + +```js +import { defineConfig } from '@nocobase/build'; + +export default defineConfig({ + modifyViteConfig: (config) => { + // vite is used to package the `src/client` side code + + // Modify the Vite configuration, for more information, please refer to: https://vitejs.dev/guide/ + return config + }, + modifyTsupConfig: (config) => { + // tsup is used to package the `src/server` side code + + // Modify the tsup configuration, for more information, please refer to: https://tsup.egoist.dev/#using-custom-configuration + return config + }, + beforeBuild: (log) => { + // The callback function before the build starts, you can do some operations before the build starts + }, + afterBuild: (log: PkgLog) => { + // The callback function after the build is completed, you can do some operations after the build is completed + }; +}) +``` diff --git a/docs/zh-CN/development/plugin-ds.md b/docs/zh-CN/development/plugin-ds.md index b1dc993b5..a91e82fc7 100644 --- a/docs/zh-CN/development/plugin-ds.md +++ b/docs/zh-CN/development/plugin-ds.md @@ -12,6 +12,34 @@ |- package.json # 插件包信息 |- server.d.ts |- server.js + |- build.config.ts # 或者 `build.config.js` ,用于修改打包配置,实现自定义逻辑 ``` `/src/server` 的教程参考 [服务端](./server) 章节,`/src/client` 的教程参考 [客户端](./client) 章节。 + +如果你想要自定义打包配置,可以在根目录下创建 `config.js` 文件,内容如下: + +```js +import { defineConfig } from '@nocobase/build'; + +export default defineConfig({ + modifyViteConfig: (config) => { + // vite 是用来打包 `src/client` 端代码的 + + // 修改 Vite 配置,具体可参考:https://vitejs.dev/guide/ + return config + }, + modifyTsupConfig: (config) => { + // tsup 是用来打包 `src/server` 端代码的 + + // 修改 tsup 配置,具体可参考:https://tsup.egoist.dev/#using-custom-configuration + return config + }, + beforeBuild: (log) => { + // 构建开始前的回调函数,可以在构建开始前做一些操作 + }, + afterBuild: (log: PkgLog) => { + // 构建完成后的回调函数,可以在构建完成后做一些操作 + }; +}); +``` diff --git a/jest.config.js b/jest.config.js index c22ea87d2..71c4f2140 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ const { pathsToModuleNameMapper } = require('ts-jest'); -const { compilerOptions } = require('./tsconfig.json'); +const { compilerOptions } = require('./tsconfig.paths.json'); const { defaults } = require('jest-config'); module.exports = { diff --git a/package.json b/package.json index e578592a8..a9ebb4033 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "react-dom": "^18.0.0", "ts-jest": "^29.1.1", "typescript": "5.1.3", - "vite": "^4.4.1", + "vite": "^4.4.9", "vitest": "^0.34.3" }, "volta": { diff --git a/packages/core/build/package.json b/packages/core/build/package.json index 2d861c109..53104cb0a 100755 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -3,6 +3,7 @@ "version": "0.14.0-alpha.3", "description": "Library build tool based on rollup.", "main": "lib/index.js", + "types": "./lib/index.d.ts", "bin": { "nocobase-build": "./bin/nocobase-build.js" }, @@ -28,7 +29,8 @@ "update-notifier": "3.0.0", "vite-plugin-css-injected-by-js": "^3.2.1", "vite-plugin-lib-inject-css": "1.2.0", - "yargs-parser": "13.1.2" + "yargs-parser": "13.1.2", + "esbuild-register": "^3.4.2" }, "license": "Apache-2.0", "scripts": { diff --git a/packages/core/build/src/build.ts b/packages/core/build/src/build.ts index 18c9463a7..142c37820 100755 --- a/packages/core/build/src/build.ts +++ b/packages/core/build/src/build.ts @@ -14,7 +14,7 @@ import { buildClient } from './buildClient'; import { buildCjs } from './buildCjs'; import { buildPlugin } from './buildPlugin'; import { buildDeclaration } from './buildDeclaration'; -import { PkgLog, getPkgLog, toUnixPath, getPackageJson } from './utils'; +import { PkgLog, getPkgLog, toUnixPath, getPackageJson, getUserConfig, UserConfig } from './utils'; import { getPackages } from './utils/getPackages'; import { Package } from '@lerna/package'; import { tarPlugin } from './tarPlugin' @@ -23,6 +23,11 @@ export async function build(pkgs: string[]) { process.env.NODE_ENV = 'production'; const packages = getPackages(pkgs); + if (packages.length === 0) { + console.error(chalk.red(`[@nocobase/build]: '${pkgs.join(', ')}' not match any packages.`)); + return; + } + const pluginPackages = getPluginPackages(packages); const cjsPackages = getCjsPackages(packages); const presetsPackages = getPresetsPackages(packages); @@ -52,7 +57,7 @@ export async function build(pkgs: string[]) { export async function buildPackages( packages: Package[], targetDir: string, - doBuildPackage: (cwd: string, sourcemap: boolean, log?: PkgLog) => Promise, + doBuildPackage: (cwd: string, userConfig: UserConfig, sourcemap: boolean, log?: PkgLog) => Promise, ) { for await (const pkg of packages) { await buildPackage(pkg, targetDir, doBuildPackage); @@ -62,7 +67,7 @@ export async function buildPackages( export async function buildPackage( pkg: Package, targetDir: string, - doBuildPackage: (cwd: string, sourcemap: boolean, log?: PkgLog) => Promise, + doBuildPackage: (cwd: string, userConfig: UserConfig, sourcemap: boolean, log?: PkgLog) => Promise, ) { const sourcemap = process.argv.includes('--sourcemap'); const noDeclaration = process.argv.includes('--no-dts'); @@ -79,15 +84,20 @@ export async function buildPackage( log(`${chalk.bold(toUnixPath(pkg.location.replace(PACKAGES_PATH, '').slice(1)))} build start`); + const userConfig = getUserConfig(pkg.location); // prebuild if (packageJson?.scripts?.prebuild) { log('prebuild'); await runScript(['prebuild'], pkg.location); await packageJson.prebuild(pkg.location); } + if (userConfig.beforeBuild) { + log('beforeBuild'); + await userConfig.beforeBuild(log); + } // build source - await doBuildPackage(pkg.location, sourcemap, log); + await doBuildPackage(pkg.location, userConfig, sourcemap, log); // build declaration if (!noDeclaration) { @@ -101,6 +111,11 @@ export async function buildPackage( await runScript(['postbuild'], pkg.location); } + if (userConfig.afterBuild) { + log('afterBuild'); + await userConfig.afterBuild(log); + } + // tar if (hasTar) { await tarPlugin(pkg.location, log); diff --git a/packages/core/build/src/buildCjs.ts b/packages/core/build/src/buildCjs.ts index 5b115250e..5397d43c6 100644 --- a/packages/core/build/src/buildCjs.ts +++ b/packages/core/build/src/buildCjs.ts @@ -3,9 +3,9 @@ import fg from 'fast-glob'; import path from 'path'; import chalk from 'chalk'; import { globExcludeFiles, EsbuildSupportExts } from './constant'; -import { PkgLog } from './utils'; +import { PkgLog, UserConfig } from './utils'; -export function buildCjs(cwd: string, sourcemap: boolean = false, log: PkgLog) { +export function buildCjs(cwd: string, userConfig: UserConfig, sourcemap: boolean = false, log: PkgLog) { log('build cjs'); const entry = fg.globSync(['src/**', ...globExcludeFiles], { cwd, absolute: true }); @@ -14,8 +14,7 @@ export function buildCjs(cwd: string, sourcemap: boolean = false, log: PkgLog) { if (otherExts.length) { log('%s will not be processed, only be copied to the lib directory.', chalk.yellow(otherExts.join(','))); } - - return build({ + return build(userConfig.modifyTsupConfig({ entry, splitting: false, clean: true, @@ -32,5 +31,5 @@ export function buildCjs(cwd: string, sourcemap: boolean = false, log: PkgLog) { }, format: 'cjs', skipNodeModulesBundle: true, - }); + })); } diff --git a/packages/core/build/src/buildClient.ts b/packages/core/build/src/buildClient.ts index fa2474208..11d5ccd96 100644 --- a/packages/core/build/src/buildClient.ts +++ b/packages/core/build/src/buildClient.ts @@ -1,26 +1,37 @@ import path from 'path'; import fg from 'fast-glob'; +import fs from 'fs-extra'; import { build as viteBuild } from 'vite'; import { build as tsupBuild } from 'tsup'; import { libInjectCss } from 'vite-plugin-lib-inject-css'; import react from '@vitejs/plugin-react'; -import { PkgLog } from './utils'; + +import { PkgLog, UserConfig } from './utils'; import { globExcludeFiles } from './constant'; -export async function buildClient(cwd: string, sourcemap: boolean = false, log: PkgLog) { + +export async function buildClient(cwd: string, userConfig: UserConfig, sourcemap: boolean = false, log: PkgLog) { log('build client'); - - await Promise.all([buildLib(cwd, sourcemap, 'cjs'), buildLib(cwd, sourcemap, 'es')]); - await buildLocale(cwd); -} - -export function buildLib(cwd: string, sourcemap: boolean, format: 'cjs' | 'es') { - const outDir = path.resolve(cwd, format === 'cjs' ? 'lib' : 'es'); - const entry = path.join(cwd, 'src/index.ts').replaceAll(/\\/g, '/'); const cwdWin = cwd.replaceAll(/\\/g, '/'); const cwdUnix = cwd.replaceAll(/\//g, '\\'); + const external = function (id: string) { + if (id.startsWith('.') || id.startsWith(cwdUnix) || id.startsWith(cwdWin)) { + return false; + } + return true; + } + await buildEsm(cwd, userConfig, sourcemap, external, log); + await buildLib(cwd, userConfig, sourcemap, external, log); + await buildLocale(cwd, userConfig, log); +} - return viteBuild({ +type External = (id: string) => boolean; + +export function buildEsm(cwd: string, userConfig: UserConfig, sourcemap: boolean, external: External, log: PkgLog) { + log('build client esm'); + const entry = path.join(cwd, 'src/index.ts').replaceAll(/\\/g, '/'); + const outDir = path.resolve(cwd, 'es'); + return viteBuild(userConfig.modifyViteConfig({ mode: 'production', define: { 'process.env.NODE_ENV': JSON.stringify('production'), @@ -33,29 +44,63 @@ export function buildLib(cwd: string, sourcemap: boolean, format: 'cjs' | 'es') sourcemap, lib: { entry, - formats: [format], + formats: ['es'], fileName: 'index', }, target: ['es2015', 'edge88', 'firefox78', 'chrome87', 'safari14'], rollupOptions: { cache: true, treeshake: true, - external(id) { - if (id.startsWith('.') || id.startsWith(cwdUnix) || id.startsWith(cwdWin)) { - return false; - } - return true; - }, + external, }, }, plugins: [react(), libInjectCss()], + })); +} + +export async function buildLib(cwd: string, userConfig: UserConfig, sourcemap: boolean, external: External, log: PkgLog) { + log('build client lib'); + const outDir = path.resolve(cwd, 'lib'); + const esDir = path.resolve(cwd, 'es'); + const entry = path.join(esDir, 'index.ts') + + fs.removeSync(entry); + fs.linkSync(path.join(cwd, 'es/index.mjs'), entry); + + await viteBuild(userConfig.modifyViteConfig({ + mode: 'production', + esbuild: { + format: 'cjs' + }, + build: { + outDir, + minify: false, + sourcemap, + lib: { + entry: path.join(cwd, 'es/index.ts'), + formats: ['cjs'], + fileName: 'index', + }, + rollupOptions: { + external, + }, + }, + })); + + fs.removeSync(entry); + + const css = fg.sync('*.css', { cwd: esDir, absolute: true }); + css.forEach((file) => { + fs.copySync(file, path.join(outDir, path.basename(file))); }); } -export function buildLocale(cwd: string) { +export function buildLocale(cwd: string, userConfig: UserConfig, log: PkgLog) { + log('build client locale'); + const entry = fg.globSync(['src/locale/**', ...globExcludeFiles], { cwd, absolute: true }); const outDir = path.resolve(cwd, 'lib', 'locale'); - return tsupBuild({ + return tsupBuild(userConfig.modifyTsupConfig({ entry, splitting: false, clean: false, @@ -67,5 +112,5 @@ export function buildLocale(cwd: string) { outDir, format: 'cjs', skipNodeModulesBundle: true, - }); -} \ No newline at end of file + })); +} diff --git a/packages/core/build/src/buildPlugin.ts b/packages/core/build/src/buildPlugin.ts index a5888b902..8e83313a3 100644 --- a/packages/core/build/src/buildPlugin.ts +++ b/packages/core/build/src/buildPlugin.ts @@ -7,7 +7,6 @@ import { build as tsupBuild } from 'tsup'; import { build as viteBuild } from 'vite'; import fg from 'fast-glob'; import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; -import { transformAsync } from '@babel/core'; import { buildCheck, @@ -20,7 +19,7 @@ import { } from './utils/buildPluginUtils'; import { getDepPkgPath, getDepsConfig } from './utils/getDepsConfig'; import { EsbuildSupportExts, globExcludeFiles } from './constant'; -import { PkgLog, getPackageJson } from './utils'; +import { PkgLog, UserConfig, getPackageJson } from './utils'; const serverGlobalFiles: string[] = ['src/**', '!src/client/**', ...globExcludeFiles]; const clientGlobalFiles: string[] = ['src/**', '!src/server/**', ...globExcludeFiles]; @@ -247,7 +246,7 @@ export async function buildServerDeps(cwd: string, serverFiles: string[], log: P } } -export async function buildPluginServer(cwd: string, sourcemap: boolean, log: PkgLog) { +export async function buildPluginServer(cwd: string, userConfig: UserConfig, sourcemap: boolean, log: PkgLog) { log('build plugin server source'); const packageJson = getPackageJson(cwd); const serverFiles = fg.globSync(serverGlobalFiles, { cwd, absolute: true }); @@ -257,7 +256,7 @@ export async function buildPluginServer(cwd: string, sourcemap: boolean, log: Pk log('%s will not be processed, only be copied to the dist directory.', chalk.yellow(otherExts.join(','))); } - await tsupBuild({ + await tsupBuild(userConfig.modifyTsupConfig({ entry: serverFiles, splitting: false, clean: false, @@ -273,50 +272,12 @@ export async function buildPluginServer(cwd: string, sourcemap: boolean, log: Pk ...otherExts.reduce((prev, cur) => ({ ...prev, [cur]: 'copy' }), {}), '.json': 'copy', }, - }); + })); await buildServerDeps(cwd, serverFiles, log); } -export function transformClientFilesToAmd(outDir: string, outputFileName: string, packageName: string, log: PkgLog) { - const files = fs.readdirSync(outDir); - - return Promise.all(files.map(async file => { - const filePath = path.join(outDir, file); - - // 只编译 JavaScript 文件 - if (path.extname(filePath) === '.mjs' || path.extname(filePath) === '.js') { - let fileContent = fs.readFileSync(filePath, 'utf-8'); - - // import('./dayjs.mjs') => import(window.staticBaseUrl + '/@nocobase/plugin-acl/dayjs.mjs?noExt') - if (file === outputFileName) { - fileContent = fileContent.replace(dynamicImportRegexp, (match, _1, dynamicImportPath) => { - let absolutePath = path.join(outDir, dynamicImportPath).replace(outDir, ''); - if (absolutePath.startsWith(path.sep)) { - absolutePath = absolutePath.slice(1); - } - return `import(window.staticBaseUrl + '/${packageName}/${absolutePath}?noExt')`; - }) - } - - const { code } = await transformAsync(fileContent, { - presets: [['@babel/preset-env', { - modules: 'amd', - targets: { - 'edge': 88, - 'firefox': 78, - 'chrome': 87, - 'safari': 14, - } - }]], - plugins: ['@babel/plugin-transform-modules-amd'], - }); - fs.writeFileSync(filePath, code, 'utf-8'); - } - })); -} - -export async function buildPluginClient(cwd: string, sourcemap: boolean, log: PkgLog) { +export async function buildPluginClient(cwd: string, userConfig: UserConfig, sourcemap: boolean, log: PkgLog) { log('build plugin client'); const packageJson = getPackageJson(cwd); const clientFiles = fg.globSync(clientGlobalFiles, { cwd, absolute: true }); @@ -326,10 +287,6 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk checkRequire(clientFiles, log); buildCheck({ cwd, packageJson, entry: 'client', files: clientFiles, log }); - const hasDynamicImport = false; - // const hasDynamicImport = clientFileSource.some((source) => { - // return source.match(dynamicImportRegexp); - // }); const outDir = path.join(cwd, target_dir, 'client'); const globals = excludePackages.reduce>((prev, curr) => { @@ -342,7 +299,8 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk const entry = fg.globSync('src/client/index.{ts,tsx,js,jsx}', { absolute: true, cwd }); const outputFileName = 'index.js'; - await viteBuild({ + + await viteBuild(userConfig.modifyViteConfig({ mode: 'production', define: { 'process.env.NODE_ENV': JSON.stringify('production'), @@ -356,7 +314,7 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk sourcemap, lib: { entry, - formats: [hasDynamicImport ? 'es' : 'umd'], + formats: ['umd'], name: packageJson.name, fileName: () => outputFileName, }, @@ -378,26 +336,13 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk react(), cssInjectedByJsPlugin({ styleId: packageJson.name }), ], - }); + })); checkFileSize(outDir, log); - - // if (hasDynamicImport) { - // await transformClientFilesToAmd(outDir, outputFileName, packageJson.name, log); - // } } -export async function buildPlugin(cwd: string, sourcemap: boolean, log: PkgLog) { - await buildPluginClient(cwd, sourcemap, log); - await buildPluginServer(cwd, sourcemap, log); - const buildFile = path.join(cwd, 'build.js'); - if (fs.existsSync(buildFile)) { - log('build others'); - try { - await require(buildFile).run(log); - } catch (error) { - console.error(error); - } - } +export async function buildPlugin(cwd: string, userConfig: UserConfig, sourcemap: boolean, log: PkgLog) { + await buildPluginClient(cwd, userConfig, sourcemap, log); + await buildPluginServer(cwd, userConfig, sourcemap, log); writeExternalPackageVersion(cwd, log); } diff --git a/packages/core/build/src/index.d.ts b/packages/core/build/src/index.d.ts new file mode 100644 index 000000000..a7cf4e15f --- /dev/null +++ b/packages/core/build/src/index.d.ts @@ -0,0 +1,14 @@ + +import { Options as TsupConfig } from 'tsup' +import { InlineConfig as ViteConfig } from 'vite' + +export type PkgLog = (msg: string, ...args: any[]) => void; + +interface UserConfig { + modifyTsupConfig?: (config: TsupConfig) => TsupConfig; + modifyViteConfig?: (config: ViteConfig) => ViteConfig; + beforeBuild?: (log: PkgLog) => void | Promise; + afterBuild?: (log: PkgLog) => void | Promise; +} + +declare const defineConfig: (config: UserConfig) => UserConfig; diff --git a/packages/core/build/src/index.ts b/packages/core/build/src/index.ts index 555cb3eb6..eb094a48f 100755 --- a/packages/core/build/src/index.ts +++ b/packages/core/build/src/index.ts @@ -1 +1,2 @@ export * from './build'; +export * from './utils' diff --git a/packages/core/build/src/utils/utils.ts b/packages/core/build/src/utils/utils.ts index d6aff5f3a..aa616dda9 100644 --- a/packages/core/build/src/utils/utils.ts +++ b/packages/core/build/src/utils/utils.ts @@ -1,5 +1,10 @@ import chalk from 'chalk'; import path from 'path'; +import fs from 'fs-extra'; +import fg from 'fast-glob'; +import { Options as TsupConfig } from 'tsup' +import { InlineConfig as ViteConfig } from 'vite' +import { register } from 'esbuild-register/dist/node'; let previousColor = ''; function randomColor() { @@ -44,3 +49,33 @@ export function toUnixPath(filepath: string) { export function getPackageJson(cwd: string) { return require(path.join(cwd, 'package.json')); } + +export interface UserConfig { + modifyTsupConfig?: (config: TsupConfig) => TsupConfig; + modifyViteConfig?: (config: ViteConfig) => ViteConfig; + beforeBuild?: (log: PkgLog) => void | Promise; + afterBuild?: (log: PkgLog) => void | Promise; +} + +export function defineConfig(config: UserConfig): UserConfig { + return config; +} + +export function getUserConfig(cwd: string) { + const config = defineConfig({ + modifyTsupConfig: (config: TsupConfig) => config, + modifyViteConfig: (config: ViteConfig) => config, + }); + + const buildConfigs = fg.sync(['build.config.js', 'build.config.ts'], { cwd }); + if (buildConfigs.length > 1) { + throw new Error(`Multiple build configs found: ${buildConfigs.join(', ')}`); + } + if (buildConfigs.length === 1) { + const { unregister } = register({}) + const userConfig = require(path.join(cwd, buildConfigs[0])); + unregister() + Object.assign(config, userConfig.default || userConfig); + } + return config; +} diff --git a/packages/core/build/tsup.config.ts b/packages/core/build/tsup.config.ts index 5fa8d2ef5..5d962ca85 100644 --- a/packages/core/build/tsup.config.ts +++ b/packages/core/build/tsup.config.ts @@ -9,8 +9,12 @@ export default defineConfig({ entry, outDir: path.join(__dirname, 'lib'), splitting: false, + silent: true, sourcemap: false, clean: true, bundle: false, + loader: { + '.d.ts': 'copy' + }, skipNodeModulesBundle: true, }); diff --git a/packages/core/cli/bin/index.js b/packages/core/cli/bin/index.js index b597fb6af..28953a098 100755 --- a/packages/core/cli/bin/index.js +++ b/packages/core/cli/bin/index.js @@ -4,6 +4,7 @@ const dotenv = require('dotenv'); const { resolve } = require('path'); const { existsSync } = require('fs'); const chalk = require('chalk'); +const { genTsConfigPaths } = require('../src/util'); const env = { APP_ENV: 'development', @@ -29,6 +30,8 @@ if (!process.env.APP_ENV_PATH && process.argv[2] && process.argv[2] === 'test') } } +genTsConfigPaths(); + dotenv.config({ path: resolve(process.cwd(), process.env.APP_ENV_PATH || '.env'), }); diff --git a/packages/core/cli/src/commands/build.js b/packages/core/cli/src/commands/build.js index 33c6e0087..39adbab65 100644 --- a/packages/core/cli/src/commands/build.js +++ b/packages/core/cli/src/commands/build.js @@ -24,6 +24,7 @@ module.exports = (cli) => { }); if (options.watch) return; } + await run('nocobase-build', [ ...pkgs, options.version ? '--version' : '', diff --git a/packages/core/cli/src/commands/clean.js b/packages/core/cli/src/commands/clean.js index b9055cbac..ab753740d 100644 --- a/packages/core/cli/src/commands/clean.js +++ b/packages/core/cli/src/commands/clean.js @@ -16,5 +16,6 @@ module.exports = (cli) => { } run('rimraf', ['-rf', './storage/app-dev']); run('rimraf', ['-rf', 'packages/*/*/{lib,esm,es,dist,node_modules}']); + run('rimraf', ['-rf', 'packages/*/@*/*/{lib,esm,es,dist,node_modules}']); }); }; diff --git a/packages/core/cli/src/commands/test.js b/packages/core/cli/src/commands/test.js index 0764beb48..993c79485 100644 --- a/packages/core/cli/src/commands/test.js +++ b/packages/core/cli/src/commands/test.js @@ -1,5 +1,5 @@ const { Command } = require('commander'); -const { nodeCheck, runAppCommand, promptForTs } = require('../util'); +const { nodeCheck, runAppCommand, promptForTs, genTsConfigPaths } = require('../util'); /** * diff --git a/packages/core/cli/src/index.js b/packages/core/cli/src/index.js index e69de29bb..85265d334 100644 --- a/packages/core/cli/src/index.js +++ b/packages/core/cli/src/index.js @@ -0,0 +1,5 @@ +const util = require('./util'); + +module.exports = { + ...util, +}; diff --git a/packages/core/cli/src/plugin-generator.js b/packages/core/cli/src/plugin-generator.js index b8f6bbc76..a84876ed3 100644 --- a/packages/core/cli/src/plugin-generator.js +++ b/packages/core/cli/src/plugin-generator.js @@ -3,6 +3,7 @@ const { existsSync } = require('fs'); const { join, resolve } = require('path'); const { Generator } = require('@umijs/utils'); const { readFile, writeFile } = require('fs').promises; +const { genTsConfigPaths } = require('./util'); const execa = require('execa'); @@ -46,16 +47,6 @@ class PluginGenerator extends Generator { }; } - async addTsConfigPaths() { - const { name } = this.context; - if (name.startsWith('@') && name.split('/')[0] !== '@nocobase') { - const target = resolve(process.cwd(), 'tsconfig.json'); - const content = require(target); - content.compilerOptions.paths[name] = [`packages/plugins/${name}/src`]; - await writeFile(target, JSON.stringify(content, null, 2), 'utf-8'); - } - } - async writing() { const { name } = this.context; const target = resolve(process.cwd(), 'packages/plugins/', name); @@ -70,7 +61,7 @@ class PluginGenerator extends Generator { path: join(__dirname, '../templates/plugin'), }); console.log(''); - await this.addTsConfigPaths(); + genTsConfigPaths(); execa.sync('yarn', ['install'], { shell: true, stdio: 'inherit' }); // execa.sync('yarn', ['build', `plugins/${name}`], { shell: true, stdio: 'inherit' }); console.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`); diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index b392f6320..b31299edf 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -1,9 +1,10 @@ const net = require('net'); const chalk = require('chalk'); const execa = require('execa'); -const { dirname, resolve } = require('path'); +const fg = require('fast-glob'); +const { dirname, join, resolve } = require('path'); const { readFile, writeFile } = require('fs').promises; -const { existsSync, mkdirSync, cpSync } = require('fs'); +const { existsSync, mkdirSync, cpSync, writeFileSync } = require('fs'); exports.isPackageValid = (package) => { try { @@ -174,3 +175,32 @@ exports.generateAppDir = function generateAppDir() { process.env.APP_PACKAGE_ROOT = appPkgPath; } }; + +exports.genTsConfigPaths = function genTsConfigPaths() { + const cwd = process.cwd(); + const cwdLength = cwd.length; + const paths = { + '@@/*': ['.dumi/tmp/*'], + }; + const packages = fg.sync(['packages/*/*/package.json', 'packages/*/*/*/package.json'], { + absolute: true, + onlyFiles: true, + }); + packages.forEach((packageFile) => { + const packageJsonName = require(packageFile).name; + const packageDir = dirname(packageFile); + const relativePath = packageDir.slice(cwdLength + 1).replace(/\\/, '/'); + const hasClient = fg.sync(['client', 'client.ts', 'client.tsx', 'client.js', 'client.jsx'], { + cwd: join(packageDir, 'src'), + }).length; + if (hasClient) { + paths[`${packageJsonName}/client`] = [`${relativePath}/src/client`]; + } + paths[packageJsonName] = [`${relativePath}/src`]; + }); + + const tsConfigJsonPath = join(cwd, './tsconfig.paths.json'); + const content = { compilerOptions: { paths } }; + writeFileSync(tsConfigJsonPath, JSON.stringify(content, null, 2), 'utf-8'); + return content; +}; diff --git a/packages/core/create-nocobase-app/templates/app/jest.config.js b/packages/core/create-nocobase-app/templates/app/jest.config.js index efe650cee..d5deb30b4 100644 --- a/packages/core/create-nocobase-app/templates/app/jest.config.js +++ b/packages/core/create-nocobase-app/templates/app/jest.config.js @@ -1,5 +1,5 @@ const { pathsToModuleNameMapper } = require('ts-jest/utils'); -const { compilerOptions } = require('./tsconfig.json'); +const { compilerOptions } = require('./tsconfig.paths.json'); const { resolve } = require('path'); module.exports = { diff --git a/packages/core/create-nocobase-app/templates/app/package.json.tpl b/packages/core/create-nocobase-app/templates/app/package.json.tpl index fea93899e..38802a228 100644 --- a/packages/core/create-nocobase-app/templates/app/package.json.tpl +++ b/packages/core/create-nocobase-app/templates/app/package.json.tpl @@ -4,6 +4,9 @@ "workspaces": [ "packages/*/*" ], + "engines": { + "node": ">=16.0.0" + }, "scripts": { "nocobase": "nocobase", "pm": "nocobase pm", diff --git a/packages/core/database/src/collection-importer.ts b/packages/core/database/src/collection-importer.ts index 1d1a78a29..ddae5023c 100644 --- a/packages/core/database/src/collection-importer.ts +++ b/packages/core/database/src/collection-importer.ts @@ -1,4 +1,5 @@ import path from 'path'; +import { existsSync } from 'fs'; import { readdir } from 'fs/promises'; import { cloneDeep, isPlainObject } from 'lodash'; import { requireModule } from '@nocobase/utils'; @@ -20,6 +21,9 @@ export class ImporterReader { } async read() { + if (!existsSync(this.directory)) { + return []; + } const files = await readdir(this.directory, { encoding: 'utf-8', }); diff --git a/packages/core/devtools/umiConfig.js b/packages/core/devtools/umiConfig.js index 08b6d32a0..efe2ae0cc 100644 --- a/packages/core/devtools/umiConfig.js +++ b/packages/core/devtools/umiConfig.js @@ -63,7 +63,7 @@ function getNamespace() { } function getTsconfigPaths() { - const content = fs.readFileSync(resolve(process.cwd(), 'tsconfig.json'), 'utf-8'); + const content = fs.readFileSync(resolve(process.cwd(), 'tsconfig.paths.json'), 'utf-8'); const json = JSON.parse(content); return json.compilerOptions.paths; } diff --git a/packages/plugins/@nocobase/plugin-acl/package.json b/packages/plugins/@nocobase/plugin-acl/package.json index ad4e1b29d..f0847cd4a 100644 --- a/packages/plugins/@nocobase/plugin-acl/package.json +++ b/packages/plugins/@nocobase/plugin-acl/package.json @@ -9,7 +9,7 @@ "main": "./dist/server/index.js", "devDependencies": { "@types/jsonwebtoken": "^8.5.8", - "async-mutex": "^0.4.0", + "async-mutex": "^0.3.2", "jsonwebtoken": "^8.5.1", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/plugins/@nocobase/plugin-china-region/build.config.ts b/packages/plugins/@nocobase/plugin-china-region/build.config.ts new file mode 100644 index 000000000..0191a008f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-china-region/build.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from '@nocobase/build'; + +import fs from 'fs/promises'; +import path from 'path'; +import { existsSync } from 'fs'; + +export default defineConfig({ + afterBuild: async (log) => { + const dir = path.resolve(__dirname, './dist/china-division'); + if (existsSync(dir)) { + await fs.rm(dir, { recursive: true }); + } + + const keys = ['areas', 'cities', 'provinces']; + for (const key of keys) { + log(`coping ${key}.json`); + await fs.cp(require.resolve(`china-division/dist/${key}.json`), path.resolve(dir, `${key}.json`), { + recursive: true, + force: true, + }); + } + }, +}); diff --git a/packages/plugins/@nocobase/plugin-china-region/build.js b/packages/plugins/@nocobase/plugin-china-region/build.js deleted file mode 100644 index 077aae633..000000000 --- a/packages/plugins/@nocobase/plugin-china-region/build.js +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs/promises'); -const path = require('path'); -const existsSync = require('fs').existsSync; - -exports.run = async (log) => { - const dir = path.resolve(__dirname, './dist/china-division'); - if (existsSync(dir)) { - await fs.rmdir(dir, { force: true, recursive: true }); - } - - const keys = ['areas', 'cities', 'provinces']; - for (const key of keys) { - log(`coping ${key}.json`); - await fs.cp(require.resolve(`china-division/dist/${key}.json`), path.resolve(dir, `${key}.json`), { - recursive: true, - force: true, - }); - } -}; diff --git a/packages/plugins/@nocobase/plugin-client/build.config.ts b/packages/plugins/@nocobase/plugin-client/build.config.ts new file mode 100644 index 000000000..9eae7c54c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-client/build.config.ts @@ -0,0 +1,42 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { defineConfig } from '@nocobase/build'; + +const existsSync = require('fs').existsSync; + +const client = path.dirname(require.resolve('@nocobase/client/package.json')); +const antd = require.resolve('antd'); + +export default defineConfig({ + afterBuild: async (log) => { + const localeDir = path.resolve(__dirname, './dist/locale'); + if (existsSync(localeDir)) { + await fs.rm(localeDir, { recursive: true }); + } + + log('coping client locale'); + await fs.cp(path.resolve(client, 'lib', 'locale'), localeDir, { + recursive: true, + force: true, + }); + + log('coping antd locale'); + const files = await fs.readdir(path.resolve(path.dirname(antd), 'locale')); + await fs.mkdir(path.resolve(localeDir, 'antd'), { recursive: true }); + for (const file of files) { + if (path.extname(file) !== '.js') { + continue; + } + const content = require(path.resolve(path.dirname(antd), 'locale', file)).default; + try { + await fs.writeFile( + path.resolve(localeDir, 'antd', file), + `module.exports = ${JSON.stringify(content)}`, + 'utf-8', + ); + } catch (error) { + log(`skip ${file}`); + } + } + }, +}); diff --git a/packages/plugins/@nocobase/plugin-client/build.js b/packages/plugins/@nocobase/plugin-client/build.js deleted file mode 100644 index 5b9cbf4e3..000000000 --- a/packages/plugins/@nocobase/plugin-client/build.js +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs/promises'); -const path = require('path'); -const existsSync = require('fs').existsSync; - -const client = require.resolve('@nocobase/client'); -const antd = require.resolve('antd'); - -exports.run = async (log) => { - const localeDir = path.resolve(__dirname, './dist/locale'); - if (existsSync(localeDir)) { - await fs.rmdir(localeDir, { force: true, recursive: true }); - } - - log('coping client locale'); - await fs.cp(path.resolve(path.dirname(client), 'locale'), localeDir, { - recursive: true, - force: true, - }); - - log('coping antd locale'); - const files = await fs.readdir(path.resolve(path.dirname(antd), 'locale')); - await fs.mkdir(path.resolve(localeDir, 'antd'), { force: true, recursive: true }); - for (const file of files) { - if (path.extname(file) !== '.js') { - continue; - } - const content = require(path.resolve(path.dirname(antd), 'locale', file)).default; - try { - await fs.writeFile( - path.resolve(localeDir, 'antd', file), - `module.exports = ${JSON.stringify(content)}`, - 'utf-8', - {}, - ); - } catch (error) { - log(`skip ${file}`); - } - } -}; diff --git a/tsconfig.json b/tsconfig.json index 77419b51e..fb2467797 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "./tsconfig.paths.json", "compilerOptions": { "esModuleInterop": true, "moduleResolution": "node", @@ -16,17 +17,7 @@ "declaration": true, "experimentalDecorators": true, "downlevelIteration": true, - "baseUrl": ".", - "paths": { - "@nocobase/app-*": ["packages/app/*/src"], - "@nocobase/plugin-*": ["packages/plugins/@nocobase/plugin-*/src"], - "@nocobase/preset-*": ["packages/presets/*/src"], - "@nocobase/evaluators/client": ["packages/core/evaluators/src/client"], - "@nocobase/utils/plugin-symlink": ["packages/core/utils/plugin-symlink"], - "@nocobase/utils/client": ["packages/core/utils/src/client"], - "@nocobase/*": ["packages/core/*/src"], - "@@/*": [".dumi/tmp/*"] - } + "baseUrl": "." }, "ts-node": { "compilerOptions": { diff --git a/vitest.config.ts b/vitest.config.ts index 58b965284..7248a6264 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,5 +1,19 @@ import react from '@vitejs/plugin-react'; import { defineConfig } from 'vitest/config'; +import tsConfigPaths from './tsconfig.paths.json'; + +const paths = tsConfigPaths.compilerOptions.paths; + +const alias = Object.keys(paths).reduce<{ find: string; replacement: string }[]>((acc, key) => { + if (key !== '@@/*') { + const value = paths[key][0]; + acc.push({ + find: key, + replacement: value, + }); + } + return acc; +}, []); export default defineConfig({ plugins: [react()], @@ -14,15 +28,8 @@ export default defineConfig({ threads: true, alias: [ { find: 'testUtils', replacement: 'testUtils.ts' }, - { find: '@nocobase/evaluators/client', replacement: 'packages/core/evaluators/src/client' }, - { find: '@nocobase/utils/client', replacement: 'packages/core/utils/src/client' }, { find: /^~antd\/(.*)/, replacement: 'antd/$1' }, - { find: /^@nocobase\/app-(.*)/, replacement: 'packages/$1/src' }, - { find: /^@nocobase\/plugin-sample-(.*)/, replacement: 'packages/samples/$1/src' }, - { find: /^@nocobase\/plugin-pro-(.*)/, replacement: 'packages/pro-plugins/$1/src' }, - { find: /^@nocobase\/plugin-(.*)/, replacement: 'packages/plugins/$1/src' }, - { find: /^@nocobase\/preset-(.*)/, replacement: 'packages/presets/$1/src' }, - { find: /^@nocobase\/(.*)/, replacement: 'packages/core/$1/src' }, + ...alias, ], include: ['packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}'], exclude: ['**/node_modules/**', '**/dist/**', '**/lib/**', '**/es/**', '**/{vitest,commitlint}.config.*'], diff --git a/yarn.lock b/yarn.lock index 605d3a154..d62199c8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7569,12 +7569,6 @@ async-mutex@^0.3.2: dependencies: tslib "^2.3.1" -async-mutex@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" - dependencies: - tslib "^2.4.0" - async-ratelimiter@^1.3.0: version "1.3.8" resolved "https://registry.npmmirror.com/async-ratelimiter/-/async-ratelimiter-1.3.8.tgz#05198a322543de43d98807c96295a9d712306928" @@ -11004,6 +10998,13 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.3" es6-symbol "^3.1.1" +esbuild-register@^3.4.2: + version "3.4.2" + resolved "https://registry.npmmirror.com/esbuild-register/-/esbuild-register-3.4.2.tgz#1e39ee0a77e8f320a9790e68c64c3559620b9175" + integrity sha512-kG/XyTDyz6+YDuyfB9ZoSIOOmgyFCH+xPRtsCa8W85HLRV5Csp+o3jWVbOSHgSLfyLc5DmP+KFDNwty4mEjC+Q== + dependencies: + debug "^4.3.4" + esbuild@0.12.15: version "0.12.15" resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.12.15.tgz#9d99cf39aeb2188265c5983e983e236829f08af0" @@ -18890,9 +18891,10 @@ postcss@^8.3.11, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.7: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.24: - version "8.4.25" - resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" +postcss@^8.4.27: + version "8.4.29" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" + integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -20678,9 +20680,10 @@ rollup@^3.20.2, rollup@^3.21.0: optionalDependencies: fsevents "~2.3.2" -rollup@^3.25.2: - version "3.26.2" - resolved "https://registry.npmmirror.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" +rollup@^3.27.1: + version "3.29.1" + resolved "https://registry.npmmirror.com/rollup/-/rollup-3.29.1.tgz#ba53a179d46ac3cd79e162dca6ab70d93cd26f78" + integrity sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg== optionalDependencies: fsevents "~2.3.2" @@ -23314,13 +23317,14 @@ vite@4.3.1: optionalDependencies: fsevents "~2.3.2" -vite@^4.4.1: - version "4.4.1" - resolved "https://registry.npmmirror.com/vite/-/vite-4.4.1.tgz#cfe0baf6af4b1b9f3b37c5ebf1e012622be3da98" +vite@^4.4.9: + version "4.4.9" + resolved "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d" + integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA== dependencies: esbuild "^0.18.10" - postcss "^8.4.24" - rollup "^3.25.2" + postcss "^8.4.27" + rollup "^3.27.1" optionalDependencies: fsevents "~2.3.2"