diff --git a/.buildrc.ts b/.buildrc.ts index da97ca26e..ee5652a74 100644 --- a/.buildrc.ts +++ b/.buildrc.ts @@ -6,7 +6,6 @@ export default { 'core/cli', 'core/create-nocobase-app', 'core/devtools', - 'core/dumi-theme-nocobase', 'app/client', ], }; diff --git a/.dumi/global.less b/.dumi/global.less new file mode 100644 index 000000000..75f241ae0 --- /dev/null +++ b/.dumi/global.less @@ -0,0 +1 @@ +@import 'antd/dist/antd.css'; diff --git a/.dumi/theme/slots/LangSwitch/index.tsx b/.dumi/theme/slots/LangSwitch/index.tsx new file mode 100644 index 000000000..4111f9eb6 --- /dev/null +++ b/.dumi/theme/slots/LangSwitch/index.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +const LangSwitch = () => { + const { hostname } = window.location + if (hostname === 'localhost') return null; + const en = window.location.href.replace(hostname, 'docs.nocobase.com') + const cn = window.location.href.replace(hostname, 'docs-cn.nocobase.com') + return EN | 中文 +} + +export default LangSwitch; diff --git a/.dumi/theme/slots/Previewer/index.tsx b/.dumi/theme/slots/Previewer/index.tsx new file mode 100644 index 000000000..9ec63f2cc --- /dev/null +++ b/.dumi/theme/slots/Previewer/index.tsx @@ -0,0 +1,23 @@ +import React, { FC, useRef, useEffect, Suspense } from 'react'; +import { Root, createRoot } from 'react-dom/client'; +import { IPreviewerProps } from 'dumi'; +import DefaultPreviewer from 'dumi/theme-default/builtins/Previewer'; + +const Previewer: FC = ({ children, ...props }) => { + const ref = useRef(null); + useEffect(() => { + let root: Root + if (ref.current) { + root = createRoot(ref.current) + root.render(loading...}>{children}) + } + return () => { + if (root) { + root.unmount() + } + } + }, []); + return
; +}; + +export default Previewer; diff --git a/.dumirc.ts b/.dumirc.ts new file mode 100644 index 000000000..71453f595 --- /dev/null +++ b/.dumirc.ts @@ -0,0 +1,54 @@ +import { getUmiConfig } from '@nocobase/devtools/umiConfig'; +import { defineConfig } from 'dumi'; +import { defineThemeConfig } from 'dumi-theme-nocobase'; +import { nav, sidebar } from './docs/config'; + +const umiConfig = getUmiConfig(); + +const lang = process.env.DOC_LANG || 'en-US'; + +console.log('process.env.DOC_LANG', process.env.DOC_LANG); + +// 设置多语言的 title +function setTitle(menuChildren) { + if (!menuChildren) return; + menuChildren.forEach((item) => { + if (typeof item === 'object') { + item.title = item[`title.${lang}`] || item.title; + if (item.children) { + setTitle(item.children); + } + } + }); +} +if (lang !== 'en-US') { + Object.values(sidebar).forEach(setTitle); +} + +export default defineConfig({ + hash: true, + alias: { + ...umiConfig.alias, + }, + outputPath: `./docs/dist/${lang}`, + resolve: { + docDirs: [`./docs/${lang}`] + }, + locales: [ + { id: 'en-US', name: 'English' }, + { id: 'zh-CN', name: '中文' }, + ], + themeConfig: defineThemeConfig({ + title: 'NocoBase', + logo: 'https://www.nocobase.com/images/logo.png', + nav: nav.map((item) => ({ ...item, title: (item[`title.${lang}`] || item.title) })), + sidebarEnhance: sidebar as any, + github: 'https://github.com/nocobase/nocobase', + footer: 'nocobase | Copyright © 2022', + localesEnhance: [ + { id: 'zh-CN', switchPrefix: '中' }, + { id: 'en-US', switchPrefix: 'en' } + ], + }), + // mfsu: true, // 报错 +}); diff --git a/.eslintignore b/.eslintignore index 56c5551b7..aa5a3a110 100755 --- a/.eslintignore +++ b/.eslintignore @@ -18,6 +18,8 @@ packages/*/*/server.d.ts packages/*/*/client.js packages/*/*/server.js packages/core/build -packages/core/dumi-theme-nocobase packages/core/devtools -packages/core/database/src/sql-parser/index.js \ No newline at end of file +packages/core/database/src/sql-parser/index.js +**/.dumi/tmp +**/.dumi/tmp-test +**/.dumi/tmp-production diff --git a/.github/workflows/nocobase-test-backend.yml b/.github/workflows/nocobase-test-backend.yml index a54929bd5..53b405d34 100644 --- a/.github/workflows/nocobase-test-backend.yml +++ b/.github/workflows/nocobase-test-backend.yml @@ -40,6 +40,7 @@ jobs: - name: Test with Sqlite run: yarn nocobase install -f && yarn test env: + NODE_OPTIONS: "--max_old_space_size=4096" DB_DIALECT: sqlite DB_STORAGE: /tmp/db.sqlite DB_UNDERSCORED: ${{ matrix.underscored }} @@ -81,6 +82,7 @@ jobs: - name: Test with postgres run: yarn nocobase install -f && yarn test env: + NODE_OPTIONS: "--max_old_space_size=4096" DB_DIALECT: postgres DB_HOST: postgres DB_PORT: 5432 @@ -118,6 +120,7 @@ jobs: - name: Test with MySQL run: yarn nocobase install -f && yarn test env: + NODE_OPTIONS: "--max_old_space_size=4096" DB_DIALECT: mysql DB_HOST: mysql DB_PORT: 3306 diff --git a/.gitignore b/.gitignore index 28be725f0..1ce5dc9d4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,8 @@ dist/ docker/**/storage cache/diskstore-* *.nbdump -storage/duplicator/* \ No newline at end of file +storage/duplicator/* +**/.dumi/tmp +**/.dumi/tmp-test +**/.dumi/tmp-production +packages/core/client/docs/contributing.md diff --git a/.prettierignore b/.prettierignore index 5a7614395..2899bee96 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,3 +10,6 @@ packages/**/lib/** packages/**/esm/** packages/**/node_modules/** packages/core/client/src/locale/* +**/.dumi/tmp +**/.dumi/tmp-test +**/.dumi/tmp-production diff --git a/.umirc.ts b/.umirc.ts deleted file mode 100644 index 9a81d36d8..000000000 --- a/.umirc.ts +++ /dev/null @@ -1,130 +0,0 @@ -import transformer from '@umijs/preset-dumi/lib/transformer'; -import { defineConfig } from 'dumi'; -import fs from 'fs'; -import path from 'path'; -import menus from './docs/menus'; - -const lang = process.env.DOC_LANG || 'en-US'; - -console.log('process.env.DOC_LANG', process.env.DOC_LANG); - -const findFilePath = (filename, lang) => { - const filePath = path.resolve(__dirname, `docs/${lang}/${filename}`); - if (fs.existsSync(`${filePath}.md`)) { - return `${filePath}.md`; - } - return; -}; - -const markdown = (filename, lang) => { - const filePath = findFilePath(filename, lang); - if (!filePath) { - return; - } - return transformer.markdown(fs.readFileSync(filePath, 'utf8').toString(), filePath); -}; - -const getPath = (value: string) => { - if (!value) { - return ''; - } - const keys = value.split('/'); - if (keys.pop() === 'index') { - return keys.join('/') || '/'; - } - return value; -}; - -const getTitle = (item, lang) => { - if (lang) { - return item[`title.${lang}`] || item.title; - } - return item.title; -}; - -const parseMenuItems = (items: any[], lang: string) => { - const menuItems: any[] = []; - for (const item of items) { - if (typeof item === 'string') { - const result = markdown(item, lang); - if (result) { - menuItems.push({ - title: result.meta.title, - disabled: result.meta.disabled, - path: getPath(item), - }); - } - } else if (item.children) { - menuItems.push({ - ...item, - title: getTitle(item, lang), - children: parseMenuItems(item.children, lang), - }); - } else if (item.path) { - menuItems.push({ - ...item, - title: getTitle(item, lang), - path: getPath(item.path), - }); - } else { - menuItems.push({ - ...item, - title: getTitle(item, lang), - }); - } - } - return menuItems; -}; - -const navs = [ - { - title: 'Welcome', - 'title.zh-CN': '欢迎', - path: '/welcome', - }, - { - title: 'User manual', - 'title.zh-CN': '使用手册', - path: '/manual', - }, - { - title: 'Plugin Development', - 'title.zh-CN': '插件开发', - path: '/development', - }, - { - title: 'API reference', - 'title.zh-CN': 'API 参考', - path: '/api', - }, - { - title: 'Schema components', - 'title.zh-CN': 'Schema 组件库', - path: '/components', - }, - { - title: 'GitHub', - path: 'https://github.com/nocobase/nocobase', - }, -]; - -export default defineConfig({ - title: 'NocoBase', - outputPath: `./docs/dist/${lang}`, - mode: 'site', - resolve: { - includes: [`./docs/${lang}`], - }, - locales: [[lang, lang]], - hash: true, - logo: 'https://www.nocobase.com/images/logo.png', - navs: { - 'en-US': navs, - 'zh-CN': navs.map((item) => ({ ...item, title: item['title.zh-CN'] || item.title })), - }, - menus: Object.keys(menus).reduce((result, key) => { - const items = menus[key]; - result[key] = parseMenuItems(items, lang); - return result; - }, {}), -}); diff --git a/README.md b/README.md index a83ce2348..c35254822 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ English | [中文](./README.zh-CN.md) | [Türkçe](./README.tr-TR.md) NocoBase is in early stage of development and is subject to frequent changes, please use caution in production environments. +NocoBase v0.10 has been released, see [v0.10: update instructions](http://docs.nocobase.com/welcome/release/v10-changelog) for details. + ## We are hiring We are looking for full-time, remote product design and development colleagues to join the team. If you have a strong interest in NocoBase, please feel free to email us at hello@nocobase.com. diff --git a/README.tr-TR.md b/README.tr-TR.md index 4ee743a9e..01d17c903 100644 --- a/README.tr-TR.md +++ b/README.tr-TR.md @@ -2,10 +2,12 @@ ![](https://nocobase.oss-cn-beijing.aliyuncs.com/bbcedd403d31cd1ccc4e9709581f5c2f.png) -**Not:** 📌 +**Note:** 📌 NocoBase, geliştirmenin ilk aşamasındadır ve sık sık değişiklik yaparak geliştiriyoruz. Lütfen üretim ortamlarında dikkatli olun. +NocoBase v0.10 has been released, see [v0.10: update instructions](http://docs.nocobase.com/welcome/release/v10-changelog) for details. + ## Çalışma arkadaşımız olabilirsiniz Takıma katılacak tam zamanlı, uzaktan ürün tasarımı ve geliştirme arkadaşları arıyoruz. NocoBase'e güçlü bir ilginiz varsa, lütfen bize hello@nocobase.com adresinden e-posta göndermekten çekinmeyin. diff --git a/README.zh-CN.md b/README.zh-CN.md index 088b8073c..035ee4c56 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -6,6 +6,8 @@ NocoBase 正处在早期开发阶段,可能变动频繁,请谨慎用于生产环境。 +NocoBase v0.10 已发布,详情查看 [v0.10 更新说明](http://docs-cn.nocobase.com/welcome/release/v10-changelog)。 + ## 我们在招聘 我们在寻找全职、远程的产品设计、开发、测试的新同事加入团队,如果你对 NocoBase 有强烈的兴趣,欢迎给我们发邮件:hello@nocobase.com diff --git a/docs/menus.ts b/docs/config.ts similarity index 84% rename from docs/menus.ts rename to docs/config.ts index c7ea23514..47527f5b5 100644 --- a/docs/menus.ts +++ b/docs/config.ts @@ -1,4 +1,31 @@ -export default { +const nav = [ + { + title: 'Welcome', + 'title.zh-CN': '欢迎', + link: '/welcome/introduction', + }, + { + title: 'User manual', + 'title.zh-CN': '使用手册', + link: '/manual/quick-start/the-first-app', + }, + { + title: 'Plugin Development', + 'title.zh-CN': '插件开发', + link: '/development', + }, + { + title: 'API reference', + 'title.zh-CN': 'API 参考', + link: '/api', + }, + { + title: 'Schema components', + 'title.zh-CN': 'Schema 组件库', + link: '/components', + }, +]; +const sidebar = { '/welcome': [ { title: 'Welcome', @@ -6,7 +33,7 @@ export default { 'title.tr-TR': 'Hoşgeldiniz', type: 'group', children: [ - '/welcome/introduction/index', + '/welcome/introduction', '/welcome/introduction/features', '/welcome/introduction/when', // '/welcome/introduction/learning-guide', @@ -22,9 +49,8 @@ export default { title: 'Installation', 'title.zh-CN': '安装', 'title.TR-TR': 'Kurulum', - type: 'subMenu', children: [ - '/welcome/getting-started/installation/index', + '/welcome/getting-started/installation', '/welcome/getting-started/installation/docker-compose', '/welcome/getting-started/installation/create-nocobase-app', '/welcome/getting-started/installation/git-clone', @@ -34,9 +60,8 @@ export default { title: 'Upgrading', 'title.zh-CN': '升级', 'title.TR-TR': 'Güncelleme', - type: 'subMenu', children: [ - '/welcome/getting-started/upgrading/index', + '/welcome/getting-started/upgrading', '/welcome/getting-started/upgrading/docker-compose', '/welcome/getting-started/upgrading/create-nocobase-app', '/welcome/getting-started/upgrading/git-clone', @@ -58,7 +83,8 @@ export default { link: 'https://github.com/nocobase/nocobase/blob/main/CHANGELOG.md', }, // '/welcome/release/index', - '/welcome/release/v08-changelog', + // '/welcome/release/v08-changelog', + '/welcome/release/v10-changelog', ], }, { @@ -117,7 +143,7 @@ export default { 'title.TR-TR': 'Başlarken', type: 'group', children: [ - '/development/index', + '/development', '/development/your-fisrt-plugin', '/development/app-ds', '/development/plugin-ds', @@ -131,14 +157,13 @@ export default { 'title.TR-TR': 'Sunucu', type: 'group', children: [ - '/development/server/index', + '/development/server', { title: 'Collections & Fields', 'title.zh-CN': '数据表和字段', 'title.TR-TR': 'Koleksiyonlar & Alanlar', - type: 'subMenu', children: [ - '/development/server/collections/index', + '/development/server/collections', '/development/server/collections/options', '/development/server/collections/configure', '/development/server/collections/association-fields', @@ -162,14 +187,13 @@ export default { 'title.TR-TR': 'Ziyaretçi(Client)', type: 'group', children: [ - '/development/client/index', + '/development/client', { title: 'UI 设计器', 'title.zh-CN': 'UI 设计器', 'title.TR-TR': 'Kullanıcı Arayüz Tasarımcısı', - type: 'subMenu', children: [ - // '/development/client/ui-schema-designer/index', + // '/development/client/ui-schema-designer', '/development/client/ui-schema-designer/what-is-ui-schema', '/development/client/ui-schema-designer/extending-schema-components', // '/development/client/ui-schema-designer/insert-adjacent', @@ -189,12 +213,12 @@ export default { }, ], '/api': [ - '/api/index', + '/api', '/api/env', { title: 'HTTP API', type: 'subMenu', - children: ['/api/http/index', '/api/http/rest-api'], + children: ['/api/http', '/api/http/rest-api'], }, { title: '@nocobase/server', @@ -209,7 +233,7 @@ export default { title: '@nocobase/database', type: 'subMenu', children: [ - '/api/database/index', + '/api/database', '/api/database/collection', '/api/database/field', '/api/database/repository', @@ -223,31 +247,18 @@ export default { { title: '@nocobase/resourcer', type: 'subMenu', - children: [ - '/api/resourcer/index', - '/api/resourcer/resource', - '/api/resourcer/action', - '/api/resourcer/middleware', - ], + children: ['/api/resourcer', '/api/resourcer/resource', '/api/resourcer/action', '/api/resourcer/middleware'], }, { title: '@nocobase/acl', type: 'subMenu', - children: [ - '/api/acl/index', - '/api/acl/acl', - '/api/acl/acl-role', - '/api/acl/acl-resource', - '/api/acl/acl-available-action', - '/api/acl/acl-available-strategy', - '/api/acl/allow-manager', - ], + children: ['/api/acl/acl', '/api/acl/acl-role', '/api/acl/acl-resource'], }, { title: '@nocobase/client', type: 'subMenu', children: [ - // '/api/client/index', + // '/api/client', '/api/client/application', '/api/client/route-switch', { @@ -255,7 +266,6 @@ export default { 'title.zh-CN': 'SchemaDesigner', 'title.TR-TR': 'Şema Tasarımcısı', - type: 'subMenu', children: [ '/api/client/schema-designer/schema-component', '/api/client/schema-designer/schema-initializer', @@ -266,7 +276,6 @@ export default { title: 'Extensions', 'title.zh-CN': 'Extensions', 'title.TR-TR': 'Eklentiler', - type: 'subMenu', children: [ // '/api/client/extensions/schema-component', '/api/client/extensions/collection-manager', @@ -276,17 +285,24 @@ export default { }, ], }, + '/api/cli', + '/api/actions', + '/api/sdk', { title: '@nocobase/cli', path: '/api/cli', + type: 'item', }, { title: '@nocobase/actions', path: '/api/actions', + type: 'item', }, { title: '@nocobase/sdk', path: '/api/sdk', + type: 'item', }, ], }; +export { nav, sidebar }; diff --git a/docs/en-US/api/client/route-switch.md b/docs/en-US/api/client/route-switch.md index 6a5dffddc..8af0f744a 100644 --- a/docs/en-US/api/client/route-switch.md +++ b/docs/en-US/api/client/route-switch.md @@ -23,8 +23,6 @@ interface RedirectProps { type: 'redirect'; to: any; path?: string; - exact?: boolean; - strict?: boolean; push?: boolean; from?: string; [key: string]: any; @@ -33,8 +31,6 @@ interface RedirectProps { interface RouteProps { type: 'route'; path?: string | string[]; - exact?: boolean; - strict?: boolean; sensitive?: boolean; component?: any; routes?: RouteProps[]; @@ -56,7 +52,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { diff --git a/docs/en-US/components.md b/docs/en-US/components.md new file mode 100644 index 000000000..0eac4eb52 --- /dev/null +++ b/docs/en-US/components.md @@ -0,0 +1 @@ +# Components diff --git a/docs/en-US/development/client/test.md b/docs/en-US/development/client/test.md index d8d19e163..a6c082f9f 100644 --- a/docs/en-US/development/client/test.md +++ b/docs/en-US/development/client/test.md @@ -25,7 +25,6 @@ describe('route-switch', () => { { type: 'route', path: '/', - exact: true, component: 'Home', }, ]} diff --git a/docs/en-US/development/client/ui-router.md b/docs/en-US/development/client/ui-router.md index c167293cd..bd27b1994 100644 --- a/docs/en-US/development/client/ui-router.md +++ b/docs/en-US/development/client/ui-router.md @@ -17,7 +17,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { diff --git a/docs/en-US/development/client/ui-schema-designer/index.md b/docs/en-US/development/client/ui-schema-designer/index.md index 102eb7c9a..bd5b6fdf7 100644 --- a/docs/en-US/development/client/ui-schema-designer/index.md +++ b/docs/en-US/development/client/ui-schema-designer/index.md @@ -1,3 +1,3 @@ # Overview - \ No newline at end of file + diff --git a/docs/en-US/index.md b/docs/en-US/index.md index ab11f4321..49a1e7610 100644 --- a/docs/en-US/index.md +++ b/docs/en-US/index.md @@ -1 +1,3 @@ - \ No newline at end of file +# Index + + diff --git a/docs/en-US/welcome/community/contributing.md b/docs/en-US/welcome/community/contributing.md index 547c2cdab..9f72dff8a 100644 --- a/docs/en-US/welcome/community/contributing.md +++ b/docs/en-US/welcome/community/contributing.md @@ -5,7 +5,7 @@ - Submit pull request - Sign the CLA -## Download +## Download ```bash # Replace the following git address with your own repo @@ -45,4 +45,4 @@ The documentation is in the docs directory and follows Markdown syntax ## Others -For more CLI instructions, please [refer to the NocoBase CLI chapter](./development/nocobase-cli.md) +For more CLI instructions, please [refer to the NocoBase CLI chapter](https://docs-cn.nocobase.com/api/cli). diff --git a/docs/en-US/welcome/getting-started/upgrading/git-clone.md b/docs/en-US/welcome/getting-started/upgrading/git-clone.md index 0673c99a3..3eb0aa027 100644 --- a/docs/en-US/welcome/getting-started/upgrading/git-clone.md +++ b/docs/en-US/welcome/getting-started/upgrading/git-clone.md @@ -12,19 +12,32 @@ cd my-nocobase-app git pull ``` -## 3. Update dependencies +## 3. 删除旧依赖文件(非必须) + +v0.10 进行了依赖的重大升级,如果 v0.9 升级 v0.10,需要删掉以下目录之后再升级 + +```bash +# 删除 .umi 相关缓存 +yarn rimraf -rf ./**/{.umi,.umi-production} +# 删除编译文件 +yarn rimraf -rf packages/*/*/{lib,esm,es,dist,node_modules} +# 删除全部依赖 +yarn rimraf -rf node_modules +``` + +## 4. Update dependencies ```bash yarn install ``` -## 4. Execute the update command +## 5. Execute the update command ```bash yarn nocobase upgrade ``` -## 5. Start NocoBase +## 6. Start NocoBase development environment @@ -41,4 +54,3 @@ yarn build # Start yarn start # Not supported on Windows platforms yet ``` - diff --git a/docs/en-US/welcome/getting-started/upgrading/index.md b/docs/en-US/welcome/getting-started/upgrading/index.md index f04985368..30aed2c8d 100644 --- a/docs/en-US/welcome/getting-started/upgrading/index.md +++ b/docs/en-US/welcome/getting-started/upgrading/index.md @@ -2,6 +2,6 @@ NocoBase supports three types of installation, with slight differences in upgrades. -- [Upgrading for Docker compose](./upgrading/docker-compose.md) -- [Upgrading for create-nocobase-app](./upgrading/create-nocobase-app.md) -- [Upgrading for Git source code](./upgrading/git-clone.md) +- [Upgrading for Docker compose](./docker-compose.md) +- [Upgrading for create-nocobase-app](./create-nocobase-app.md) +- [Upgrading for Git source code](./git-clone.md) diff --git a/docs/en-US/welcome/release/v10-changelog.md b/docs/en-US/welcome/release/v10-changelog.md new file mode 100644 index 000000000..2dbbcebb1 --- /dev/null +++ b/docs/en-US/welcome/release/v10-changelog.md @@ -0,0 +1,233 @@ +# v0.10: Update instructions + +## New features in the second quarter + +- Association field component improvements, support for multiple component switching + - Select + - Record picker + - Sub-form/Sub-details + - Sub-table + - File manager + - Title(read only) +- Quick creation of relational data, supports two quick creation modes + - Add in drop-down menu to quickly create a new record based on the title field + - Add in pop-up window to configure complex add forms +- Duplicate action, supports two modes + - Direct duplicate + - Copy into the form and continue to fill in +- Form data templates +- Filter data scope support variables +- List block +- Grid card block +- Mobile client plugin +- User authentication plugin, support for different authentication methods + - Email/Password + - SMS + - OIDC + - SAML +- Workflow nodes + - Manual node upgrade, support for adding and editing from existing collections + - Loop node + - Aggregate node +- File manager + - Provide file collection template + - Provide file manager component + +## Upgrading applications + +### Upgrading for Docker compose + +No change, upgrade reference [Upgrading for Docker compose](/welcome/getting-started/upgrading/docker-compose) + +### Upgrading for Git source code + +v0.10 has a major upgrade of dependencies, so to prevent errors when upgrading the source code, you need to delete the following directories before upgrading + +```bash +### Remove .umi-related cache +yarn rimraf -rf "./**/{.umi,.umi-production}" +# Delete compiled files +yarn rimraf -rf "./packages/*/*/{lib,esm,es,dist,node_modules}" +# Remove dependencies +yarn rimraf -rf node_modules +``` + +See [Upgrading for Git source code](/welcome/getting-started/upgrading/git-clone) for more details + +### Upgrading for create-nocobase-app + +It is recommended that `yarn create` re-download the new version and modify the `.env` configuration, for more details refer to [major version upgrade guide](/welcome/getting-started/upgrading/create-nocobase-app#major-upgrade) + +## Upcoming deprecated and potentially incompatible changes + +### Sub-table field component + +Not compatible with new version, block fields need to be removed and reassigned (UI reassignment only) + +### Attachment upload api changes + +In addition to the built-in attachments table, users can also custom file collection, the upload api for attachments has been changed from `/api/attachments:upload` to `/api/:create`, upload is deprecated, still compatible with v0.10 but will be Removed. + +### signin/signup api changes + +The nocobase kernel provides a more powerful [auth module](https://github.com/nocobase/nocobase/tree/main/packages/plugins/auth) with the following changes to the user login, registration, verification, and logout apis: + +```bash +/api/users:signin -> /api/auth:signIn +/api/users:signup -> /api/auth:signUp +/api/users:signout -> /api/auth:signOut +/api/users:check -> /api/auth:check +``` + +Note: The above users interface, which is deprecated, is still compatible with v0.10, but will be removed in the next major release. + +### Adjustments to date field filtering + +If date related filtering was previously configured in the data range, it needs to be deleted and reconfigured. + +## Third-party plugin upgrade guide + +### Dependencies upgrade + +dependencies mainly including + +- `react` upgrade to v18 +- `react-dom` upgrade to v18 +- `react-router` upgrade to v6.11 +- `umi` upgrade to v4 +- `dumi` upgrade to v2 + +The `package.json` dependencies should be changed to the latest version, e.g: + +```diff +{ + "devDependencies": { ++ "react": "^18". ++ "react-dom": "^18". ++ "react-router-dom": "^6.11.2". +- "react": "^17". +- "react-dom": "^17". +- "react-router-dom": "^5". + } +} +``` + +### Code changes + +Because react-router has been upgraded, the related code also needs to be changed, the main changes include + +### Layout Component + +Layout component needs to use `` instead of `props.children`. + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function Layout(props) { + return ( +
+- { props.children } ++ +
+ ); +} +``` + +if you use `React.cloneElement` to render the route component, you need to change it like this: + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function RouteComponent(props) { + return ( +
+- { React.cloneElement(props.children, { someProp: 'p1' }) } ++ +
+ ); +} +``` + +Change the route component to get the value from `useOutletContext` + +```diff +import React from 'react'; ++ import { useOutletContext } from 'react-router-dom'; + +- export function Comp(props){ ++ export function Comp() { ++ const props = useOutletContext(); + return props.someProp; +} +``` + +### Redirect + +`` is changed to ``. + +```diff +- ++ +``` + +### useHistory + +`useNavigate` is changed to `useHistory`. + +```diff +- import { useHistory } from 'react-router-dom'; ++ import { useNavigate} from 'react-router-dom'; + +- const history = useHistory(); ++ const navigate = useNavigate(); + +- history.push('/about') ++ navigate('/about') + +- history.replace('/about') ++ navigate('/about', { replace: true }) +``` + +### useLocation + +`useLocation()` is changed to `useLocation`. + +```diff +- const location= useLocation(); ++ const location= useLocation(); +``` + +`const { query } = useLocation()` is changed to `useSearchParams()`。 + +```diff +- const location = useLocation(); +- const query = location.query; +- const name = query.name; ++ const [searchParams, setSearchParams] = useSearchParams(); ++ searchParams.get('name'); +``` + +### path + +All of the following are valid route paths in v6: + +``` +/groups +/groups/admin +/users/:id +/users/:id/messages +/files/* +/files/:id/* +``` + +The following RegExp-style route paths are not valid in v6: + +``` +/tweets/:id(\d+) +/files/*/cat.jpg +/files-* +``` + +For more api changes, please refer to [react-router@6](https://reactrouter.com/en/main/upgrading/v5)。 diff --git a/docs/tr-TR/api/client/route-switch.md b/docs/tr-TR/api/client/route-switch.md index 3aacb7083..6739d10a1 100644 --- a/docs/tr-TR/api/client/route-switch.md +++ b/docs/tr-TR/api/client/route-switch.md @@ -23,8 +23,6 @@ interface RedirectProps { type: 'redirect'; to: any; path?: string; - exact?: boolean; - strict?: boolean; push?: boolean; from?: string; [key: string]: any; @@ -33,8 +31,6 @@ interface RedirectProps { interface RouteProps { type: 'route'; path?: string | string[]; - exact?: boolean; - strict?: boolean; sensitive?: boolean; component?: any; routes?: RouteProps[]; @@ -56,7 +52,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { @@ -76,4 +71,4 @@ export default () => { ); }; -``` \ No newline at end of file +``` diff --git a/docs/tr-TR/components.md b/docs/tr-TR/components.md new file mode 100644 index 000000000..0eac4eb52 --- /dev/null +++ b/docs/tr-TR/components.md @@ -0,0 +1 @@ +# Components diff --git a/docs/tr-TR/development/client/test.md b/docs/tr-TR/development/client/test.md index d8d19e163..a6c082f9f 100644 --- a/docs/tr-TR/development/client/test.md +++ b/docs/tr-TR/development/client/test.md @@ -25,7 +25,6 @@ describe('route-switch', () => { { type: 'route', path: '/', - exact: true, component: 'Home', }, ]} diff --git a/docs/tr-TR/development/client/ui-router.md b/docs/tr-TR/development/client/ui-router.md index c167293cd..bd27b1994 100644 --- a/docs/tr-TR/development/client/ui-router.md +++ b/docs/tr-TR/development/client/ui-router.md @@ -17,7 +17,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { diff --git a/docs/tr-TR/development/client/ui-schema-designer/index.md b/docs/tr-TR/development/client/ui-schema-designer/index.md index 102eb7c9a..bd5b6fdf7 100644 --- a/docs/tr-TR/development/client/ui-schema-designer/index.md +++ b/docs/tr-TR/development/client/ui-schema-designer/index.md @@ -1,3 +1,3 @@ # Overview - \ No newline at end of file + diff --git a/docs/tr-TR/index.md b/docs/tr-TR/index.md index ab11f4321..49a1e7610 100644 --- a/docs/tr-TR/index.md +++ b/docs/tr-TR/index.md @@ -1 +1,3 @@ - \ No newline at end of file +# Index + + diff --git a/docs/tr-TR/welcome/community/contributing.md b/docs/tr-TR/welcome/community/contributing.md index beb193751..13e065817 100644 --- a/docs/tr-TR/welcome/community/contributing.md +++ b/docs/tr-TR/welcome/community/contributing.md @@ -5,7 +5,7 @@ - Çekme isteği(pull request) gönderin - CLA'yı imzalayın -## İndir +## İndir ```bash # Aşağıdaki git adresini kendi deponuzla değiştirin @@ -47,4 +47,4 @@ Belgeler docs dizinindedir ve Markdown sözdizimini takip eder ## Diğerleri -Daha fazla CLI talimatı için lütfen [NocoBase CLI bölümüne bakın](./development/nocobase-cli.md) +Daha fazla CLI talimatı için lütfen [NocoBase CLI bölümüne bakın](https://docs-cn.nocobase.com/api/cli). diff --git a/docs/tr-TR/welcome/getting-started/upgrading/index.md b/docs/tr-TR/welcome/getting-started/upgrading/index.md index 3b022ccb6..3bcfb5f91 100644 --- a/docs/tr-TR/welcome/getting-started/upgrading/index.md +++ b/docs/tr-TR/welcome/getting-started/upgrading/index.md @@ -2,6 +2,6 @@ NocoBase, yükseltmelerde küçük farklılıklarla üç tür yüklemeyi destekler. -- [Docker için Güncelleme](./upgrading/docker-compose.md) -- [create-nocobase-app için Güncelleme](./upgrading/create-nocobase-app.md) -- [Git Kaynak koduyla Güncelleme](./upgrading/git-clone.md) +- [Docker için Güncelleme](./docker-compose.md) +- [create-nocobase-app için Güncelleme](./create-nocobase-app.md) +- [Git Kaynak koduyla Güncelleme](./git-clone.md) diff --git a/docs/tr-TR/welcome/release/v10-changelog.md b/docs/tr-TR/welcome/release/v10-changelog.md new file mode 100644 index 000000000..b5ff1d43d --- /dev/null +++ b/docs/tr-TR/welcome/release/v10-changelog.md @@ -0,0 +1,156 @@ +# v0.10:update instructions + +v0.10 has made major upgrades to dependencies, including `react`, `react-dom`, `react-router`, `umi`, and `dumi`. + +## 依赖升级 + +- Upgrade `react@^17`, `react-dom@^17` to `react@^18`, `react-dom@^18` version + +```diff +{ + "devDependencies": { ++ "react": "^18", ++ "react-dom": "^18", +- "react": "^17", +- "react-dom": "^17", + } +} +``` + +- Upgrade `react-router@5` to `react-router@6` + +```diff +{ + "devDependencies": { ++ "react-router-dom": "^6.11.2", +- "react-router-dom": "^5", + } +} +``` + +## `react-router` change description + +because the upgrade of `react-router` requires some code changes. + +### Layout Component + +Layout component needs to use `` instead of `props.children`. + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function Layout(props) { + return ( +
+- { props.children } ++ +
+ ); +} +``` + +if you use `React.cloneElement` to render the route component, you need to change it like this: + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function RouteComponent(props) { + return ( +
+- { React.cloneElement(props.children, { someProp: 'p1' }) } ++ +
+ ); +} +``` + +Change the route component to get the value from `useOutletContext` + +```diff +import React from 'react'; ++ import { useOutletContext } from 'react-router-dom'; + +- export function Comp(props){ ++ export function Comp() { ++ const props = useOutletContext(); + return props.someProp; +} +``` + +### Redirect + +`` is changed to ``. + +```diff +- ++ +``` + +### useHistory + +`useNavigate` is changed to `useHistory`. + +```diff +- import { useHistory } from 'react-router-dom'; ++ import { useNavigate} from 'react-router-dom'; + +- const history = useHistory(); ++ const navigate = useNavigate(); + +- history.push('/about') ++ navigate('/about') + +- history.replace('/about') ++ navigate('/about', { replace: true }) +``` + +### useLocation + +`useLocation()` is changed to `useLocation`. + +```diff +- const location= useLocation(); ++ const location= useLocation(); +``` + +`const { query } = useLocation()` is changed to `useSearchParams()`。 + +```diff +- const location = useLocation(); +- const query = location.query; +- const name = query.name; ++ const [searchParams, setSearchParams] = useSearchParams(); ++ searchParams.get('name'); +``` + +### path + +All of the following are valid route paths in v6: + +``` +/groups +/groups/admin +/users/:id +/users/:id/messages +/files/* +/files/:id/* +``` + +The following RegExp-style route paths are not valid in v6: + +``` +/tweets/:id(\d+) +/files/*/cat.jpg +/files-* +``` + +For more changes and api changes, please refer to [react-router@6](https://reactrouter.com/en/main/upgrading/v5)。 + +## Docs Demo + +```diff +- ++ +``` diff --git a/docs/zh-CN/api/actions.md b/docs/zh-CN/api/actions.md index c752fc74e..0aa20b605 100644 --- a/docs/zh-CN/api/actions.md +++ b/docs/zh-CN/api/actions.md @@ -1,4 +1,4 @@ -# 内置常用资源操作 +# @nocobase/actions ## 概览 diff --git a/docs/zh-CN/api/cli.md b/docs/zh-CN/api/cli.md index a073396c6..5464c4361 100644 --- a/docs/zh-CN/api/cli.md +++ b/docs/zh-CN/api/cli.md @@ -1,4 +1,4 @@ -# NocoBase CLI +# @nocobase/cli NocoBase CLI 旨在帮助你开发、构建和部署 NocoBase 应用。 diff --git a/docs/zh-CN/api/client/route-switch.md b/docs/zh-CN/api/client/route-switch.md index 3aacb7083..6739d10a1 100644 --- a/docs/zh-CN/api/client/route-switch.md +++ b/docs/zh-CN/api/client/route-switch.md @@ -23,8 +23,6 @@ interface RedirectProps { type: 'redirect'; to: any; path?: string; - exact?: boolean; - strict?: boolean; push?: boolean; from?: string; [key: string]: any; @@ -33,8 +31,6 @@ interface RedirectProps { interface RouteProps { type: 'route'; path?: string | string[]; - exact?: boolean; - strict?: boolean; sensitive?: boolean; component?: any; routes?: RouteProps[]; @@ -56,7 +52,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { @@ -76,4 +71,4 @@ export default () => { ); }; -``` \ No newline at end of file +``` diff --git a/docs/zh-CN/components.md b/docs/zh-CN/components.md new file mode 100644 index 000000000..0eac4eb52 --- /dev/null +++ b/docs/zh-CN/components.md @@ -0,0 +1 @@ +# Components diff --git a/docs/zh-CN/development/client/test.md b/docs/zh-CN/development/client/test.md index 452ea9f46..1af06a831 100644 --- a/docs/zh-CN/development/client/test.md +++ b/docs/zh-CN/development/client/test.md @@ -25,7 +25,6 @@ describe('route-switch', () => { { type: 'route', path: '/', - exact: true, component: 'Home', }, ]} @@ -38,4 +37,4 @@ describe('route-switch', () => { expect(container).toMatchSnapshot(); }); }); -``` \ No newline at end of file +``` diff --git a/docs/zh-CN/development/client/ui-router.md b/docs/zh-CN/development/client/ui-router.md index 5a620942b..a4c40946e 100644 --- a/docs/zh-CN/development/client/ui-router.md +++ b/docs/zh-CN/development/client/ui-router.md @@ -17,7 +17,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { @@ -60,4 +59,4 @@ export default React.memo((props) => { }); ``` -完整示例查看 [packages/samples/custom-page](https://github.com/nocobase/nocobase/tree/develop/packages/samples/custom-page) \ No newline at end of file +完整示例查看 [packages/samples/custom-page](https://github.com/nocobase/nocobase/tree/develop/packages/samples/custom-page) diff --git a/docs/zh-CN/development/client/ui-schema-designer/index.md b/docs/zh-CN/development/client/ui-schema-designer/index.md index 102eb7c9a..bd5b6fdf7 100644 --- a/docs/zh-CN/development/client/ui-schema-designer/index.md +++ b/docs/zh-CN/development/client/ui-schema-designer/index.md @@ -1,3 +1,3 @@ # Overview - \ No newline at end of file + diff --git a/docs/zh-CN/index.md b/docs/zh-CN/index.md index ab11f4321..49a1e7610 100644 --- a/docs/zh-CN/index.md +++ b/docs/zh-CN/index.md @@ -1 +1,3 @@ - \ No newline at end of file +# Index + + diff --git a/docs/zh-CN/welcome/community/contributing.md b/docs/zh-CN/welcome/community/contributing.md index c34b7d6dc..76c982967 100644 --- a/docs/zh-CN/welcome/community/contributing.md +++ b/docs/zh-CN/welcome/community/contributing.md @@ -45,4 +45,4 @@ yarn doc --lang=en-US ## 其他 -更多 Commands 使用说明 [参考 NocoBase CLI 章节](./development/nocobase-cli.md) +更多 Commands 使用说明 [参考 NocoBase CLI 章节](https://docs-cn.nocobase.com/api/cli)。 diff --git a/docs/zh-CN/welcome/getting-started/installation/index.md b/docs/zh-CN/welcome/getting-started/installation/index.md index 1a5e732a2..6940b17b5 100644 --- a/docs/zh-CN/welcome/getting-started/installation/index.md +++ b/docs/zh-CN/welcome/getting-started/installation/index.md @@ -4,9 +4,9 @@ NocoBase 支持三种安装方式: -- [Docker 安装(推荐)](./installation/docker-compose.md) -- [create-nocobase-app 安装](./installation/create-nocobase-app.md) -- [Git 源码安装](./installation/git-clone.md) +- [Docker 安装(推荐)](./docker-compose.md) +- [create-nocobase-app 安装](./create-nocobase-app.md) +- [Git 源码安装](./git-clone.md) ## 如何选择 diff --git a/docs/zh-CN/welcome/getting-started/upgrading/git-clone.md b/docs/zh-CN/welcome/getting-started/upgrading/git-clone.md index 79285aa43..093a15ea6 100644 --- a/docs/zh-CN/welcome/getting-started/upgrading/git-clone.md +++ b/docs/zh-CN/welcome/getting-started/upgrading/git-clone.md @@ -12,19 +12,32 @@ cd my-nocobase-app git pull ``` -## 3. 更新依赖 +## 3. 删除旧依赖文件(非必须) +v0.10 进行了依赖的重大升级,如果 v0.9 升级 v0.10,需要删掉以下目录之后再升级 + +```bash +# 删除 .umi 相关缓存 +yarn rimraf -rf ./**/{.umi,.umi-production} +# 删除编译文件 +yarn rimraf -rf packages/*/*/{lib,esm,es,dist,node_modules} +# 删除全部依赖 +yarn rimraf -rf node_modules ``` + +## 4. 更新依赖 + +```bash yarn install ``` -## 4. 执行更新命令 +## 5. 执行更新命令 ```bash yarn nocobase upgrade ``` -## 5. 启动 NocoBase +## 6. 启动 NocoBase 开发环境 diff --git a/docs/zh-CN/welcome/getting-started/upgrading/index.md b/docs/zh-CN/welcome/getting-started/upgrading/index.md index c71ad7416..b914a5ebc 100644 --- a/docs/zh-CN/welcome/getting-started/upgrading/index.md +++ b/docs/zh-CN/welcome/getting-started/upgrading/index.md @@ -2,6 +2,6 @@ NocoBase 支持三种安装方式,升级时略有不同。 -- [Docker 安装的升级](./upgrading/docker-compose.md) -- [create-nocobase-app 安装的升级](./upgrading/create-nocobase-app.md) -- [Git 源码安装的升级](./upgrading/git-clone.md) +- [Docker 安装的升级](./docker-compose.md) +- [create-nocobase-app 安装的升级](./create-nocobase-app.md) +- [Git 源码安装的升级](./git-clone.md) diff --git a/docs/zh-CN/welcome/release/v10-changelog.md b/docs/zh-CN/welcome/release/v10-changelog.md new file mode 100644 index 000000000..5dc68bc6c --- /dev/null +++ b/docs/zh-CN/welcome/release/v10-changelog.md @@ -0,0 +1,232 @@ +# v0.10:更新说明 + +## 第二季度的新特性 + +- 关系字段组件改进,支持多种组件切换 + - 下拉选择器 + - 数据选择器 + - 子表单/子详情 + - 子表格 + - 文件管理器 + - 标题(仅阅读模式) +- 快捷创建关系数据,支持两种快捷创建模式 + - 下拉菜单里添加,基于标题字段快速创建一条新纪录 + - 弹窗里添加,可以配置复杂的添加表单 +- 复制操作,支持两种复制模式 + - 直接复制 + - 复制到表单里并继续填写 +- 表单数据模板 +- 筛选数据范围支持变量 +- 列表区块 +- 网格卡片区块 +- 移动端插件 +- 用户认证插件,支持不同的登录方式 + - Email/Password + - SMS + - OIDC + - SAML +- 工作流新增节点 + - 人工节点升级,支持从现有数据表里新增和编辑 + - 循环节点 + - 聚合查询节点 +- 文件管理器 + - 提供文件表模板 + - 提供文件管理器组件 + +## 应用升级 + +### Docker 安装的升级 + +无变化,升级参考 [Docker 镜像升级指南](/welcome/getting-started/upgrading/docker-compose) + +### 源码安装的升级 + +v0.10 进行了依赖的重大升级,源码升级时,以防出错,需要删掉以下目录之后再升级 + +```bash +# 删除 .umi 相关缓存 +yarn rimraf -rf "./**/{.umi,.umi-production}" +# 删除编译文件 +yarn rimraf -rf "packages/*/*/{lib,esm,es,dist,node_modules}" +# 删除依赖 +yarn rimraf -rf node_modules +``` + +更多详情参考 [Git 源码升级指南](/welcome/getting-started/upgrading/git-clone) + +### create-nocobase-app 安装的升级 + +建议 `yarn create` 重新下载新版本,再更新 .env 配置,更多详情参考 [大版本升级指南](/welcome/getting-started/upgrading/create-nocobase-app#大版本升级) + +## 即将遗弃和可能不兼容的变化 + +### 子表格字段组件 + +不兼容新版,区块字段需要删除重配(只需要 UI 重配) + +### 附件上传接口的变更 + +除了内置的 attachments 表以外,用户也可以自定义文件表,附件的上传接口由 `/api/attachments:upload` 变更为 `/api/:create`,upload 已废弃,依旧兼容 v0.10,但会在下个大版本里移除。 + +### 登录、注册接口的变更 + +nocobase 内核提供了更强大的 [auth 模块](https://github.com/nocobase/nocobase/tree/main/packages/plugins/auth),用户登录、注册、校验、注销接口变更如下: + +```bash +/api/users:signin -> /api/auth:signIn +/api/users:signup -> /api/auth:signUp +/api/users:signout -> /api/auth:signOut +/api/users:check -> /api/auth:check +``` + +注:以上 users 接口,已废弃,依旧兼容 v0.10,但会在下个大版本里移除。 + +### 日期字段筛选的调整 + +如果之前数据范围里配置了日期相关筛选,需要删掉重新配置。 + +## 第三方插件升级指南 + +### 依赖升级 + +v0.10 依赖升级,主要包括 + +- `react` 升级到 v18 +- `react-dom` 升级到 v18 +- `react-router` 升级到 v6.11 +- `umi` 升级到 v4 +- `dumi` 升级到 v2 + +插件的 `package.json` 相关依赖要更改为最新版,如: + +```diff +{ + "devDependencies": { ++ "react": "^18". ++ "react-dom": "^18". ++ "react-router-dom": "^6.11.2". +- "react": "^17". +- "react-dom": "^17". +- "react-router-dom": "^5". + } +} +``` + +### 代码修改 + +由于 react-router 的升级,代码层面也需要改动,主要变更包括 + +#### Layout 布局组件 + +Layout 布局组件需要使用 `` 代替 `props.children`。 + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function Layout(props) { + return ( +
+- { props.children } ++ +
+ ); +} +``` + +使用了 `React.cloneElement` 方式渲染的路由组件改造,示例: + +```diff +import React from 'react'; ++ import { Outlet } from 'react-router-dom'; + +export default function RouteComponent(props) { + return ( +
+- { React.cloneElement(props.children, { someProp: 'p1' }) } ++ +
+ ); +} +``` + +组件改成从 `useOutletContext` 取值 + +```diff +import React from 'react'; ++ import { useOutletContext } from 'react-router-dom'; + +- export function Comp(props){ ++ export function Comp() { ++ const props = useOutletContext(); + return props.someProp; +} +``` + +#### Redirect + +`` 转为 ``。 + +```diff +- ++ +``` + +#### useHistory + +`useNavigate` 代替 `useHistory`。 + +```diff +- import { useHistory } from 'react-router-dom'; ++ import { useNavigate} from 'react-router-dom'; + +- const history = useHistory(); ++ const navigate = useNavigate(); + +- history.push('/about') ++ navigate('/about') + +- history.replace('/about') ++ navigate('/about', { replace: true }) +``` + +#### useLocation + +`useLocation()` 改为 `useLocation`。 + +```diff +- const location= useLocation(); ++ const location= useLocation(); +``` + +`const { query } = useLocation()` 改为 `useSearchParams()`。 + +```diff +- const location = useLocation(); +- const query = location.query; +- const name = query.name; ++ const [searchParams, setSearchParams] = useSearchParams(); ++ searchParams.get('name'); +``` + +#### path + +支持下面的 `path` 方式 + +``` +/groups +/groups/admin +/users/:id +/users/:id/messages +/files/* +/files/:id/* +``` + +不再支持如下方式 +``` +/tweets/:id(\d+) +/files/*/cat.jpg +/files-* +``` + +更多改动和 api 变更,请查阅 [react-router@6](https://reactrouter.com/en/main/upgrading/v5)。 diff --git a/lerna.json b/lerna.json index b15c5021d..8719d492b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "npmClient": "yarn", "useWorkspaces": true, "npmClientArgs": [ diff --git a/package.json b/package.json index 588150a80..e4a812d9e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,11 @@ }, "resolutions": { "@types/react": "^17.0.0", - "@types/react-dom": "^17.0.0" + "@types/react-dom": "^17.0.0", + "react-router-dom": "^6.11.2", + "react-router": "^6.11.2", + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "config": { "ghooks": { @@ -40,6 +44,10 @@ } }, "devDependencies": { + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", "@commitlint/cli": "^16.1.0", "@commitlint/config-conventional": "^16.0.0", "@commitlint/prompt-cli": "^16.1.0", @@ -48,6 +56,8 @@ "@testing-library/user-event": "^14.4.3", "@vitejs/plugin-react": "^4.0.0", "auto-changelog": "^2.4.0", + "dumi": "^2.2.0", + "dumi-theme-nocobase": "^0.2.11", "ghooks": "^2.0.4", "jsdom-worker": "^0.3.0", "prettier": "^2.2.1", diff --git a/packages/app/client/.umirc.ts b/packages/app/client/.umirc.ts index e2b393eb8..31708ed5b 100644 --- a/packages/app/client/.umirc.ts +++ b/packages/app/client/.umirc.ts @@ -1,34 +1,50 @@ -import { getUmiConfig, resolveNocobasePackagesAlias } from '@nocobase/devtools/umiConfig'; +import { getUmiConfig } from '@nocobase/devtools/umiConfig'; import { defineConfig } from 'umi'; const umiConfig = getUmiConfig(); process.env.MFSU_AD = 'none'; +process.env.DID_YOU_KNOW = 'none'; export default defineConfig({ + title: 'Loading...', + favicons: ['/favicon/favicon.ico'], + metas: [ + { name: 'viewport', content: 'initial-scale=0.1' }, + ], + links: [ + { rel: 'apple-touch-icon', size: '180x180', ref: '/favicon/apple-touch-icon.png' }, + { rel: 'icon', type: 'image/png', size: '32x32', ref: '/favicon/favicon-32x32.png' }, + { rel: 'icon', type: 'image/png', size: '16x16', ref: '/favicon/favicon-16x16.png' }, + { rel: 'manifest', href: '/favicon/site.webmanifest' }, + { rel: 'stylesheet', href: '/global.css' }, + ], + headScripts: [ + '/browser-checker.js', + '/set-router.js', + ], hash: true, + alias: { + ...umiConfig.alias, + }, define: { ...umiConfig.define, }, - dynamicImportSyntax: {}, - // only proxy when using `umi dev` - // if the assets are built, will not proxy proxy: { ...umiConfig.proxy, }, - nodeModulesTransform: { - type: 'none', - }, - routes: [{ path: '/', exact: false, component: '@/pages/index' }], - // fastRefresh: {}, - chainWebpack(memo) { - resolveNocobasePackagesAlias(memo); - - // 在引入 mermaid 之后,运行 yarn dev 的时候会报错,添加下面的代码可以解决。 - memo.module - .rule('js-in-node_modules') - .test(/(react-error-boundary.*\.js|htmlparser2|(.*mermaid.*\.js$))/) - .include.clear(); - return memo; + fastRefresh: false, // 热更新会导致 Context 丢失,不开启 + mfsu: false, + esbuildMinifyIIFE: true, + // srcTranspiler: 'esbuild', // 不行,各种报错 + // mfsu: { + // esbuild: true // 不行,各种报错 + // }, + // 浏览器兼容性,兼容到 2018 年的浏览器 + targets: { + chrome: 69, + edge: 79, + safari: 12, }, + routes: [{ path: '/*', component: 'index' }], }); diff --git a/packages/app/client/package.json b/packages/app/client/package.json index d2fa5077a..fbe15c939 100644 --- a/packages/app/client/package.json +++ b/packages/app/client/package.json @@ -1,9 +1,9 @@ { "name": "@nocobase/app-client", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "license": "AGPL-3.0", "devDependencies": { - "@nocobase/client": "0.9.4-alpha.2" + "@nocobase/client": "0.10.0-alpha.1" }, "repository": { "type": "git", diff --git a/packages/app/client/plugin.ts b/packages/app/client/plugin.ts new file mode 100644 index 000000000..08f5d675e --- /dev/null +++ b/packages/app/client/plugin.ts @@ -0,0 +1,17 @@ +import { IApi } from 'umi'; + +export default (api: IApi) => { + api.addRuntimePlugin(() => ['../plugin-routerBase/runtime.ts']); + api.addRuntimePluginKey(() => ['routerBase']); + + api.onGenerateFiles(() => { + api.writeTmpFile({ + path: '../plugin-routerBase/runtime.ts', + content: ` +export function modifyContextOpts(memo) { + return { ...memo, basename: window.routerBase }; +}; +`, + }); + }); +}; diff --git a/packages/app/client/public/browser-checker.js b/packages/app/client/public/browser-checker.js new file mode 100644 index 000000000..168706a37 --- /dev/null +++ b/packages/app/client/public/browser-checker.js @@ -0,0 +1 @@ +showLog = true; function log(m) { if (window.console && showLog) { console.log(m) } } function css_browser_selector(u) { var uaInfo = {}, screens = [320, 480, 640, 768, 1024, 1152, 1280, 1440, 1680, 1920, 2560], allScreens = screens.length, ua = u.toLowerCase(), is = function (t) { return RegExp(t, 'i').test(ua) }, version = function (p, n) { n = n.replace('.', '_'); var i = n.indexOf('_'), ver = ''; while (i > 0) { ver += ' ' + p + n.substring(0, i); i = n.indexOf('_', i + 1) } ver += ' ' + p + n; return ver }, g = 'gecko', w = 'webkit', c = 'chrome', f = 'firefox', s = 'safari', o = 'opera', m = 'mobile', a = 'android', bb = 'blackberry', lang = 'lang_', dv = 'device_', html = document.documentElement, b = [(!/opera|webtv/i.test(ua) && /msie\s(\d+)/.test(ua)) || /trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.test(ua) ? 'ie ie' + (/trident\/4\.0/.test(ua) ? '8' : RegExp.$1 == '11.0' ? '11' : RegExp.$1) : is('firefox/') ? g + ' ' + f + (/firefox\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua) ? ' ' + f + RegExp.$2 + ' ' + f + RegExp.$2 + '_' + RegExp.$4 : '') : is('gecko/') ? g : is('opera') ? o + (/version\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua) ? ' ' + o + RegExp.$2 + ' ' + o + RegExp.$2 + '_' + RegExp.$4 : /opera(\s|\/)(\d+)\.(\d+)/.test(ua) ? ' ' + o + RegExp.$2 + ' ' + o + RegExp.$2 + '_' + RegExp.$3 : '') : is('konqueror') ? 'konqueror' : is('blackberry') ? bb + (/Version\/(\d+)(\.(\d+)+)/i.test(ua) ? ' ' + bb + RegExp.$1 + ' ' + bb + RegExp.$1 + RegExp.$2.replace('.', '_') : /Blackberry ?(([0-9]+)([a-z]?))[\/|;]/gi.test(ua) ? ' ' + bb + RegExp.$2 + (RegExp.$3 ? ' ' + bb + RegExp.$2 + RegExp.$3 : '') : '') : is('android') ? a + (/Version\/(\d+)(\.(\d+))+/i.test(ua) ? ' ' + a + RegExp.$1 + ' ' + a + RegExp.$1 + RegExp.$2.replace('.', '_') : '') + (/Android (.+); (.+) Build/i.test(ua) ? ' ' + dv + RegExp.$2.replace(/ /g, '_').replace(/-/g, '_') : '') : is('chrome') ? w + ' ' + c + (/chrome\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua) ? ' ' + c + RegExp.$2 + (RegExp.$4 > 0 ? ' ' + c + RegExp.$2 + '_' + RegExp.$4 : '') : '') : is('iron') ? w + ' iron' : is('applewebkit/') ? w + ' ' + s + (/version\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua) ? ' ' + s + RegExp.$2 + ' ' + s + RegExp.$2 + RegExp.$3.replace('.', '_') : / Safari\/(\d+)/i.test(ua) ? RegExp.$1 == '419' || RegExp.$1 == '417' || RegExp.$1 == '416' || RegExp.$1 == '412' ? ' ' + s + '2_0' : RegExp.$1 == '312' ? ' ' + s + '1_3' : RegExp.$1 == '125' ? ' ' + s + '1_2' : RegExp.$1 == '85' ? ' ' + s + '1_0' : '' : '') : is('mozilla/') ? g : '', is('android|mobi|mobile|j2me|iphone|ipod|ipad|blackberry|playbook|kindle|silk',) ? m : '', is('j2me') ? 'j2me' : is('ipad|ipod|iphone') ? (/CPU( iPhone)? OS (\d+[_|\.]\d+([_|\.]\d+)*)/i.test(ua) ? 'ios' + version('ios', RegExp.$2) : '') + ' ' + (/(ip(ad|od|hone))/gi.test(ua) ? RegExp.$1 : '') : is('playbook') ? 'playbook' : is('kindle|silk') ? 'kindle' : is('playbook') ? 'playbook' : is('mac') ? 'mac' + (/mac os x ((\d+)[.|_](\d+))/.test(ua) ? ' mac' + RegExp.$2 + ' mac' + RegExp.$1.replace('.', '_') : '') : is('win') ? 'win' + (is('windows nt 6.2') ? ' win8' : is('windows nt 6.1') ? ' win7' : is('windows nt 6.0') ? ' vista' : is('windows nt 5.2') || is('windows nt 5.1') ? ' win_xp' : is('windows nt 5.0') ? ' win_2k' : is('windows nt 4.0') || is('WinNT4.0') ? ' win_nt' : '') : is('freebsd') ? 'freebsd' : is('x11|linux') ? 'linux' : '', /[; |\[](([a-z]{2})(\-[a-z]{2})?)[)|;|\]]/i.test(ua) ? (lang + RegExp.$2).replace('-', '_') + (RegExp.$3 != '' ? (' ' + lang + RegExp.$1).replace('-', '_') : '') : '', is('ipad|iphone|ipod') && !is('safari') ? 'ipad_app' : '',]; console.debug(ua); function screenSize() { var w = window.outerWidth || html.clientWidth; var h = window.outerHeight || html.clientHeight; uaInfo.orientation = w < h ? 'portrait' : 'landscape'; html.className = html.className.replace(/ ?orientation_\w+/g, '').replace(/ [min|max|cl]+[w|h]_\d+/g, ''); for (var i = allScreens - 1; i >= 0; i--) { if (w >= screens[i]) { uaInfo.maxw = screens[i]; break } } widthClasses = ''; for (var info in uaInfo) { widthClasses += ' ' + info + '_' + uaInfo[info] } html.className = html.className + widthClasses; return widthClasses } window.onresize = screenSize; screenSize(); function retina() { var r = window.devicePixelRatio > 1; if (r) { html.className += ' retina' } else { html.className += ' non-retina' } } retina(); var cssbs = b.join(' ') + ' js '; html.className = (cssbs + html.className.replace(/\b(no[-|_]?)?js\b/g, '')).replace(/^ /, '').replace(/ +/g, ' '); return cssbs } css_browser_selector(navigator.userAgent); \ No newline at end of file diff --git a/packages/app/client/public/global.css b/packages/app/client/public/global.css new file mode 100644 index 000000000..395e9435b --- /dev/null +++ b/packages/app/client/public/global.css @@ -0,0 +1,29 @@ +#root { + width: 100%; + height: 100%; +} +/* width */ +.win ::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +/* Track */ +.win ::-webkit-scrollbar-track { + background: #f1f1f1; +} + +/* Handle */ +.win ::-webkit-scrollbar-thumb { + background: #c0c0c0; + border-radius: 4px; +} + +/* Handle on hover */ +.win ::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +.win .rc-virtual-list-scrollbar-thumb { + background: #c0c0c0 !important; +} diff --git a/packages/app/client/public/set-router.js b/packages/app/client/public/set-router.js new file mode 100644 index 000000000..6e9c95382 --- /dev/null +++ b/packages/app/client/public/set-router.js @@ -0,0 +1,2 @@ +var match = location.pathname.match(/^\/apps\/([^/]*)\//); +window.routerBase = match ? match[0] : "/"; \ No newline at end of file diff --git a/packages/app/client/src/pages/document.ejs b/packages/app/client/src/pages/document.ejs deleted file mode 100644 index 8747521a6..000000000 --- a/packages/app/client/src/pages/document.ejs +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Loading... - - - - - - - - -
- - - diff --git a/packages/app/client/tsconfig.json b/packages/app/client/tsconfig.json index cce36a3b9..010fbcb06 100644 --- a/packages/app/client/tsconfig.json +++ b/packages/app/client/tsconfig.json @@ -22,5 +22,16 @@ "allowSyntheticDefaultImports": true }, "include": ["mock/**/*", "src/**/*", "config/**/*", ".umirc.ts", "typings.d.ts"], - "exclude": ["node_modules", "lib", "es", "dist", "typings", "**/__test__", "test", "docs", "tests"] + "exclude": [ + "node_modules", + "lib", + "es", + "public", + "dist", + "typings", + "**/__test__", + "test", + "docs", + "tests" + ] } diff --git a/packages/app/server/package.json b/packages/app/server/package.json index 09b1d218b..383b130ae 100644 --- a/packages/app/server/package.json +++ b/packages/app/server/package.json @@ -1,12 +1,12 @@ { "name": "@nocobase/app-server", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/preset-nocobase": "0.9.4-alpha.2" + "@nocobase/preset-nocobase": "0.10.0-alpha.1" }, "repository": { "type": "git", diff --git a/packages/core/acl/package.json b/packages/core/acl/package.json index 95eab6f94..9b015dde1 100644 --- a/packages/core/acl/package.json +++ b/packages/core/acl/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/acl", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "Apache-2.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/resourcer": "0.9.4-alpha.2", - "@nocobase/utils": "0.9.4-alpha.2", + "@nocobase/resourcer": "0.10.0-alpha.1", + "@nocobase/utils": "0.10.0-alpha.1", "minimatch": "^5.1.1" }, "repository": { diff --git a/packages/core/actions/package.json b/packages/core/actions/package.json index cdc8016bf..8e0234360 100644 --- a/packages/core/actions/package.json +++ b/packages/core/actions/package.json @@ -1,14 +1,14 @@ { "name": "@nocobase/actions", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "Apache-2.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/cache": "0.9.4-alpha.2", - "@nocobase/database": "0.9.4-alpha.2", - "@nocobase/resourcer": "0.9.4-alpha.2" + "@nocobase/cache": "0.10.0-alpha.1", + "@nocobase/database": "0.10.0-alpha.1", + "@nocobase/resourcer": "0.10.0-alpha.1" }, "repository": { "type": "git", diff --git a/packages/core/auth/package.json b/packages/core/auth/package.json index a5648a3bc..33fb85e81 100644 --- a/packages/core/auth/package.json +++ b/packages/core/auth/package.json @@ -1,19 +1,19 @@ { "name": "@nocobase/auth", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "Apache-2.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/database": "0.9.4-alpha.2", - "@nocobase/resourcer": "0.9.4-alpha.2", - "@nocobase/utils": "0.9.4-alpha.2", - "@nocobase/actions": "0.9.4-alpha.2" + "@nocobase/actions": "0.10.0-alpha.1", + "@nocobase/database": "0.10.0-alpha.1", + "@nocobase/resourcer": "0.10.0-alpha.1", + "@nocobase/utils": "0.10.0-alpha.1" }, "repository": { "type": "git", "url": "git+https://github.com/nocobase/nocobase.git", "directory": "packages/auth" } -} \ No newline at end of file +} diff --git a/packages/core/build/package.json b/packages/core/build/package.json index 9223f9336..a7e148c59 100755 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/build", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "Library build tool based on rollup.", "main": "lib/index.js", "bin": { diff --git a/packages/core/build/src/fixtures/build/babel-syntax/package.json b/packages/core/build/src/fixtures/build/babel-syntax/package.json index 1368588bd..f86651c7b 100755 --- a/packages/core/build/src/fixtures/build/babel-syntax/package.json +++ b/packages/core/build/src/fixtures/build/babel-syntax/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "react": "^16.8.4" + "react": "^18.0.0" } } diff --git a/packages/core/build/src/fixtures/build/rollup-resolve-jsx/package.json b/packages/core/build/src/fixtures/build/rollup-resolve-jsx/package.json index 55ef290f1..e7b40d62e 100755 --- a/packages/core/build/src/fixtures/build/rollup-resolve-jsx/package.json +++ b/packages/core/build/src/fixtures/build/rollup-resolve-jsx/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "react": "16" + "react": "18.0.0" } } diff --git a/packages/core/build/src/fixtures/build/rollup-svgr/package.json b/packages/core/build/src/fixtures/build/rollup-svgr/package.json index 55ef290f1..e7b40d62e 100755 --- a/packages/core/build/src/fixtures/build/rollup-svgr/package.json +++ b/packages/core/build/src/fixtures/build/rollup-svgr/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "react": "16" + "react": "18.0.0" } } diff --git a/packages/core/cache/package.json b/packages/core/cache/package.json index 19684a9d2..559666bf3 100644 --- a/packages/core/cache/package.json +++ b/packages/core/cache/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cache", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "Apache-2.0", "main": "./lib/index.js", diff --git a/packages/core/cli/package.json b/packages/core/cli/package.json index 63eb9aad5..8854c5484 100644 --- a/packages/core/cli/package.json +++ b/packages/core/cli/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cli", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "description": "", "license": "Apache-2.0", "main": "./src/index.js", @@ -8,16 +8,19 @@ "nocobase": "./bin/index.js" }, "dependencies": { + "@types/fs-extra": "^11.0.1", "chalk": "^4.1.1", "commander": "^9.2.0", "dotenv": "^10.0.0", "execa": "^5.1.1", + "fast-glob": "^3.2.12", + "fs-extra": "^11.1.1", "pm2": "^5.2.0", "portfinder": "^1.0.28", "serve": "^13.0.2" }, "devDependencies": { - "@nocobase/devtools": "0.9.4-alpha.2" + "@nocobase/devtools": "0.10.0-alpha.1" }, "repository": { "type": "git", diff --git a/packages/core/cli/src/commands/clean.js b/packages/core/cli/src/commands/clean.js index 9d0de95d5..34906e6f2 100644 --- a/packages/core/cli/src/commands/clean.js +++ b/packages/core/cli/src/commands/clean.js @@ -14,6 +14,7 @@ module.exports = (cli) => { if (!isDev()) { return; } - run('rimraf', ['-rf', 'packages/*/*/{lib,esm,es,dist}']); + run('rimraf', ['-rf', './**/{.umi,.umi-production}']); + run('rimraf', ['-rf', 'packages/*/*/{lib,esm,es,dist,node_modules}']); }); }; diff --git a/packages/core/cli/src/commands/doc.js b/packages/core/cli/src/commands/doc.js index 581f889a3..98efc065c 100644 --- a/packages/core/cli/src/commands/doc.js +++ b/packages/core/cli/src/commands/doc.js @@ -1,6 +1,8 @@ const { Command } = require('commander'); const { resolve, isAbsolute } = require('path'); const { run, isDev } = require('../util'); +const fs = require('fs-extra'); +const glob = require('fast-glob'); /** * @@ -9,30 +11,57 @@ const { run, isDev } = require('../util'); module.exports = (cli) => { cli .command('doc') + .argument('[dev|build|serve]') + .argument('[packages]') .option('--lang [lang]') .allowUnknownOption() - .action((options) => { + .action((command, pkg, options) => { if (!isDev()) { return; } + + // 指定项目目录 + let cwd = process.cwd(); + if (command === undefined && pkg === undefined) { + command = 'dev'; + } else if (!['dev', 'build', 'serve'].includes(command) && pkg === undefined) { + pkg = command; + command = 'dev'; + cwd = resolve(process.cwd(), 'packages', pkg); + } + if (pkg !== undefined) { + process.env.APP_ROOT = `packages/${pkg}`; + } + + // 删除 tmp 目录 + const tmpDir = glob.sync( + ['.dumi/tmp', '.dumi/tmp-production', 'packages/*/*/.dumi/tmp', 'packages/*/*.dumi/tmp-production'], + { + cwd: process.cwd(), + onlyDirectories: true, + }, + ); + tmpDir.forEach((dir) => { + fs.removeSync(dir); + }); + + // 设置语言 process.env.DOC_LANG = options.lang || 'en-US'; - const docThemePath = process.env.DOC_THEME_PATH; - if (!docThemePath) { - process.env.DUMI_THEME = resolve(process.cwd(), 'packages/core/dumi-theme-nocobase/src'); + + if (command === 'serve') { + // 如果是多语言,则需要进入内部,如果不是多语言,则直接进入 docs/dist + let dir = resolve(cwd, 'docs/dist', process.env.DOC_LANG); + if (!existsSync(dir)) { + dir = resolve(cwd, 'docs/dist'); + } + run('serve', [dir]); } else { - process.env.DUMI_THEME = isAbsolute(docThemePath) ? docThemePath : resolve(process.cwd(), docThemePath); - } - // TODO(bug): space replace = not work - const index = process.argv.indexOf(`--lang=${options.lang}`); - if (index > 0) { - process.argv.splice(index, 1); - } - const argv = process.argv.slice(3); - const argument = argv.shift() || 'dev'; - if (argument === 'serve') { - run('serve', [`${resolve(process.cwd(), 'docs/dist', process.env.DOC_LANG)}`, ...argv]); - } else { - run('dumi', [argument, ...argv]); + run('dumi', [command], { + env: { + APP_ROOT: process.env.APP_ROOT, + DOC_LANG: process.env.DOC_LANG, + }, + }); } }); }; diff --git a/packages/core/client/.dumi/global.less b/packages/core/client/.dumi/global.less new file mode 100644 index 000000000..75f241ae0 --- /dev/null +++ b/packages/core/client/.dumi/global.less @@ -0,0 +1 @@ +@import 'antd/dist/antd.css'; diff --git a/packages/core/client/.dumi/theme/builtins/LangSwitch/index.tsx b/packages/core/client/.dumi/theme/builtins/LangSwitch/index.tsx new file mode 100644 index 000000000..4111f9eb6 --- /dev/null +++ b/packages/core/client/.dumi/theme/builtins/LangSwitch/index.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +const LangSwitch = () => { + const { hostname } = window.location + if (hostname === 'localhost') return null; + const en = window.location.href.replace(hostname, 'docs.nocobase.com') + const cn = window.location.href.replace(hostname, 'docs-cn.nocobase.com') + return EN | 中文 +} + +export default LangSwitch; diff --git a/packages/core/client/.dumi/theme/builtins/Previewer/index.tsx b/packages/core/client/.dumi/theme/builtins/Previewer/index.tsx new file mode 100644 index 000000000..9ec63f2cc --- /dev/null +++ b/packages/core/client/.dumi/theme/builtins/Previewer/index.tsx @@ -0,0 +1,23 @@ +import React, { FC, useRef, useEffect, Suspense } from 'react'; +import { Root, createRoot } from 'react-dom/client'; +import { IPreviewerProps } from 'dumi'; +import DefaultPreviewer from 'dumi/theme-default/builtins/Previewer'; + +const Previewer: FC = ({ children, ...props }) => { + const ref = useRef(null); + useEffect(() => { + let root: Root + if (ref.current) { + root = createRoot(ref.current) + root.render(loading...
}>{children}) + } + return () => { + if (root) { + root.unmount() + } + } + }, []); + return
; +}; + +export default Previewer; diff --git a/packages/core/client/.dumirc.ts b/packages/core/client/.dumirc.ts new file mode 100644 index 000000000..18cf515ab --- /dev/null +++ b/packages/core/client/.dumirc.ts @@ -0,0 +1,56 @@ +import { defineConfig } from 'dumi'; +import { defineThemeConfig } from 'dumi-theme-nocobase' +import { getUmiConfig } from '@nocobase/devtools/umiConfig'; +import fs from 'fs'; +import path from 'path'; + +const contributingPath = path.resolve(__dirname, './contributing.md'); +const docsContributingPath = path.resolve(__dirname, './docs/contributing.md'); + +// check if the target path already exists, and remove it if it does +if (fs.existsSync(docsContributingPath)) { + fs.unlinkSync(docsContributingPath); +} + +fs.copyFileSync(contributingPath, docsContributingPath); + +const umiConfig = getUmiConfig(); + +export default defineConfig({ + hash: true, + alias: { + ...umiConfig.alias, + }, + resolve: { + atomDirs: [ + { type: 'component', dir: 'src' }, + { type: 'component', dir: 'src/schema-component/antd' }, + { type: 'component', dir: 'src/route-switch/antd' }, + ] + }, + themeConfig: defineThemeConfig({ + title: 'NocoBase', + logo: 'https://www.nocobase.com/images/logo.png', + github: 'https://github.com/nocobase/nocobase', + footer: 'nocobase | Copyright © 2022', + sidebarGroupModePath: ['/components'], + nav: [ + { + title: 'Intro', + link: '/intro', + }, + { + title: 'Client', + link: '/components/acl', + }, + { + title: 'Develop', + link: '/develop', + }, + { + title: 'Contributing', + link: '/contributing', + } + ] + }) +}); diff --git a/packages/core/client/.umirc.ts b/packages/core/client/.umirc.ts deleted file mode 100644 index 1d959e7a8..000000000 --- a/packages/core/client/.umirc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { resolveNocobasePackagesAlias } from '@nocobase/devtools/umiConfig'; -import { defineConfig } from 'dumi'; - -export default defineConfig({ - title: 'NocoBase', - // outputPath: `./docs/dist/${lang}`, - mode: 'site', - resolve: { - includes: ['./'], - }, - // locales: [[lang, lang]], - hash: true, - logo: 'https://www.nocobase.com/images/logo.png', - navs: [ - null, - { - title: 'GitHub', - path: 'https://github.com/nocobase/nocobase', - }, - ], - chainWebpack(memo) { - resolveNocobasePackagesAlias(memo); - }, -}); diff --git a/packages/core/client/contributing.md b/packages/core/client/contributing.md index 515749f6e..1f4f1b8a1 100644 --- a/packages/core/client/contributing.md +++ b/packages/core/client/contributing.md @@ -57,5 +57,5 @@ export default () =>

Hello NocoBase!

; 也可以引用 demo 文件 ```markdown - + ``` diff --git a/packages/core/client/develop.md b/packages/core/client/develop.md deleted file mode 100644 index fb934422a..000000000 --- a/packages/core/client/develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sidemenu: false ---- - - diff --git a/packages/core/client/docs/develop.md b/packages/core/client/docs/develop.md new file mode 100644 index 000000000..78c8cf4f7 --- /dev/null +++ b/packages/core/client/docs/develop.md @@ -0,0 +1,5 @@ +--- +sidebar: false +--- + + diff --git a/packages/core/client/intro.md b/packages/core/client/docs/intro.md similarity index 98% rename from packages/core/client/intro.md rename to packages/core/client/docs/intro.md index 106ff364d..1951d0768 100644 --- a/packages/core/client/intro.md +++ b/packages/core/client/docs/intro.md @@ -34,7 +34,7 @@ export default app.compose(); * title: Router */ import React from 'react'; -import { Route, Switch, Link, MemoryRouter as Router } from 'react-router-dom'; +import { Route, Routes, Link, MemoryRouter as Router } from 'react-router-dom'; const Home = () =>

Home

; const About = () =>

About

; @@ -42,14 +42,10 @@ const About = () =>

About

; const App = () => ( Home, About - - - - - - - - + + }> + }> + ); @@ -74,7 +70,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'Home', }, { @@ -515,7 +510,6 @@ const routes: RouteRedirectProps[] = [ { type: 'route', path: '/', - exact: true, component: 'RouteSchemaComponent', schema: { name: 'home', @@ -642,7 +636,7 @@ dn.insertAfterEnd(schema); 相关例子如下: - + insertAdjacent 操作不仅可以用于新增节点,也可以用于现有节点的位置移动,如以下拖拽排序的例子: @@ -815,7 +809,7 @@ const { data, loading } = useRequest(); 但是这样的方式不利于 Providers 的管理和扩展,为此提炼了 `compose()` 函数用于配置多个 providers,如下: - + ## Application diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 796e1f1b2..0f1816534 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/client", - "version": "0.9.4-alpha.2", + "version": "0.10.0-alpha.1", "license": "Apache-2.0", "main": "lib", "module": "es/index.js", @@ -13,9 +13,9 @@ "@formily/antd": "2.2.24", "@formily/core": "2.2.24", "@formily/react": "2.2.24", - "@nocobase/evaluators": "0.9.4-alpha.2", - "@nocobase/sdk": "0.9.4-alpha.2", - "@nocobase/utils": "0.9.4-alpha.2", + "@nocobase/evaluators": "0.10.0-alpha.1", + "@nocobase/sdk": "0.10.0-alpha.1", + "@nocobase/utils": "0.10.0-alpha.1", "ahooks": "^3.7.2", "antd": "^4.24.8", "axios": "^0.26.1", @@ -40,24 +40,24 @@ "react-image-lightbox": "^5.1.4", "react-js-cron": "^3.1.0", "react-quill": "^1.3.5", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.11.2", "react-to-print": "^2.14.7", "sanitize-html": "2.10.0", "solarlunar-es": "^1.0.9", "use-deep-compare-effect": "^1.8.1" }, "peerDependencies": { - "@types/react": ">=16.8.0 || >=17.0.0", - "@types/react-dom": ">=16.8.0 || >=17.0.0", - "react": ">=16.8.0 || >=17.0.0", - "react-dom": ">=16.8.0", - "react-is": ">=16.8.0 || >=17.0.0" + "react": ">=18.0.0", + "react-dom": ">=18.0.0", + "react-is": ">=18.0.0" }, "devDependencies": { "@testing-library/react": "^12.1.2", "@types/markdown-it": "12.2.3", "@types/markdown-it-highlightjs": "3.3.1", - "axios-mock-adapter": "^1.20.0" + "axios-mock-adapter": "^1.20.0", + "dumi": "^2.2.0", + "dumi-theme-nocobase": "^0.2.9" }, "gitHead": "ce588eefb0bfc50f7d5bbee575e0b5e843bf6644" } diff --git a/packages/core/client/src/acl/ACLProvider.tsx b/packages/core/client/src/acl/ACLProvider.tsx index e263809e2..d5e2e3d26 100644 --- a/packages/core/client/src/acl/ACLProvider.tsx +++ b/packages/core/client/src/acl/ACLProvider.tsx @@ -2,7 +2,7 @@ import { Field } from '@formily/core'; import { Schema, useField, useFieldSchema } from '@formily/react'; import { Spin } from 'antd'; import React, { createContext, useContext, useEffect } from 'react'; -import { Redirect } from 'react-router-dom'; +import { Navigate } from 'react-router-dom'; import { useAPIClient, useRequest } from '../api-client'; import { useBlockRequestContext } from '../block-provider/BlockProvider'; import { useCollection } from '../collection-manager'; @@ -52,7 +52,7 @@ export const ACLRolesCheckProvider = (props) => { return ; } if (result.error) { - return ; + return ; } return {props.children}; }; diff --git a/packages/core/client/src/acl/index.md b/packages/core/client/src/acl/index.md index fa0ae96ff..4de99929a 100644 --- a/packages/core/client/src/acl/index.md +++ b/packages/core/client/src/acl/index.md @@ -1,10 +1,7 @@ --- -nav: - title: Client - path: /client group: title: Client - path: /client + order: 1 --- # ACL diff --git a/packages/core/client/src/antd-config-provider/index.md b/packages/core/client/src/antd-config-provider/index.md index c066a8f68..f773165e6 100644 --- a/packages/core/client/src/antd-config-provider/index.md +++ b/packages/core/client/src/antd-config-provider/index.md @@ -1,12 +1,11 @@ --- -nav: - path: /client group: - path: /client + title: Client + order: 1 --- # AntdConfigProvider 为 antd 组件提供统一的全局化配置 - + diff --git a/packages/core/client/src/api-client/APIClientProvider.tsx b/packages/core/client/src/api-client/APIClientProvider.tsx index 29bd5c465..043b5236d 100644 --- a/packages/core/client/src/api-client/APIClientProvider.tsx +++ b/packages/core/client/src/api-client/APIClientProvider.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import { APIClient } from './APIClient'; import { APIClientContext } from './context'; export interface APIClientProviderProps { apiClient: APIClient; + children?: ReactNode; } export const APIClientProvider: React.FC = (props) => { diff --git a/packages/core/client/src/api-client/index.md b/packages/core/client/src/api-client/index.md index 244296d57..e4c67d46d 100644 --- a/packages/core/client/src/api-client/index.md +++ b/packages/core/client/src/api-client/index.md @@ -1,8 +1,7 @@ --- -nav: - path: /client group: - path: /client + title: Client + order: 1 --- # APIClient @@ -55,7 +54,7 @@ const { data, loading, run } = apiClient.service('uid'); `api.service(uid)` 的例子,ComponentB 里刷新 ComponentA 的请求数据 - + ## APIClientProvider @@ -99,7 +98,7 @@ run({ 例子如下: - + 或者是 NocoBase 的 resource & action 请求: @@ -120,7 +119,7 @@ run({ 例子如下: - + 也可以是自定义的异步函数: diff --git a/packages/core/client/src/application-v2/Application.tsx b/packages/core/client/src/application-v2/Application.tsx new file mode 100644 index 000000000..6dbfaafd3 --- /dev/null +++ b/packages/core/client/src/application-v2/Application.tsx @@ -0,0 +1,91 @@ +import { i18n } from 'i18next'; +import { merge } from 'lodash'; +import get from 'lodash/get'; +import set from 'lodash/set'; +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { I18nextProvider } from 'react-i18next'; +import { APIClient, APIClientProvider } from '../api-client'; +import { Plugin } from './Plugin'; +import { PluginManager } from './PluginManager'; +import { Router } from './Router'; +import { AppComponent, defaultAppComponents } from './components'; +import { ApplicationOptions } from './types'; + +export class Application { + providers: any[]; + router: Router; + plugins: Map; + scopes: Record; + i18n: i18n; + apiClient: APIClient; + components: any; + pm: PluginManager; + + constructor(protected _options: ApplicationOptions) { + this.providers = []; + this.plugins = new Map(); + this.scopes = merge(this.scopes, _options.scopes || {}); + this.components = merge(defaultAppComponents, _options.components || {}); + this.apiClient = new APIClient(_options.apiClient); + this.router = new Router(_options.router, { app: this }); + this.pm = new PluginManager(this); + this.useDefaultProviders(); + } + + get options() { + return this._options; + } + + useDefaultProviders() { + this.use([APIClientProvider, { apiClient: this.apiClient }]); + this.use(I18nextProvider, { i18n: this.i18n }); + } + + getPlugin(name: string) { + return this.plugins.get(name); + } + + getComponent(name: string) { + return get(this.components, name); + } + + renderComponent(name: string, props = {}) { + return React.createElement(this.getComponent(name), props); + } + + registerComponent(name: string, component: any) { + set(this.components, name, component); + } + + registerComponents(components: any) { + Object.keys(components).forEach((name) => { + this.registerComponent(name, components[name]); + }); + } + + registerScopes(scopes: Record) { + this.scopes = merge(this.scopes, scopes); + } + + use(component: any, props?: any) { + this.providers.push(props ? [component, props] : component); + } + + async load() { + return this.pm.load(); + } + + getRootComponent() { + return () => ; + } + + mount(selector: string) { + const container = typeof selector === 'string' ? document.querySelector(selector) : selector; + if (container) { + const App = this.getRootComponent(); + const root = createRoot(container); + root.render(); + } + } +} diff --git a/packages/core/client/src/application-v2/Plugin.ts b/packages/core/client/src/application-v2/Plugin.ts new file mode 100644 index 000000000..9abf92693 --- /dev/null +++ b/packages/core/client/src/application-v2/Plugin.ts @@ -0,0 +1,30 @@ +import { Application } from './Application'; +import { PluginOptions } from './types'; + +export class Plugin { + constructor(protected _options: PluginOptions, protected app: Application) { + this.app = app; + } + + get options() { + return this._options; + } + + get name() { + return this._options.name; + } + + get pm() { + return this.app.pm; + } + + get router() { + return this.app.router; + } + + async afterAdd() {} + + async beforeLoad() {} + + async load() {} +} diff --git a/packages/core/client/src/application-v2/PluginManager.ts b/packages/core/client/src/application-v2/PluginManager.ts new file mode 100644 index 000000000..32f6221d3 --- /dev/null +++ b/packages/core/client/src/application-v2/PluginManager.ts @@ -0,0 +1,83 @@ +import { Application } from './Application'; +import { Plugin } from './Plugin'; +import { type PluginOptions } from './types'; + +export interface PluginManagerOptions { + plugins: string[]; +} + +type PluginNameOrClass = string | typeof Plugin; + +export class PluginManager { + protected pluginInstances: Map; + protected pluginPrepares: Map; + + constructor(protected app: Application) { + this.pluginInstances = new Map(); + this.pluginPrepares = new Map(); + this.addPresetPlugins(); + } + + protected addPresetPlugins() { + const { plugins } = this.app.options; + for (const plugin of plugins) { + if (typeof plugin === 'string') { + this.prepare(plugin); + } else { + this.prepare(...plugin); + } + } + } + + prepare(nameOrClass: PluginNameOrClass, options?: PluginOptions) { + let opts: any = {}; + if (typeof nameOrClass === 'string') { + opts['name'] = nameOrClass; + } else { + opts = { ...options, Plugin: nameOrClass }; + } + return this.pluginPrepares.set(opts.name, opts); + } + + async add(nameOrClass: PluginNameOrClass, options?: PluginOptions) { + let opts: any = {}; + if (typeof nameOrClass === 'string') { + opts['name'] = nameOrClass; + } else { + opts = { ...options, Plugin: nameOrClass }; + } + const plugin = await this.makePlugin(opts); + this.pluginInstances.set(plugin.name, plugin); + await plugin.afterAdd(); + return plugin; + } + + async makePlugin(opts) { + const { importPlugins } = this.app.options; + let P: typeof Plugin = opts.Plugin; + if (!P) { + P = await importPlugins(opts.name); + } + if (!P) { + throw new Error(`Plugin "${opts.name} " not found`); + } + console.log(opts, P); + return new P(opts, this.app); + } + + async load() { + for (const opts of this.pluginPrepares.values()) { + const plugin = await this.makePlugin(opts); + this.pluginInstances.set(plugin.name, plugin); + await plugin.afterAdd(); + } + + for (const plugin of this.pluginInstances.values()) { + await plugin.beforeLoad(); + } + + for (const plugin of this.pluginInstances.values()) { + await plugin.load(); + } + } +} diff --git a/packages/core/client/src/application-v2/Router.ts b/packages/core/client/src/application-v2/Router.ts new file mode 100644 index 000000000..5886bd542 --- /dev/null +++ b/packages/core/client/src/application-v2/Router.ts @@ -0,0 +1,67 @@ +import set from 'lodash/set'; +import { createBrowserRouter, createHashRouter, createMemoryRouter } from 'react-router-dom'; +import { Application } from './Application'; +import { RouterOptions } from './types'; + +export class Router { + protected app: Application; + protected routes: Map; + + constructor(protected options?: RouterOptions, protected context?: any) { + this.routes = new Map(); + this.app = context.app; + } + + getRoutes() { + const routes = {}; + for (const [name, route] of this.routes) { + set(routes, name.split('.').join('.children.'), route); + } + + const transform = (item) => { + if (item.component) { + item.Component = this.app.getComponent(item.component); + } + return item; + }; + + const toArr = (items: any) => { + return Object.values(items || {}).map((item) => { + if (item.children) { + item.children = toArr(item.children); + } + return transform(item); + }); + }; + return toArr(routes); + } + + createRouter() { + const { type, ...opts } = this.options; + const routes = this.getRoutes(); + if (routes.length === 0) { + return null; + } + switch (type) { + case 'hash': + return createHashRouter(routes, opts) as any; + case 'browser': + return createBrowserRouter(routes, opts) as any; + case 'memory': + return createMemoryRouter(routes, opts) as any; + default: + return createMemoryRouter(routes, opts) as any; + } + } + + add(name: string, route: any) { + this.routes.set(name, route); + Object.keys(route.children || {}).forEach((key) => { + this.routes.set(`${name}.${key}`, route.children[key]); + }); + } + + remove(name: string) { + this.routes.delete(name); + } +} diff --git a/packages/core/client/src/application-v2/components/AppComponent.tsx b/packages/core/client/src/application-v2/components/AppComponent.tsx new file mode 100644 index 000000000..7b7de37aa --- /dev/null +++ b/packages/core/client/src/application-v2/components/AppComponent.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { ApplicationContext } from '../context'; +import { useApp, useLoad } from '../hooks'; + +const Internal = React.memo(() => { + const app = useApp(); + const loading = useLoad(); + if (loading) { + return app.renderComponent('App.Spin'); + } + return app.renderComponent('App.Main', { + app, + providers: app.providers, + }); +}); + +export const AppComponent = (props) => { + const { app } = props; + return ( + + + + ); +}; diff --git a/packages/core/client/src/application-v2/components/MainComponent.tsx b/packages/core/client/src/application-v2/components/MainComponent.tsx new file mode 100644 index 000000000..ae9a046f0 --- /dev/null +++ b/packages/core/client/src/application-v2/components/MainComponent.tsx @@ -0,0 +1,9 @@ +import React, { useMemo } from 'react'; +import { Application } from '../Application'; +import { RouterProvider } from './RouterProvider'; + +export const MainComponent = React.memo((props: { app: Application; providers: any[] }) => { + const { app, providers } = props; + const router = useMemo(() => app.router.createRouter(), []); + return ; +}); diff --git a/packages/core/client/src/application-v2/components/RouterProvider.tsx b/packages/core/client/src/application-v2/components/RouterProvider.tsx new file mode 100644 index 000000000..06131985e --- /dev/null +++ b/packages/core/client/src/application-v2/components/RouterProvider.tsx @@ -0,0 +1,119 @@ +// @ts-nocheck +import type { RouterState } from '@remix-run/router'; +import React from 'react'; +import { + UNSAFE_DataRouterContext as DataRouterContext, + UNSAFE_DataRouterStateContext as DataRouterStateContext, + UNSAFE_LocationContext as LocationContext, + UNSAFE_RouteContext as RouteContext, + Router, + UNSAFE_useRoutesImpl as useRoutesImpl, + type DataRouteObject, + type RouterProviderProps, +} from 'react-router'; +import { compose } from '../compose'; + +const START_TRANSITION = 'startTransition'; + +/** + * Given a Remix Router instance, render the appropriate UI + */ +export function RouterProvider({ + fallbackElement, + router, + providers, +}: RouterProviderProps & { providers?: any }): React.ReactElement { + // Need to use a layout effect here so we are subscribed early enough to + // pick up on any render-driven redirects/navigations (useEffect/) + const [state, setStateImpl] = React.useState(router.state); + const setState = React.useCallback( + (newState: RouterState) => { + START_TRANSITION in React ? React[START_TRANSITION](() => setStateImpl(newState)) : setStateImpl(newState); + }, + [setStateImpl], + ); + React.useLayoutEffect(() => router.subscribe(setState), [router, setState]); + + const navigator = React.useMemo((): Navigator => { + return { + createHref: router.createHref, + encodeLocation: router.encodeLocation, + go: (n) => router.navigate(n), + push: (to, state, opts) => + router.navigate(to, { + state, + preventScrollReset: opts?.preventScrollReset, + }), + replace: (to, state, opts) => + router.navigate(to, { + replace: true, + state, + preventScrollReset: opts?.preventScrollReset, + }), + }; + }, [router]); + + const basename = router.basename || '/'; + + const dataRouterContext = React.useMemo( + () => ({ + router, + navigator, + static: false, + basename, + }), + [router, navigator, basename], + ); + const Providers = compose(...providers)((props) => <>{props.children}); + // The fragment and {null} here are important! We need them to keep React 18's + // useId happy when we are server-rendering since we may have a