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 <chenlinxh@gmail.com>
This commit is contained in:
jack zhang 2023-09-15 08:51:20 +08:00 committed by GitHub
parent dbe3af26a1
commit 9e5e96b9e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 376 additions and 216 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ storage/plugins
storage/tar storage/tar
storage/tmp storage/tmp
storage/app.watch.ts storage/app.watch.ts
tsconfig.paths.json

View File

@ -12,6 +12,34 @@ An empty plugin can be created quickly with `yarn pm create my-plugin`, with the
|- package.json # plugin package information |- package.json # plugin package information
|- server.d.ts |- server.d.ts
|- server.js |- 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. 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
};
})
```

View File

@ -12,6 +12,34 @@
|- package.json # 插件包信息 |- package.json # 插件包信息
|- server.d.ts |- server.d.ts
|- server.js |- server.js
|- build.config.ts # 或者 `build.config.js` ,用于修改打包配置,实现自定义逻辑
``` ```
`/src/server` 的教程参考 [服务端](./server) 章节,`/src/client` 的教程参考 [客户端](./client) 章节。 `/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) => {
// 构建完成后的回调函数,可以在构建完成后做一些操作
};
});
```

View File

@ -1,5 +1,5 @@
const { pathsToModuleNameMapper } = require('ts-jest'); const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json'); const { compilerOptions } = require('./tsconfig.paths.json');
const { defaults } = require('jest-config'); const { defaults } = require('jest-config');
module.exports = { module.exports = {

View File

@ -87,7 +87,7 @@
"react-dom": "^18.0.0", "react-dom": "^18.0.0",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
"typescript": "5.1.3", "typescript": "5.1.3",
"vite": "^4.4.1", "vite": "^4.4.9",
"vitest": "^0.34.3" "vitest": "^0.34.3"
}, },
"volta": { "volta": {

View File

@ -3,6 +3,7 @@
"version": "0.14.0-alpha.3", "version": "0.14.0-alpha.3",
"description": "Library build tool based on rollup.", "description": "Library build tool based on rollup.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "./lib/index.d.ts",
"bin": { "bin": {
"nocobase-build": "./bin/nocobase-build.js" "nocobase-build": "./bin/nocobase-build.js"
}, },
@ -28,7 +29,8 @@
"update-notifier": "3.0.0", "update-notifier": "3.0.0",
"vite-plugin-css-injected-by-js": "^3.2.1", "vite-plugin-css-injected-by-js": "^3.2.1",
"vite-plugin-lib-inject-css": "1.2.0", "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", "license": "Apache-2.0",
"scripts": { "scripts": {

View File

@ -14,7 +14,7 @@ import { buildClient } from './buildClient';
import { buildCjs } from './buildCjs'; import { buildCjs } from './buildCjs';
import { buildPlugin } from './buildPlugin'; import { buildPlugin } from './buildPlugin';
import { buildDeclaration } from './buildDeclaration'; 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 { getPackages } from './utils/getPackages';
import { Package } from '@lerna/package'; import { Package } from '@lerna/package';
import { tarPlugin } from './tarPlugin' import { tarPlugin } from './tarPlugin'
@ -23,6 +23,11 @@ export async function build(pkgs: string[]) {
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
const packages = getPackages(pkgs); 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 pluginPackages = getPluginPackages(packages);
const cjsPackages = getCjsPackages(packages); const cjsPackages = getCjsPackages(packages);
const presetsPackages = getPresetsPackages(packages); const presetsPackages = getPresetsPackages(packages);
@ -52,7 +57,7 @@ export async function build(pkgs: string[]) {
export async function buildPackages( export async function buildPackages(
packages: Package[], packages: Package[],
targetDir: string, targetDir: string,
doBuildPackage: (cwd: string, sourcemap: boolean, log?: PkgLog) => Promise<any>, doBuildPackage: (cwd: string, userConfig: UserConfig, sourcemap: boolean, log?: PkgLog) => Promise<any>,
) { ) {
for await (const pkg of packages) { for await (const pkg of packages) {
await buildPackage(pkg, targetDir, doBuildPackage); await buildPackage(pkg, targetDir, doBuildPackage);
@ -62,7 +67,7 @@ export async function buildPackages(
export async function buildPackage( export async function buildPackage(
pkg: Package, pkg: Package,
targetDir: string, targetDir: string,
doBuildPackage: (cwd: string, sourcemap: boolean, log?: PkgLog) => Promise<any>, doBuildPackage: (cwd: string, userConfig: UserConfig, sourcemap: boolean, log?: PkgLog) => Promise<any>,
) { ) {
const sourcemap = process.argv.includes('--sourcemap'); const sourcemap = process.argv.includes('--sourcemap');
const noDeclaration = process.argv.includes('--no-dts'); 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`); log(`${chalk.bold(toUnixPath(pkg.location.replace(PACKAGES_PATH, '').slice(1)))} build start`);
const userConfig = getUserConfig(pkg.location);
// prebuild // prebuild
if (packageJson?.scripts?.prebuild) { if (packageJson?.scripts?.prebuild) {
log('prebuild'); log('prebuild');
await runScript(['prebuild'], pkg.location); await runScript(['prebuild'], pkg.location);
await packageJson.prebuild(pkg.location); await packageJson.prebuild(pkg.location);
} }
if (userConfig.beforeBuild) {
log('beforeBuild');
await userConfig.beforeBuild(log);
}
// build source // build source
await doBuildPackage(pkg.location, sourcemap, log); await doBuildPackage(pkg.location, userConfig, sourcemap, log);
// build declaration // build declaration
if (!noDeclaration) { if (!noDeclaration) {
@ -101,6 +111,11 @@ export async function buildPackage(
await runScript(['postbuild'], pkg.location); await runScript(['postbuild'], pkg.location);
} }
if (userConfig.afterBuild) {
log('afterBuild');
await userConfig.afterBuild(log);
}
// tar // tar
if (hasTar) { if (hasTar) {
await tarPlugin(pkg.location, log); await tarPlugin(pkg.location, log);

View File

@ -3,9 +3,9 @@ import fg from 'fast-glob';
import path from 'path'; import path from 'path';
import chalk from 'chalk'; import chalk from 'chalk';
import { globExcludeFiles, EsbuildSupportExts } from './constant'; 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'); log('build cjs');
const entry = fg.globSync(['src/**', ...globExcludeFiles], { cwd, absolute: true }); 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) { if (otherExts.length) {
log('%s will not be processed, only be copied to the lib directory.', chalk.yellow(otherExts.join(','))); log('%s will not be processed, only be copied to the lib directory.', chalk.yellow(otherExts.join(',')));
} }
return build(userConfig.modifyTsupConfig({
return build({
entry, entry,
splitting: false, splitting: false,
clean: true, clean: true,
@ -32,5 +31,5 @@ export function buildCjs(cwd: string, sourcemap: boolean = false, log: PkgLog) {
}, },
format: 'cjs', format: 'cjs',
skipNodeModulesBundle: true, skipNodeModulesBundle: true,
}); }));
} }

View File

@ -1,26 +1,37 @@
import path from 'path'; import path from 'path';
import fg from 'fast-glob'; import fg from 'fast-glob';
import fs from 'fs-extra';
import { build as viteBuild } from 'vite'; import { build as viteBuild } from 'vite';
import { build as tsupBuild } from 'tsup'; import { build as tsupBuild } from 'tsup';
import { libInjectCss } from 'vite-plugin-lib-inject-css'; import { libInjectCss } from 'vite-plugin-lib-inject-css';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { PkgLog } from './utils';
import { PkgLog, UserConfig } from './utils';
import { globExcludeFiles } from './constant'; 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'); 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 cwdWin = cwd.replaceAll(/\\/g, '/');
const cwdUnix = 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', mode: 'production',
define: { define: {
'process.env.NODE_ENV': JSON.stringify('production'), 'process.env.NODE_ENV': JSON.stringify('production'),
@ -33,29 +44,63 @@ export function buildLib(cwd: string, sourcemap: boolean, format: 'cjs' | 'es')
sourcemap, sourcemap,
lib: { lib: {
entry, entry,
formats: [format], formats: ['es'],
fileName: 'index', fileName: 'index',
}, },
target: ['es2015', 'edge88', 'firefox78', 'chrome87', 'safari14'], target: ['es2015', 'edge88', 'firefox78', 'chrome87', 'safari14'],
rollupOptions: { rollupOptions: {
cache: true, cache: true,
treeshake: true, treeshake: true,
external(id) { external,
if (id.startsWith('.') || id.startsWith(cwdUnix) || id.startsWith(cwdWin)) {
return false;
}
return true;
},
}, },
}, },
plugins: [react(), libInjectCss()], 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 entry = fg.globSync(['src/locale/**', ...globExcludeFiles], { cwd, absolute: true });
const outDir = path.resolve(cwd, 'lib', 'locale'); const outDir = path.resolve(cwd, 'lib', 'locale');
return tsupBuild({ return tsupBuild(userConfig.modifyTsupConfig({
entry, entry,
splitting: false, splitting: false,
clean: false, clean: false,
@ -67,5 +112,5 @@ export function buildLocale(cwd: string) {
outDir, outDir,
format: 'cjs', format: 'cjs',
skipNodeModulesBundle: true, skipNodeModulesBundle: true,
}); }));
} }

View File

@ -7,7 +7,6 @@ import { build as tsupBuild } from 'tsup';
import { build as viteBuild } from 'vite'; import { build as viteBuild } from 'vite';
import fg from 'fast-glob'; import fg from 'fast-glob';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import { transformAsync } from '@babel/core';
import { import {
buildCheck, buildCheck,
@ -20,7 +19,7 @@ import {
} from './utils/buildPluginUtils'; } from './utils/buildPluginUtils';
import { getDepPkgPath, getDepsConfig } from './utils/getDepsConfig'; import { getDepPkgPath, getDepsConfig } from './utils/getDepsConfig';
import { EsbuildSupportExts, globExcludeFiles } from './constant'; import { EsbuildSupportExts, globExcludeFiles } from './constant';
import { PkgLog, getPackageJson } from './utils'; import { PkgLog, UserConfig, getPackageJson } from './utils';
const serverGlobalFiles: string[] = ['src/**', '!src/client/**', ...globExcludeFiles]; const serverGlobalFiles: string[] = ['src/**', '!src/client/**', ...globExcludeFiles];
const clientGlobalFiles: string[] = ['src/**', '!src/server/**', ...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'); log('build plugin server source');
const packageJson = getPackageJson(cwd); const packageJson = getPackageJson(cwd);
const serverFiles = fg.globSync(serverGlobalFiles, { cwd, absolute: true }); 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(','))); 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, entry: serverFiles,
splitting: false, splitting: false,
clean: false, clean: false,
@ -273,50 +272,12 @@ export async function buildPluginServer(cwd: string, sourcemap: boolean, log: Pk
...otherExts.reduce((prev, cur) => ({ ...prev, [cur]: 'copy' }), {}), ...otherExts.reduce((prev, cur) => ({ ...prev, [cur]: 'copy' }), {}),
'.json': 'copy', '.json': 'copy',
}, },
}); }));
await buildServerDeps(cwd, serverFiles, log); await buildServerDeps(cwd, serverFiles, log);
} }
export function transformClientFilesToAmd(outDir: string, outputFileName: string, packageName: string, log: PkgLog) { export async function buildPluginClient(cwd: string, userConfig: UserConfig, sourcemap: boolean, 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) {
log('build plugin client'); log('build plugin client');
const packageJson = getPackageJson(cwd); const packageJson = getPackageJson(cwd);
const clientFiles = fg.globSync(clientGlobalFiles, { cwd, absolute: true }); 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); checkRequire(clientFiles, log);
buildCheck({ cwd, packageJson, entry: 'client', files: 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 outDir = path.join(cwd, target_dir, 'client');
const globals = excludePackages.reduce<Record<string, string>>((prev, curr) => { const globals = excludePackages.reduce<Record<string, string>>((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 entry = fg.globSync('src/client/index.{ts,tsx,js,jsx}', { absolute: true, cwd });
const outputFileName = 'index.js'; const outputFileName = 'index.js';
await viteBuild({
await viteBuild(userConfig.modifyViteConfig({
mode: 'production', mode: 'production',
define: { define: {
'process.env.NODE_ENV': JSON.stringify('production'), 'process.env.NODE_ENV': JSON.stringify('production'),
@ -356,7 +314,7 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk
sourcemap, sourcemap,
lib: { lib: {
entry, entry,
formats: [hasDynamicImport ? 'es' : 'umd'], formats: ['umd'],
name: packageJson.name, name: packageJson.name,
fileName: () => outputFileName, fileName: () => outputFileName,
}, },
@ -378,26 +336,13 @@ export async function buildPluginClient(cwd: string, sourcemap: boolean, log: Pk
react(), react(),
cssInjectedByJsPlugin({ styleId: packageJson.name }), cssInjectedByJsPlugin({ styleId: packageJson.name }),
], ],
}); }));
checkFileSize(outDir, log); checkFileSize(outDir, log);
// if (hasDynamicImport) {
// await transformClientFilesToAmd(outDir, outputFileName, packageJson.name, log);
// }
} }
export async function buildPlugin(cwd: string, sourcemap: boolean, log: PkgLog) { export async function buildPlugin(cwd: string, userConfig: UserConfig, sourcemap: boolean, log: PkgLog) {
await buildPluginClient(cwd, sourcemap, log); await buildPluginClient(cwd, userConfig, sourcemap, log);
await buildPluginServer(cwd, sourcemap, log); await buildPluginServer(cwd, userConfig, 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);
}
}
writeExternalPackageVersion(cwd, log); writeExternalPackageVersion(cwd, log);
} }

14
packages/core/build/src/index.d.ts vendored Normal file
View File

@ -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<void>;
afterBuild?: (log: PkgLog) => void | Promise<void>;
}
declare const defineConfig: (config: UserConfig) => UserConfig;

View File

@ -1 +1,2 @@
export * from './build'; export * from './build';
export * from './utils'

View File

@ -1,5 +1,10 @@
import chalk from 'chalk'; import chalk from 'chalk';
import path from 'path'; 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 = ''; let previousColor = '';
function randomColor() { function randomColor() {
@ -44,3 +49,33 @@ export function toUnixPath(filepath: string) {
export function getPackageJson(cwd: string) { export function getPackageJson(cwd: string) {
return require(path.join(cwd, 'package.json')); return require(path.join(cwd, 'package.json'));
} }
export interface UserConfig {
modifyTsupConfig?: (config: TsupConfig) => TsupConfig;
modifyViteConfig?: (config: ViteConfig) => ViteConfig;
beforeBuild?: (log: PkgLog) => void | Promise<void>;
afterBuild?: (log: PkgLog) => void | Promise<void>;
}
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;
}

View File

@ -9,8 +9,12 @@ export default defineConfig({
entry, entry,
outDir: path.join(__dirname, 'lib'), outDir: path.join(__dirname, 'lib'),
splitting: false, splitting: false,
silent: true,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
bundle: false, bundle: false,
loader: {
'.d.ts': 'copy'
},
skipNodeModulesBundle: true, skipNodeModulesBundle: true,
}); });

View File

@ -4,6 +4,7 @@ const dotenv = require('dotenv');
const { resolve } = require('path'); const { resolve } = require('path');
const { existsSync } = require('fs'); const { existsSync } = require('fs');
const chalk = require('chalk'); const chalk = require('chalk');
const { genTsConfigPaths } = require('../src/util');
const env = { const env = {
APP_ENV: 'development', APP_ENV: 'development',
@ -29,6 +30,8 @@ if (!process.env.APP_ENV_PATH && process.argv[2] && process.argv[2] === 'test')
} }
} }
genTsConfigPaths();
dotenv.config({ dotenv.config({
path: resolve(process.cwd(), process.env.APP_ENV_PATH || '.env'), path: resolve(process.cwd(), process.env.APP_ENV_PATH || '.env'),
}); });

View File

@ -24,6 +24,7 @@ module.exports = (cli) => {
}); });
if (options.watch) return; if (options.watch) return;
} }
await run('nocobase-build', [ await run('nocobase-build', [
...pkgs, ...pkgs,
options.version ? '--version' : '', options.version ? '--version' : '',

View File

@ -16,5 +16,6 @@ module.exports = (cli) => {
} }
run('rimraf', ['-rf', './storage/app-dev']); 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}']);
run('rimraf', ['-rf', 'packages/*/@*/*/{lib,esm,es,dist,node_modules}']);
}); });
}; };

View File

@ -1,5 +1,5 @@
const { Command } = require('commander'); const { Command } = require('commander');
const { nodeCheck, runAppCommand, promptForTs } = require('../util'); const { nodeCheck, runAppCommand, promptForTs, genTsConfigPaths } = require('../util');
/** /**
* *

View File

@ -0,0 +1,5 @@
const util = require('./util');
module.exports = {
...util,
};

View File

@ -3,6 +3,7 @@ const { existsSync } = require('fs');
const { join, resolve } = require('path'); const { join, resolve } = require('path');
const { Generator } = require('@umijs/utils'); const { Generator } = require('@umijs/utils');
const { readFile, writeFile } = require('fs').promises; const { readFile, writeFile } = require('fs').promises;
const { genTsConfigPaths } = require('./util');
const execa = require('execa'); 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() { async writing() {
const { name } = this.context; const { name } = this.context;
const target = resolve(process.cwd(), 'packages/plugins/', name); const target = resolve(process.cwd(), 'packages/plugins/', name);
@ -70,7 +61,7 @@ class PluginGenerator extends Generator {
path: join(__dirname, '../templates/plugin'), path: join(__dirname, '../templates/plugin'),
}); });
console.log(''); console.log('');
await this.addTsConfigPaths(); genTsConfigPaths();
execa.sync('yarn', ['install'], { shell: true, stdio: 'inherit' }); execa.sync('yarn', ['install'], { shell: true, stdio: 'inherit' });
// execa.sync('yarn', ['build', `plugins/${name}`], { 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}`)}`); console.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`);

View File

@ -1,9 +1,10 @@
const net = require('net'); const net = require('net');
const chalk = require('chalk'); const chalk = require('chalk');
const execa = require('execa'); 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 { readFile, writeFile } = require('fs').promises;
const { existsSync, mkdirSync, cpSync } = require('fs'); const { existsSync, mkdirSync, cpSync, writeFileSync } = require('fs');
exports.isPackageValid = (package) => { exports.isPackageValid = (package) => {
try { try {
@ -174,3 +175,32 @@ exports.generateAppDir = function generateAppDir() {
process.env.APP_PACKAGE_ROOT = appPkgPath; 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;
};

View File

@ -1,5 +1,5 @@
const { pathsToModuleNameMapper } = require('ts-jest/utils'); const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig.json'); const { compilerOptions } = require('./tsconfig.paths.json');
const { resolve } = require('path'); const { resolve } = require('path');
module.exports = { module.exports = {

View File

@ -4,6 +4,9 @@
"workspaces": [ "workspaces": [
"packages/*/*" "packages/*/*"
], ],
"engines": {
"node": ">=16.0.0"
},
"scripts": { "scripts": {
"nocobase": "nocobase", "nocobase": "nocobase",
"pm": "nocobase pm", "pm": "nocobase pm",

View File

@ -1,4 +1,5 @@
import path from 'path'; import path from 'path';
import { existsSync } from 'fs';
import { readdir } from 'fs/promises'; import { readdir } from 'fs/promises';
import { cloneDeep, isPlainObject } from 'lodash'; import { cloneDeep, isPlainObject } from 'lodash';
import { requireModule } from '@nocobase/utils'; import { requireModule } from '@nocobase/utils';
@ -20,6 +21,9 @@ export class ImporterReader {
} }
async read() { async read() {
if (!existsSync(this.directory)) {
return [];
}
const files = await readdir(this.directory, { const files = await readdir(this.directory, {
encoding: 'utf-8', encoding: 'utf-8',
}); });

View File

@ -63,7 +63,7 @@ function getNamespace() {
} }
function getTsconfigPaths() { 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); const json = JSON.parse(content);
return json.compilerOptions.paths; return json.compilerOptions.paths;
} }

View File

@ -9,7 +9,7 @@
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"devDependencies": { "devDependencies": {
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
"async-mutex": "^0.4.0", "async-mutex": "^0.3.2",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
{ {
"extends": "./tsconfig.paths.json",
"compilerOptions": { "compilerOptions": {
"esModuleInterop": true, "esModuleInterop": true,
"moduleResolution": "node", "moduleResolution": "node",
@ -16,17 +17,7 @@
"declaration": true, "declaration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"downlevelIteration": true, "downlevelIteration": true,
"baseUrl": ".", "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/*"]
}
}, },
"ts-node": { "ts-node": {
"compilerOptions": { "compilerOptions": {

View File

@ -1,5 +1,19 @@
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { defineConfig } from 'vitest/config'; 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({ export default defineConfig({
plugins: [react()], plugins: [react()],
@ -14,15 +28,8 @@ export default defineConfig({
threads: true, threads: true,
alias: [ alias: [
{ find: 'testUtils', replacement: 'testUtils.ts' }, { 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: /^~antd\/(.*)/, replacement: 'antd/$1' },
{ find: /^@nocobase\/app-(.*)/, replacement: 'packages/$1/src' }, ...alias,
{ 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' },
], ],
include: ['packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}'], include: ['packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}'],
exclude: ['**/node_modules/**', '**/dist/**', '**/lib/**', '**/es/**', '**/{vitest,commitlint}.config.*'], exclude: ['**/node_modules/**', '**/dist/**', '**/lib/**', '**/es/**', '**/{vitest,commitlint}.config.*'],

View File

@ -7569,12 +7569,6 @@ async-mutex@^0.3.2:
dependencies: dependencies:
tslib "^2.3.1" 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: async-ratelimiter@^1.3.0:
version "1.3.8" version "1.3.8"
resolved "https://registry.npmmirror.com/async-ratelimiter/-/async-ratelimiter-1.3.8.tgz#05198a322543de43d98807c96295a9d712306928" 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-iterator "^2.0.3"
es6-symbol "^3.1.1" 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: esbuild@0.12.15:
version "0.12.15" version "0.12.15"
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.12.15.tgz#9d99cf39aeb2188265c5983e983e236829f08af0" 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" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
postcss@^8.4.24: postcss@^8.4.27:
version "8.4.25" version "8.4.29"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd"
integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==
dependencies: dependencies:
nanoid "^3.3.6" nanoid "^3.3.6"
picocolors "^1.0.0" picocolors "^1.0.0"
@ -20678,9 +20680,10 @@ rollup@^3.20.2, rollup@^3.21.0:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
rollup@^3.25.2: rollup@^3.27.1:
version "3.26.2" version "3.29.1"
resolved "https://registry.npmmirror.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" resolved "https://registry.npmmirror.com/rollup/-/rollup-3.29.1.tgz#ba53a179d46ac3cd79e162dca6ab70d93cd26f78"
integrity sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
@ -23314,13 +23317,14 @@ vite@4.3.1:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
vite@^4.4.1: vite@^4.4.9:
version "4.4.1" version "4.4.9"
resolved "https://registry.npmmirror.com/vite/-/vite-4.4.1.tgz#cfe0baf6af4b1b9f3b37c5ebf1e012622be3da98" resolved "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d"
integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==
dependencies: dependencies:
esbuild "^0.18.10" esbuild "^0.18.10"
postcss "^8.4.24" postcss "^8.4.27"
rollup "^3.25.2" rollup "^3.27.1"
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"