From 32025eeaa983efd29ec83ca6ca7777c32c3a650c Mon Sep 17 00:00:00 2001 From: liu <31910599+BBBigCat@users.noreply.github.com> Date: Fri, 15 Mar 2024 00:37:12 +0800 Subject: [PATCH 01/21] feat: vite upgrade to 4.5.2 and improve `esbuildHelperChecker` (#12193) * feat: vite deps upgrade to 4.5.2 * chore: improve esbuildMinifyIIFE check log * chore: disable `esbuildHelperChecker` with vite mode --------- Co-authored-by: bin.liu Co-authored-by: fz6m <59400654+fz6m@users.noreply.github.com> --- packages/bundler-vite/package.json | 2 +- packages/preset-umi/package.json | 2 +- .../esbuildHelperChecker.ts | 13 +- pnpm-lock.yaml | 1359 ++++++++++++++--- 4 files changed, 1149 insertions(+), 227 deletions(-) diff --git a/packages/bundler-vite/package.json b/packages/bundler-vite/package.json index c8bbea5263d3..23c3bdb788dc 100644 --- a/packages/bundler-vite/package.json +++ b/packages/bundler-vite/package.json @@ -34,7 +34,7 @@ "postcss-preset-env": "7.5.0", "rollup-plugin-visualizer": "5.9.0", "systemjs": "^6.14.1", - "vite": "4.3.1" + "vite": "4.5.2" }, "devDependencies": { "@types/caniuse-lite": "1.0.5", diff --git a/packages/preset-umi/package.json b/packages/preset-umi/package.json index 45ecdd1f9628..ac5e6f092bb9 100644 --- a/packages/preset-umi/package.json +++ b/packages/preset-umi/package.json @@ -73,7 +73,7 @@ "multer": "1.4.4", "os-locale": "^6.0.2", "sirv": "2.0.2", - "vite": "4.3.1" + "vite": "4.5.2" }, "publishConfig": { "access": "public" diff --git a/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts b/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts index c024a3f2e34f..21f4673e86ab 100644 --- a/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts +++ b/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts @@ -27,11 +27,15 @@ async function checkDir(opts: { dir: string }) { .filter((v) => varMap[v].length > 1) .map((v) => `${v} (${varMap[v].join(', ')})`); if (conflicts.length) { - throw new Error( - `Found conflicts in esbuild helpers: ${conflicts.join( - ', ', - )}, please set esbuildMinifyIIFE: true in your config file.`, + logger.fatal( + chalk.yellow( + `Found conflicts in esbuild helpers: ${conflicts.join(', ')}`, + ), ); + logger.info( + `please set ${chalk.blue('esbuildMinifyIIFE: true')} in your config file`, + ); + throw new Error(`Found conflicts in esbuild helpers.`); } logger.info(`[esbuildHelperChecker] No conflicts found.`); } @@ -72,6 +76,7 @@ export default (api: IApi) => { }); api.onBuildComplete(async ({ err }) => { + if (api.config.vite) return; if (process.env.OKAM) return; if (err) return; const jsMinifier = api.config.jsMinifier || 'esbuild'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6181225ce7d..f6403eff4970 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1861,7 +1861,7 @@ importers: version: link:../utils '@vitejs/plugin-react': specifier: 4.0.0 - version: 4.0.0(vite@4.3.1) + version: 4.0.0(vite@4.5.2) core-js: specifier: 3.34.0 version: 3.34.0 @@ -1878,15 +1878,15 @@ importers: specifier: ^6.14.1 version: 6.14.2 vite: - specifier: 4.3.1 - version: 4.3.1(@types/node@18.11.18)(less@4.1.3)(sass@1.65.1) + specifier: 4.5.2 + version: 4.5.2(@types/node@18.11.18)(less@4.1.3) devDependencies: '@types/caniuse-lite': specifier: 1.0.5 version: 1.0.5 '@vitejs/plugin-legacy': specifier: 4.1.1 - version: 4.1.1(vite@4.3.1) + version: 4.1.1(vite@4.5.2) caniuse-lite: specifier: 1.0.30001571 version: 1.0.30001571 @@ -2104,7 +2104,7 @@ importers: version: 7.23.6 '@babel/eslint-parser': specifier: 7.23.3 - version: 7.23.3(@babel/core@7.23.6)(eslint@7.32.0) + version: 7.23.3(@babel/core@7.23.6)(eslint@8.35.0) '@stylelint/postcss-css-in-js': specifier: ^0.38.0 version: 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.21) @@ -2501,8 +2501,8 @@ importers: specifier: 2.0.2 version: 2.0.2 vite: - specifier: 4.3.1 - version: 4.3.1(@types/node@18.11.18)(less@4.1.3)(sass@1.65.1) + specifier: 4.5.2 + version: 4.5.2(@types/node@18.11.18)(less@4.1.3) packages/preset-vue: dependencies: @@ -5245,13 +5245,14 @@ packages: slash: 2.0.0 optionalDependencies: '@nicolo-ribaudo/chokidar-2': 2.1.8-no-fsevents.3 - chokidar: 3.5.3 + chokidar: 3.6.0 dev: false /@babel/code-frame@7.12.11: resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} dependencies: '@babel/highlight': 7.22.20 + dev: true /@babel/code-frame@7.22.13: resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} @@ -5428,29 +5429,6 @@ packages: transitivePeerDependencies: - supports-color - /@babel/core@7.23.2: - resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.2) - '@babel/helpers': 7.23.6 - '@babel/parser': 7.23.6 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 - convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/core@7.23.6: resolution: {integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==} engines: {node: '>=6.9.0'} @@ -5493,6 +5471,7 @@ packages: source-map: 0.5.7 transitivePeerDependencies: - supports-color + dev: true /@babel/eslint-parser@7.17.0(@babel/core@7.17.9)(eslint@8.35.0): resolution: {integrity: sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==} @@ -5528,8 +5507,8 @@ packages: semver: 6.3.1 dev: true - /@babel/eslint-parser@7.22.15(@babel/core@7.23.2)(eslint@8.35.0): - resolution: {integrity: sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==} + /@babel/eslint-parser@7.23.3(@babel/core@7.22.5)(eslint@7.32.0): + resolution: {integrity: sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': ^7.11.0 @@ -5538,14 +5517,14 @@ packages: eslint: optional: true dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.5 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 8.35.0 + eslint: 7.32.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 dev: true - /@babel/eslint-parser@7.23.3(@babel/core@7.22.5)(eslint@7.32.0): + /@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@7.32.0): resolution: {integrity: sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: @@ -5555,14 +5534,14 @@ packages: eslint: optional: true dependencies: - '@babel/core': 7.22.5 + '@babel/core': 7.23.6 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 eslint: 7.32.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 dev: true - /@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@7.32.0): + /@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@8.35.0): resolution: {integrity: sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: @@ -5574,7 +5553,7 @@ packages: dependencies: '@babel/core': 7.23.6 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 7.32.0 + eslint: 8.35.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 @@ -6159,20 +6138,6 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.20 - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.2): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} @@ -7670,7 +7635,6 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.22.5): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} @@ -7698,6 +7662,7 @@ packages: dependencies: '@babel/core': 7.4.5 '@babel/helper-plugin-utils': 7.22.5 + dev: true /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.20.12): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -9213,17 +9178,6 @@ packages: '@babel/helper-simple-access': 7.22.5 dev: false - /@babel/plugin-transform-modules-commonjs@7.23.0: - resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.0) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - dev: true - /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.20.12): resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} engines: {node: '>=6.9.0'} @@ -11940,6 +11894,20 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-color-function@1.1.0(postcss@8.4.35): + resolution: {integrity: sha512-5D5ND/mZWcQoSfYnSPsXtuiFxhzmhxt6pcjrFLJyldj+p0ZN2vvRpYNX+lahFTtMhAYOa2WmkdGINr0yP0CvGA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-font-format-keywords@1.0.0(postcss@8.4.21): resolution: {integrity: sha512-oO0cZt8do8FdVBX8INftvIA4lUrKUSCcWUf9IwH9IPWOgKT22oAZFXeHLoDK7nhB2SmkNycp5brxfNMRLIhd6Q==} engines: {node: ^12 || ^14 || >=16} @@ -11965,6 +11933,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-font-format-keywords@1.0.0(postcss@8.4.35): + resolution: {integrity: sha512-oO0cZt8do8FdVBX8INftvIA4lUrKUSCcWUf9IwH9IPWOgKT22oAZFXeHLoDK7nhB2SmkNycp5brxfNMRLIhd6Q==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-hwb-function@1.0.1(postcss@8.4.21): resolution: {integrity: sha512-AMZwWyHbbNLBsDADWmoXT9A5yl5dsGEBeJSJRUJt8Y9n8Ziu7Wstt4MC8jtPW7xjcLecyfJwtnUTNSmOzcnWeg==} engines: {node: ^12 || ^14 || >=16} @@ -11990,6 +11971,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-hwb-function@1.0.1(postcss@8.4.35): + resolution: {integrity: sha512-AMZwWyHbbNLBsDADWmoXT9A5yl5dsGEBeJSJRUJt8Y9n8Ziu7Wstt4MC8jtPW7xjcLecyfJwtnUTNSmOzcnWeg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-ic-unit@1.0.0(postcss@8.4.21): resolution: {integrity: sha512-i4yps1mBp2ijrx7E96RXrQXQQHm6F4ym1TOD0D69/sjDjZvQ22tqiEvaNw7pFZTUO5b9vWRHzbHzP9+UKuw+bA==} engines: {node: ^12 || ^14 || >=16} @@ -12017,6 +12011,20 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-ic-unit@1.0.0(postcss@8.4.35): + resolution: {integrity: sha512-i4yps1mBp2ijrx7E96RXrQXQQHm6F4ym1TOD0D69/sjDjZvQ22tqiEvaNw7pFZTUO5b9vWRHzbHzP9+UKuw+bA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-is-pseudo-class@2.0.5(postcss@8.4.21): resolution: {integrity: sha512-Ek+UFI4UP2hB9u0N1cJd6KgSF1rL0J3PT4is0oSStuus8+WzbGGPyJNMOKQ0w/tyPjxiCnOI4RdSMZt3nks64g==} engines: {node: ^12 || ^14 || >=16} @@ -12044,6 +12052,20 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /@csstools/postcss-is-pseudo-class@2.0.5(postcss@8.4.35): + resolution: {integrity: sha512-Ek+UFI4UP2hB9u0N1cJd6KgSF1rL0J3PT4is0oSStuus8+WzbGGPyJNMOKQ0w/tyPjxiCnOI4RdSMZt3nks64g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/selector-specificity': 2.0.1(postcss-selector-parser@6.0.10)(postcss@8.4.35) + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /@csstools/postcss-normalize-display-values@1.0.0(postcss@8.4.21): resolution: {integrity: sha512-bX+nx5V8XTJEmGtpWTO6kywdS725t71YSLlxWt78XoHUbELWgoCXeOFymRJmL3SU1TLlKSIi7v52EWqe60vJTQ==} engines: {node: ^12 || ^14 || >=16} @@ -12069,6 +12091,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-normalize-display-values@1.0.0(postcss@8.4.35): + resolution: {integrity: sha512-bX+nx5V8XTJEmGtpWTO6kywdS725t71YSLlxWt78XoHUbELWgoCXeOFymRJmL3SU1TLlKSIi7v52EWqe60vJTQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-oklab-function@1.1.0(postcss@8.4.21): resolution: {integrity: sha512-e/Q5HopQzmnQgqimG9v3w2IG4VRABsBq3itOcn4bnm+j4enTgQZ0nWsaH/m9GV2otWGQ0nwccYL5vmLKyvP1ww==} engines: {node: ^12 || ^14 || >=16} @@ -12096,6 +12131,20 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-oklab-function@1.1.0(postcss@8.4.35): + resolution: {integrity: sha512-e/Q5HopQzmnQgqimG9v3w2IG4VRABsBq3itOcn4bnm+j4enTgQZ0nWsaH/m9GV2otWGQ0nwccYL5vmLKyvP1ww==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-progressive-custom-properties@1.3.0(postcss@8.4.21): resolution: {integrity: sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==} engines: {node: ^12 || ^14 || >=16} @@ -12121,6 +12170,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-progressive-custom-properties@1.3.0(postcss@8.4.35): + resolution: {integrity: sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-stepped-value-functions@1.0.0(postcss@8.4.21): resolution: {integrity: sha512-q8c4bs1GumAiRenmFjASBcWSLKrbzHzWl6C2HcaAxAXIiL2rUlUWbqQZUjwVG5tied0rld19j/Mm90K3qI26vw==} engines: {node: ^12 || ^14 || >=16} @@ -12146,6 +12208,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /@csstools/postcss-stepped-value-functions@1.0.0(postcss@8.4.35): + resolution: {integrity: sha512-q8c4bs1GumAiRenmFjASBcWSLKrbzHzWl6C2HcaAxAXIiL2rUlUWbqQZUjwVG5tied0rld19j/Mm90K3qI26vw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /@csstools/postcss-unset-value@1.0.1(postcss@8.4.21): resolution: {integrity: sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg==} engines: {node: ^12 || ^14 || >=16} @@ -12169,6 +12244,18 @@ packages: postcss: 8.4.32 dev: true + /@csstools/postcss-unset-value@1.0.1(postcss@8.4.35): + resolution: {integrity: sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /@csstools/selector-specificity@2.0.1(postcss-selector-parser@6.0.10)(postcss@8.4.21): resolution: {integrity: sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==} engines: {node: ^12 || ^14 || >=16} @@ -12196,6 +12283,20 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /@csstools/selector-specificity@2.0.1(postcss-selector-parser@6.0.10)(postcss@8.4.35): + resolution: {integrity: sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + postcss-selector-parser: ^6.0.10 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /@ctrl/tinycolor@3.4.1: resolution: {integrity: sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==} engines: {node: '>=10'} @@ -12479,6 +12580,14 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-arm@0.15.18: resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} @@ -12504,6 +12613,14 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-x64@0.16.17: resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} engines: {node: '>=12'} @@ -12521,6 +12638,14 @@ packages: requiresBuild: true optional: true + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/darwin-arm64@0.16.17: resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} engines: {node: '>=12'} @@ -12538,6 +12663,14 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/darwin-x64@0.16.17: resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} engines: {node: '>=12'} @@ -12555,6 +12688,14 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/freebsd-arm64@0.16.17: resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} engines: {node: '>=12'} @@ -12572,6 +12713,14 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/freebsd-x64@0.16.17: resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} engines: {node: '>=12'} @@ -12589,6 +12738,14 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/linux-arm64@0.16.17: resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} engines: {node: '>=12'} @@ -12606,6 +12763,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-arm@0.16.17: resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} engines: {node: '>=12'} @@ -12623,6 +12788,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ia32@0.16.17: resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} engines: {node: '>=12'} @@ -12640,6 +12813,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-loong64@0.15.18: resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} @@ -12665,6 +12846,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-mips64el@0.16.17: resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} engines: {node: '>=12'} @@ -12682,6 +12871,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ppc64@0.16.17: resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} engines: {node: '>=12'} @@ -12699,6 +12896,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-riscv64@0.16.17: resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} engines: {node: '>=12'} @@ -12716,6 +12921,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-s390x@0.16.17: resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} engines: {node: '>=12'} @@ -12733,6 +12946,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-x64@0.16.17: resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} engines: {node: '>=12'} @@ -12750,6 +12971,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/netbsd-x64@0.16.17: resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} engines: {node: '>=12'} @@ -12767,6 +12996,14 @@ packages: requiresBuild: true optional: true + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + /@esbuild/openbsd-x64@0.16.17: resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} engines: {node: '>=12'} @@ -12784,6 +13021,14 @@ packages: requiresBuild: true optional: true + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + /@esbuild/sunos-x64@0.16.17: resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} engines: {node: '>=12'} @@ -12801,6 +13046,14 @@ packages: requiresBuild: true optional: true + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + /@esbuild/win32-arm64@0.16.17: resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} engines: {node: '>=12'} @@ -12818,6 +13071,14 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-ia32@0.16.17: resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} engines: {node: '>=12'} @@ -12835,6 +13096,14 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-x64@0.16.17: resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} engines: {node: '>=12'} @@ -12852,6 +13121,14 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@7.32.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -12896,6 +13173,7 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color + dev: true /@eslint/eslintrc@2.0.0: resolution: {integrity: sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==} @@ -13152,6 +13430,7 @@ packages: minimatch: 3.1.2 transitivePeerDependencies: - supports-color + dev: true /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -15612,6 +15891,23 @@ packages: - supports-color dev: true + /@stylelint/postcss-css-in-js@0.38.0(postcss-syntax@0.36.2)(postcss@8.4.35): + resolution: {integrity: sha512-XOz5CAe49kS95p5yRd+DAIWDojTjfmyAQ4bbDlXMdbZTQ5t0ThjSLvWI6JI2uiS7MFurVBkZ6zUqcimzcLTBoQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + peerDependencies: + postcss: '>=7.0.0' + postcss-syntax: '>=0.36.2' + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@babel/core': 7.23.6 + postcss: 8.4.35 + postcss-syntax: 0.36.2(postcss@8.4.35) + transitivePeerDependencies: + - supports-color + dev: true + /@stylelint/postcss-markdown@0.36.2(postcss-syntax@0.36.2)(postcss@7.0.39): resolution: {integrity: sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==} peerDependencies: @@ -17619,10 +17915,10 @@ packages: - supports-color dev: true - /@umijs/ast@4.0.90: - resolution: {integrity: sha512-l9RQZj77A/Kywru2ZR1QfRsCdw9b1JRFZpi9caJ47xkM+cJklWC4c6tBcT5SsPsQEAOUaRAcJKCxmwj5X15ZJA==} + /@umijs/ast@4.1.2: + resolution: {integrity: sha512-ejgp07sn4IEMf8Urxt+su9KrUWIWp0rhtnljB3XVR2A4mcVdz1jtSQbqYwQFtgn6Mp6tCMG+H1fkweRWj7+vfQ==} dependencies: - '@umijs/bundler-utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 transitivePeerDependencies: - supports-color dev: true @@ -17639,14 +17935,14 @@ packages: - supports-color dev: true - /@umijs/babel-preset-umi@4.0.90: - resolution: {integrity: sha512-EIpuJdDutSlHTW5AbR2L8rRx+tC1QrLbvxrQek69lM+0Jijnn2n1gznIKKH8ol8yfhNm1o4q54ATWU+SjIDMlg==} + /@umijs/babel-preset-umi@4.1.2: + resolution: {integrity: sha512-SdizYktVzp5ODwOQEeHzAwt+/WGUaIO/py9z1lGQdzxGfiTkEMU5hB70bh0fFKn3jOKCKTdBLw8BfIi2E/GwDQ==} dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.6 '@bloomberg/record-tuple-polyfill': 0.0.4 - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 - core-js: 3.28.0 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 + core-js: 3.34.0 transitivePeerDependencies: - supports-color dev: true @@ -17665,16 +17961,16 @@ packages: - supports-color dev: true - /@umijs/bundler-esbuild@4.0.90: - resolution: {integrity: sha512-qTC1jkvlFyAyBYvWVVtpHl/RY6jwAeVYppR3eg8lD5HDB5+JK4fsR+waPDjXz56KJfFr+T9+jT4I9LcbbQjKiw==} + /@umijs/bundler-esbuild@4.1.2: + resolution: {integrity: sha512-LcAlqoQKDUeEYmkLw2mB2T9FBOjZQsFSzCw6ZYItTV2zIdFNlH4U5slfhRqlwQbzxDgBrbPLEGl0M85CqEFE4w==} hasBin: true dependencies: - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 enhanced-resolve: 5.9.3 - postcss: 8.4.32 - postcss-flexbugs-fixes: 5.0.2(postcss@8.4.32) - postcss-preset-env: 7.5.0(postcss@8.4.32) + postcss: 8.4.35 + postcss-flexbugs-fixes: 5.0.2(postcss@8.4.35) + postcss-preset-env: 7.5.0(postcss@8.4.35) transitivePeerDependencies: - supports-color dev: true @@ -17702,10 +17998,10 @@ packages: - supports-color dev: true - /@umijs/bundler-utils@4.0.90: - resolution: {integrity: sha512-BMdKGrBLF1ReQYxgJ4egJppVr8ZiP3yoolsKLZg5KmttcuFmOJLGJ/2FXzMM9KbtKfKDDnCINi25STWtWJyF9A==} + /@umijs/bundler-utils@4.1.2: + resolution: {integrity: sha512-bcN3VSgCPZjyLmQrRWPfPkuhVP0GCFyBLTxzr4vPHQTYx7FjHJcvpEbOsXoVNiBHowRA8J6PGCB/jxqRSO1yxw==} dependencies: - '@umijs/utils': 4.0.90 + '@umijs/utils': 4.1.2 esbuild: 0.17.19 regenerate: 1.4.2 regenerate-unicode-properties: 10.1.1 @@ -17731,19 +18027,19 @@ packages: - supports-color dev: true - /@umijs/bundler-vite@4.0.90(@types/node@18.11.18)(postcss@8.4.32)(sass@1.65.1): - resolution: {integrity: sha512-Z4O+BhsmHmFKBgSJcQGy+6wyVDYMDtpBD37l5wI2xjEHkHwSiwLf9VCt1oUEwT+yv1mTaBfq8bUjconTFL/ydA==} + /@umijs/bundler-vite@4.1.2(@types/node@18.11.18)(postcss@8.4.35)(sass@1.65.1): + resolution: {integrity: sha512-znqi0rb8zsh90jT8duCkGj/gcli8xEkjQDi1y6BA/dshIO9Ra4KT9riijJsaumiD+OJrABpV2DWjX7JrszzByg==} hasBin: true dependencies: '@svgr/core': 6.5.1 - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 '@vitejs/plugin-react': 4.0.0(vite@4.3.1) - core-js: 3.28.0 + core-js: 3.34.0 less: 4.1.3 - postcss-preset-env: 7.5.0(postcss@8.4.32) + postcss-preset-env: 7.5.0(postcss@8.4.35) rollup-plugin-visualizer: 5.9.0(rollup@3.7.0) - systemjs: 6.14.2 + systemjs: 6.14.3 vite: 4.3.1(@types/node@18.11.18)(less@4.1.3)(sass@1.65.1) transitivePeerDependencies: - '@types/node' @@ -17790,29 +18086,29 @@ packages: - webpack-plugin-serve dev: true - /@umijs/bundler-webpack@4.0.90(typescript@4.8.4): - resolution: {integrity: sha512-8m8SD7hduEPQXsMmhU/QtvcAJQBhAmhhMjyCVCd6OSTryd2/kz9EeVjVUSQdKuuHdGzCBKfbB23lnMvu2loTyg==} + /@umijs/bundler-webpack@4.1.2(typescript@4.8.4): + resolution: {integrity: sha512-rCf+H/k1Ru/twlCvAlqjjRIZRZJNZmaXpROaQ6VviPDj1F5YLDWMCunvdGf8KOeBVTLy4syhdxn95ZIyKcPpPA==} hasBin: true dependencies: '@svgr/core': 6.5.1 '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) '@types/hapi__joi': 17.1.9 - '@umijs/babel-preset-umi': 4.0.90 - '@umijs/bundler-utils': 4.0.90 + '@umijs/babel-preset-umi': 4.1.2 + '@umijs/bundler-utils': 4.1.2 '@umijs/case-sensitive-paths-webpack-plugin': 1.0.1 - '@umijs/mfsu': 4.0.90 + '@umijs/mfsu': 4.1.2 '@umijs/react-refresh-webpack-plugin': 0.5.11(react-refresh@0.14.0)(webpack@5.88.2) - '@umijs/utils': 4.0.90 + '@umijs/utils': 4.1.2 cors: 2.8.5 css-loader: 6.7.1(webpack@5.88.2) - es5-imcompatible-versions: 0.1.88 + es5-imcompatible-versions: 0.1.89 fork-ts-checker-webpack-plugin: 8.0.0(typescript@4.8.4) jest-worker: 29.4.3 lightningcss: 1.22.1 node-libs-browser: 2.2.1 - postcss: 8.4.32 - postcss-preset-env: 7.5.0(postcss@8.4.32) + postcss: 8.4.35 + postcss-preset-env: 7.5.0(postcss@8.4.35) react-error-overlay: 6.0.9 react-refresh: 0.14.0 transitivePeerDependencies: @@ -17827,29 +18123,29 @@ packages: - webpack-plugin-serve dev: true - /@umijs/bundler-webpack@4.0.90(typescript@4.9.5): - resolution: {integrity: sha512-8m8SD7hduEPQXsMmhU/QtvcAJQBhAmhhMjyCVCd6OSTryd2/kz9EeVjVUSQdKuuHdGzCBKfbB23lnMvu2loTyg==} + /@umijs/bundler-webpack@4.1.2(typescript@4.9.5): + resolution: {integrity: sha512-rCf+H/k1Ru/twlCvAlqjjRIZRZJNZmaXpROaQ6VviPDj1F5YLDWMCunvdGf8KOeBVTLy4syhdxn95ZIyKcPpPA==} hasBin: true dependencies: '@svgr/core': 6.5.1 '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) '@types/hapi__joi': 17.1.9 - '@umijs/babel-preset-umi': 4.0.90 - '@umijs/bundler-utils': 4.0.90 + '@umijs/babel-preset-umi': 4.1.2 + '@umijs/bundler-utils': 4.1.2 '@umijs/case-sensitive-paths-webpack-plugin': 1.0.1 - '@umijs/mfsu': 4.0.90 + '@umijs/mfsu': 4.1.2 '@umijs/react-refresh-webpack-plugin': 0.5.11(react-refresh@0.14.0)(webpack@5.88.2) - '@umijs/utils': 4.0.90 + '@umijs/utils': 4.1.2 cors: 2.8.5 css-loader: 6.7.1(webpack@5.88.2) - es5-imcompatible-versions: 0.1.88 + es5-imcompatible-versions: 0.1.89 fork-ts-checker-webpack-plugin: 8.0.0(typescript@4.9.5)(webpack@5.88.2) jest-worker: 29.4.3 lightningcss: 1.22.1 node-libs-browser: 2.2.1 - postcss: 8.4.32 - postcss-preset-env: 7.5.0(postcss@8.4.32) + postcss: 8.4.35 + postcss-preset-env: 7.5.0(postcss@8.4.35) react-error-overlay: 6.0.9 react-refresh: 0.14.0 transitivePeerDependencies: @@ -17876,11 +18172,11 @@ packages: - supports-color dev: true - /@umijs/core@4.0.90: - resolution: {integrity: sha512-wAv32YX6drhSdC7f6TlSp/I0J0evD2Ajs80akqiBraBtjdW6ACoB3zvM5Z06bRigpsICS6UWbuCu1bfZNb8PbQ==} + /@umijs/core@4.1.2: + resolution: {integrity: sha512-OZlZKG+26coItwPJBtPmqB2zGSnVJqi/BVFm4MYB3fDtY5+/KmPCSXur/FJvpmRInoVAJkahbsycmMh+dUT0wQ==} dependencies: - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 transitivePeerDependencies: - supports-color dev: true @@ -18094,20 +18390,20 @@ packages: - typescript dev: true - /@umijs/lint@4.0.90(eslint@8.35.0)(jest@29.4.3)(typescript@4.9.5): - resolution: {integrity: sha512-vGcyqLYhKrWgaTffLAGhdZRfFfow8C69NJJ5klOtz6KApa6LjrJoF2+nkz0JMpAovMfd5cCmV8HRMgs4aSOBZA==} + /@umijs/lint@4.1.2(eslint@8.35.0)(jest@29.4.3)(typescript@4.9.5): + resolution: {integrity: sha512-sdau5ICWOtyEsQrRHuS5wx4iPZhu56DGrxZkASAXGtQxntoJeWzx/e+qAhdsiFiWZrNXbbOOdlafpGyLy6BoKA==} dependencies: - '@babel/core': 7.23.2 - '@babel/eslint-parser': 7.22.15(@babel/core@7.23.2)(eslint@8.35.0) - '@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.32) + '@babel/core': 7.23.6 + '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@8.35.0) + '@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.35) '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.35.0)(typescript@4.9.5) '@typescript-eslint/parser': 5.62.0(eslint@8.35.0)(typescript@4.9.5) - '@umijs/babel-preset-umi': 4.0.90 + '@umijs/babel-preset-umi': 4.1.2 eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.35.0)(jest@29.4.3)(typescript@4.9.5) eslint-plugin-react: 7.33.2(eslint@8.35.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.35.0) - postcss: 8.4.32 - postcss-syntax: 0.36.2(postcss@8.4.32) + postcss: 8.4.35 + postcss-syntax: 0.36.2(postcss@8.4.35) stylelint-config-standard: 25.0.0 transitivePeerDependencies: - eslint @@ -18122,20 +18418,20 @@ packages: - typescript dev: true - /@umijs/lint@4.0.90(jest@29.4.3)(typescript@4.9.5): - resolution: {integrity: sha512-vGcyqLYhKrWgaTffLAGhdZRfFfow8C69NJJ5klOtz6KApa6LjrJoF2+nkz0JMpAovMfd5cCmV8HRMgs4aSOBZA==} + /@umijs/lint@4.1.2(jest@29.4.3)(typescript@4.9.5): + resolution: {integrity: sha512-sdau5ICWOtyEsQrRHuS5wx4iPZhu56DGrxZkASAXGtQxntoJeWzx/e+qAhdsiFiWZrNXbbOOdlafpGyLy6BoKA==} dependencies: - '@babel/core': 7.23.2 - '@babel/eslint-parser': 7.22.15(@babel/core@7.23.2)(eslint@8.35.0) - '@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.32) + '@babel/core': 7.23.6 + '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@8.35.0) + '@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.35) '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(typescript@4.9.5) '@typescript-eslint/parser': 5.62.0(eslint@8.35.0)(typescript@4.9.5) - '@umijs/babel-preset-umi': 4.0.90 + '@umijs/babel-preset-umi': 4.1.2 eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0)(jest@29.4.3)(typescript@4.9.5) eslint-plugin-react: 7.33.2(eslint@8.35.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.35.0) - postcss: 8.4.32 - postcss-syntax: 0.36.2(postcss@8.4.32) + postcss: 8.4.35 + postcss-syntax: 0.36.2(postcss@8.4.35) stylelint-config-standard: 25.0.0 transitivePeerDependencies: - eslint @@ -18200,12 +18496,12 @@ packages: - supports-color dev: true - /@umijs/mfsu@4.0.90: - resolution: {integrity: sha512-zAlDMAR4PzCRBD5VxpMkZ+BJ4Z3Ce4gmtVCEJBX7p4uREZ7lHUimi/31VF+oNOL28IoodpPuit9abJ2+6i2tOA==} + /@umijs/mfsu@4.1.2: + resolution: {integrity: sha512-PxkSdQWR0323B1PKLjn2F3ZLfxByg3MmwgRipUbwX3dC8ae9kqIR5wEB2bfL1uXSg9x63o2JMsv5/ObfhrsmYw==} dependencies: - '@umijs/bundler-esbuild': 4.0.90 - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-esbuild': 4.1.2 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 enhanced-resolve: 5.9.3 is-equal: 1.7.0 transitivePeerDependencies: @@ -18239,8 +18535,8 @@ packages: - supports-color dev: true - /@umijs/plugin-run@4.0.90: - resolution: {integrity: sha512-/OoJkZkdfuHvVxXoxa/MMyHa7lVLuy6txFccY32V9wKPqPjnPiSkYZrnhZTykMyEmKXbXTX2yqqhyY91gSTgRA==} + /@umijs/plugin-run@4.1.2: + resolution: {integrity: sha512-SkCOd1ZMe5zJp0avxC0UWr4/MK4QU3iqMzNOk6kBYPMIZgsNSn+6+iol+Sb3aqn3RzU3TQWHeKtO2ckZyrD+vw==} dependencies: tsx: 3.12.2 dev: true @@ -18293,8 +18589,8 @@ packages: - supports-color dev: true - /@umijs/plugins@4.0.90(@types/react-dom@18.0.10)(@types/react@18.0.26)(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-9PGsd2ocRFf7DIy6dZn/k5IGAodzVPe1wi1t9lMN1RmT+5JoRHCV77eMCDAiBuF1uDU4yxEKXCnEkUNfkQovgw==} + /@umijs/plugins@4.1.2(@types/react-dom@18.0.10)(@types/react@18.0.26)(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-BcM+8WW8L8qByiVYHzzxzbXGUpbrCg2F1R1yjsD+uev8YQ0epI6X+Kzn0Np7/e7aPEJA737272Gvv088GCaBqQ==} dependencies: '@ahooksjs/use-request': 2.8.15(react@17.0.2) '@ant-design/antd-theme-variable': 1.0.0 @@ -18304,7 +18600,7 @@ packages: '@ant-design/pro-components': 2.3.20(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2) '@tanstack/react-query': 4.24.10(react-dom@17.0.2)(react@17.0.2) '@tanstack/react-query-devtools': 4.24.10(@tanstack/react-query@4.24.10)(react-dom@17.0.2)(react@17.0.2) - '@umijs/bundler-utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 '@umijs/valtio': 1.0.4(@types/react@18.0.26)(react@17.0.2) antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.7) axios: 0.27.2(debug@4.3.4) @@ -18378,39 +18674,39 @@ packages: - webpack-plugin-serve dev: true - /@umijs/preset-umi@4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5): - resolution: {integrity: sha512-Q7JX0tpE6jVuYMQuWGRRMtTr8avjcvCsN6ENwIAZrzVlYgDKXLgo5T8BO+DAzY8acwgx9o9Y1iCXN1m9IIjk2w==} + /@umijs/preset-umi@4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5): + resolution: {integrity: sha512-tZe7mWS2vCoULXcY5Zx10nVWxMHPXl1+Pj3X0hiPE2oPYJYw5eYtE0+IivNAOUH2M2d5c8j257MgG+XND8Gpbw==} dependencies: '@iconify/utils': 2.1.1 '@svgr/core': 6.5.1 - '@umijs/ast': 4.0.90 - '@umijs/babel-preset-umi': 4.0.90 - '@umijs/bundler-esbuild': 4.0.90 - '@umijs/bundler-utils': 4.0.90 - '@umijs/bundler-vite': 4.0.90(@types/node@18.11.18)(postcss@8.4.32)(sass@1.65.1) - '@umijs/bundler-webpack': 4.0.90(typescript@4.9.5) - '@umijs/core': 4.0.90 + '@umijs/ast': 4.1.2 + '@umijs/babel-preset-umi': 4.1.2 + '@umijs/bundler-esbuild': 4.1.2 + '@umijs/bundler-utils': 4.1.2 + '@umijs/bundler-vite': 4.1.2(@types/node@18.11.18)(postcss@8.4.35)(sass@1.65.1) + '@umijs/bundler-webpack': 4.1.2(typescript@4.9.5) + '@umijs/core': 4.1.2 '@umijs/did-you-know': 1.0.3 '@umijs/es-module-parser': 0.0.7 '@umijs/history': 5.3.1 - '@umijs/mfsu': 4.0.90 - '@umijs/plugin-run': 4.0.90 - '@umijs/renderer-react': 4.0.90(react-dom@18.1.0)(react@18.1.0) - '@umijs/server': 4.0.90 + '@umijs/mfsu': 4.1.2 + '@umijs/plugin-run': 4.1.2 + '@umijs/renderer-react': 4.1.2(react-dom@18.1.0)(react@18.1.0) + '@umijs/server': 4.1.2 '@umijs/ui': 3.0.1 - '@umijs/utils': 4.0.90 - '@umijs/zod2ts': 4.0.90 + '@umijs/utils': 4.1.2 + '@umijs/zod2ts': 4.1.2 babel-plugin-dynamic-import-node: 2.3.3 click-to-react-component: 1.1.0(@types/react@18.0.26)(react-dom@18.1.0)(react@18.1.0) - core-js: 3.28.0 + core-js: 3.34.0 current-script-polyfill: 1.0.0 enhanced-resolve: 5.9.3 fast-glob: 3.2.12 html-webpack-plugin: 5.5.0(webpack@5.75.0) less-plugin-resolve: 1.0.2 path-to-regexp: 1.7.0 - postcss: 8.4.32 - postcss-prefix-selector: 1.16.0(postcss@8.4.32) + postcss: 8.4.35 + postcss-prefix-selector: 1.16.0(postcss@8.4.35) react: 18.1.0 react-dom: 18.1.0(react@18.1.0) react-router: 6.3.0(react@18.1.0) @@ -18511,8 +18807,8 @@ packages: react-router-dom: 6.3.0(react-dom@18.1.0)(react@18.1.0) dev: true - /@umijs/renderer-react@4.0.90(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-3EvhbFIf8L8D8yJEW8HcmiMttetXR22x4yvLJpJkspnYeQajnpGSGhYuq5W6lOaq9gVHxU08D1CGaXdhbzoPqA==} + /@umijs/renderer-react@4.1.2(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-zAZ1yU/PTkit/Nl0JsArS8ZwWyhmFpMEMRKpFZqw1rYieXTlNGvZTm2twJj+rNzsxwoNmW7E24glJGjpm1CunA==} peerDependencies: react: '>=16.8' react-dom: '>=16.8' @@ -18522,7 +18818,7 @@ packages: react-dom: optional: true dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.6 '@loadable/component': 5.15.2(react@17.0.2) history: 5.3.0 react: 17.0.2 @@ -18531,8 +18827,8 @@ packages: react-router-dom: 6.3.0(react-dom@17.0.2)(react@17.0.2) dev: true - /@umijs/renderer-react@4.0.90(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-3EvhbFIf8L8D8yJEW8HcmiMttetXR22x4yvLJpJkspnYeQajnpGSGhYuq5W6lOaq9gVHxU08D1CGaXdhbzoPqA==} + /@umijs/renderer-react@4.1.2(react-dom@18.1.0)(react@18.1.0): + resolution: {integrity: sha512-zAZ1yU/PTkit/Nl0JsArS8ZwWyhmFpMEMRKpFZqw1rYieXTlNGvZTm2twJj+rNzsxwoNmW7E24glJGjpm1CunA==} peerDependencies: react: '>=16.8' react-dom: '>=16.8' @@ -18542,7 +18838,7 @@ packages: react-dom: optional: true dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.6 '@loadable/component': 5.15.2(react@18.1.0) history: 5.3.0 react: 18.1.0 @@ -18580,10 +18876,10 @@ packages: - supports-color dev: true - /@umijs/server@4.0.90: - resolution: {integrity: sha512-lwcV6mq+jLNZS/XSAxJFrBW9d/hhiIFIxc6OIj+K8WfeJDrc1H/G/BGM8dtqv56Vnl/HUqoAW7sND+BAdaa1fg==} + /@umijs/server@4.1.2: + resolution: {integrity: sha512-1oUWhF4qW2T4BqYKRTtZm+REJpDzPdQ3oeXubAIpFqek5Z0ABKcp7/mkH68AVRztsag0t9cXuBN/AL5GkvjXww==} dependencies: - '@umijs/bundler-utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 history: 5.3.0 react: 18.1.0 react-dom: 18.1.0(react@18.1.0) @@ -18608,13 +18904,13 @@ packages: - supports-color dev: true - /@umijs/test@4.0.90: - resolution: {integrity: sha512-3/l/qaO/9NzB6zTCTklgLpDgNqm75KXzV4+2wtGf6evH2rubNY5SDokTlfC8v5Pb/PLvRdVmERnyrelFbVjBrA==} + /@umijs/test@4.1.2: + resolution: {integrity: sha512-mmP0bmvYx/gHIgq8mxbMJSgLxBKh5Tp2ZiuK68aoqlBWSBXU2xB9LM4KGno3Or9vihSFAUzK1wWEcVj7CQCY4w==} dependencies: - '@babel/plugin-transform-modules-commonjs': 7.23.0 + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.0) '@jest/types': 27.5.1 - '@umijs/bundler-utils': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 + '@umijs/utils': 4.1.2 babel-jest: 29.7.0 esbuild: 0.17.19 identity-obj-proxy: 3.0.0 @@ -18664,8 +18960,8 @@ packages: pino: 7.11.0 dev: true - /@umijs/utils@4.0.90: - resolution: {integrity: sha512-H+/pgp6SjYiehBbSLLaFOdVvLgGpi4mGLKU08j9u1E8uj5Y1DfKISPfzr+PS4YGminQ4RpUvFOw1OicH6fGo3A==} + /@umijs/utils@4.1.2: + resolution: {integrity: sha512-YBzN7Zn3595W93t8XPh8IbKMInr6NecXPtHVD+L7fJFv5oCrzHxCkvSZBTmTXuFMyW+9rHT0TlsXM4gqQ1n18Q==} dependencies: chokidar: 3.5.3 pino: 7.11.0 @@ -18680,8 +18976,8 @@ packages: - react dev: true - /@umijs/zod2ts@4.0.90: - resolution: {integrity: sha512-W2L6Sp/aSYDnWwNICgwACzxmkX0WcpHW3zUIwCqEfcj8EhuWoB8E79bP7ENoVeFgjEh+u4SR7C+7T27I3ngq+g==} + /@umijs/zod2ts@4.1.2: + resolution: {integrity: sha512-h5P45gXxTVWdDeIuB/EQ9lDqJgPrm747Ox77WvK1ooG+jN4xBB6uoH5slCLenl3ig+M1nRLIadVsD/8kjrWWHw==} dev: true /@unocss/cli@0.33.5: @@ -18985,7 +19281,7 @@ packages: resolution: {integrity: sha512-JGZ11QV+/ZcfudW2Cz2JVp54/pJNXbsuWRgSh2ZmmZdQBKXqBtIGrwI1Wyx8nlbzAiEFe7FHi4K1zX4//jxTnQ==} dev: true - /@vitejs/plugin-legacy@4.1.1(vite@4.3.1): + /@vitejs/plugin-legacy@4.1.1(vite@4.5.2): resolution: {integrity: sha512-um3gbVouD2Q/g19C0qpDfHwveXDCAHzs8OC3e9g6aXpKoD1H14himgs7wkMnhAynBJy7QqUoZNAXDuqN8zLR2g==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -18999,7 +19295,7 @@ packages: magic-string: 0.30.4 regenerator-runtime: 0.13.11 systemjs: 6.14.2 - vite: 4.3.1(@types/node@18.11.18)(less@4.1.3)(sass@1.65.1) + vite: 4.5.2(@types/node@18.11.18)(less@4.1.3) transitivePeerDependencies: - supports-color dev: true @@ -19048,6 +19344,22 @@ packages: vite: 4.3.1(@types/node@18.11.18)(less@4.1.3)(sass@1.65.1) transitivePeerDependencies: - supports-color + dev: true + + /@vitejs/plugin-react@4.0.0(vite@4.5.2): + resolution: {integrity: sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.6) + react-refresh: 0.14.0 + vite: 4.5.2(@types/node@18.11.18)(less@4.1.3) + transitivePeerDependencies: + - supports-color + dev: false /@vitejs/plugin-vue-jsx@3.0.1(vue@3.2.45): resolution: {integrity: sha512-+Jb7ggL48FSPS1uhPnJbJwWa9Sr90vQ+d0InW+AhBM22n+cfuYqJZDckBc+W3QSHe1WDvewMZfa4wZOtk5pRgw==} @@ -19538,6 +19850,7 @@ packages: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: acorn: 7.4.1 + dev: true /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -19777,6 +20090,7 @@ packages: /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + dev: true /ansi-escapes@3.2.0: resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==} @@ -20904,6 +21218,25 @@ packages: postcss-value-parser: 4.2.0 dev: true + /autoprefixer@10.4.13(postcss@8.4.35): + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + browserslist: 4.22.2 + caniuse-lite: 1.0.30001571 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /autoprefixer@9.8.8: resolution: {integrity: sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==} dependencies: @@ -21302,7 +21635,7 @@ packages: dependencies: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.4.5) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.20.12) lodash: 4.17.21 picomatch: 2.3.1 styled-components: 6.1.1(react-dom@18.1.0)(react@18.1.0) @@ -22407,6 +22740,23 @@ packages: optionalDependencies: fsevents: 2.3.3 + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + requiresBuild: true + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + optional: true + /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} dev: true @@ -23381,7 +23731,6 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 dev: false - bundledDependencies: false /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -23484,6 +23833,20 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /css-blank-pseudo@3.0.3(postcss@8.4.35): + resolution: {integrity: sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /css-color-keywords@1.0.0: resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} engines: {node: '>=4'} @@ -23543,6 +23906,20 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /css-has-pseudo@3.0.4(postcss@8.4.35): + resolution: {integrity: sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /css-in-js-utils@2.0.1: resolution: {integrity: sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==} dependencies: @@ -23631,6 +24008,19 @@ packages: postcss: 8.4.32 dev: true + /css-prefers-color-scheme@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /css-select-base-adapter@0.1.1: resolution: {integrity: sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==} dev: true @@ -24847,9 +25237,9 @@ packages: '@swc/core': 1.3.72 '@types/hast': 2.3.5 '@types/mdast': 3.0.12 - '@umijs/bundler-utils': 4.0.90 - '@umijs/core': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/bundler-utils': 4.1.2 + '@umijs/core': 4.1.2 + '@umijs/utils': 4.1.2 animated-scroll-to: 2.3.0 classnames: 2.3.2 codesandbox: 2.2.3 @@ -24898,7 +25288,7 @@ packages: remark-rehype: 10.1.0 sass: 1.65.1 sitemap: 7.1.1 - umi: 4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(jest@29.4.3)(prettier@2.8.4)(react-dom@18.1.0)(react@18.1.0)(sass@1.65.1)(typescript@4.9.5) + umi: 4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(jest@29.4.3)(prettier@2.8.4)(react-dom@18.1.0)(react@18.1.0)(sass@1.65.1)(typescript@4.9.5) unified: 10.1.2 unist-util-visit: 4.1.2 unist-util-visit-parents: 5.1.3 @@ -25149,6 +25539,7 @@ packages: engines: {node: '>=8.6'} dependencies: ansi-colors: 4.1.3 + dev: true /entities@1.1.2: resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} @@ -25340,6 +25731,10 @@ packages: resolution: {integrity: sha512-GDJTmDGd65qyDk9fGClO+MOUgHuBuGMCL6EawvLH8Ob+HN8ui1OJwa4fEK0qh3fg2ieT2/pdZj41yrxnmOrK8w==} dev: true + /es5-imcompatible-versions@0.1.89: + resolution: {integrity: sha512-metQ5Hi5dgBiaoc2VjGx2IABciw0djiE1+KbRWHbgQng9KnJQ1niBIA6vvLKWgA9R02kQZQRvFJ504ev0AQbzQ==} + dev: true + /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true @@ -26282,6 +26677,35 @@ packages: '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -26714,6 +27138,7 @@ packages: engines: {node: '>=6'} dependencies: eslint-visitor-keys: 1.3.0 + dev: true /eslint-utils@3.0.0(eslint@7.32.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} @@ -26743,6 +27168,7 @@ packages: /eslint-visitor-keys@1.3.0: resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} engines: {node: '>=4'} + dev: true /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} @@ -26806,6 +27232,7 @@ packages: v8-compile-cache: 2.3.0 transitivePeerDependencies: - supports-color + dev: true /eslint@8.35.0: resolution: {integrity: sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==} @@ -26861,6 +27288,7 @@ packages: acorn: 7.4.1 acorn-jsx: 5.3.2(acorn@7.4.1) eslint-visitor-keys: 1.3.0 + dev: true /espree@9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} @@ -27391,11 +27819,11 @@ packages: hasBin: true dependencies: '@microsoft/api-extractor': 7.34.3(@types/node@18.11.18) - '@umijs/babel-preset-umi': 4.0.90 - '@umijs/bundler-utils': 4.0.90 - '@umijs/bundler-webpack': 4.0.90(typescript@4.8.4) - '@umijs/core': 4.0.90 - '@umijs/utils': 4.0.90 + '@umijs/babel-preset-umi': 4.1.2 + '@umijs/bundler-utils': 4.1.2 + '@umijs/bundler-webpack': 4.1.2(typescript@4.8.4) + '@umijs/core': 4.1.2 + '@umijs/utils': 4.1.2 '@vercel/ncc': 0.33.3 babel-plugin-dynamic-import-node: 2.3.3 babel-plugin-module-resolver: 4.1.0 @@ -28098,6 +28526,7 @@ packages: /functional-red-black-tree@1.0.1: resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -29472,6 +29901,7 @@ packages: /ignore@4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} engines: {node: '>= 4'} + dev: true /ignore@5.2.0: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} @@ -29513,6 +29943,7 @@ packages: /immutable@4.1.0: resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} + dev: true /import-fresh@2.0.0: resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} @@ -31403,7 +31834,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.11 + graceful-fs: 4.2.10 /jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} @@ -35716,6 +36147,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-attribute-case-insensitive@5.0.1(postcss@8.4.35): + resolution: {integrity: sha512-wrt2VndqSLJpyBRNz9OmJcgnhI9MaongeWgapdBuUMu2a/KNJ8SENesG4SdiTnQwGO9b1VKbTWYAfCPeokLqZQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-calc@8.2.4(postcss@8.4.21): resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} peerDependencies: @@ -35768,6 +36212,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-clamp@4.1.0(postcss@8.4.35): + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-color-functional-notation@4.2.3(postcss@8.4.21): resolution: {integrity: sha512-5fbr6FzFzjwHXKsVnkmEYrJYG8VNNzvD1tAXaPPWR97S6rhKI5uh2yOfV5TAzhDkZoq4h+chxEplFDc8GeyFtw==} engines: {node: ^12 || ^14 || >=16} @@ -35793,6 +36250,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-color-functional-notation@4.2.3(postcss@8.4.35): + resolution: {integrity: sha512-5fbr6FzFzjwHXKsVnkmEYrJYG8VNNzvD1tAXaPPWR97S6rhKI5uh2yOfV5TAzhDkZoq4h+chxEplFDc8GeyFtw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-color-hex-alpha@8.0.4(postcss@8.4.21): resolution: {integrity: sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==} engines: {node: ^12 || ^14 || >=16} @@ -35818,6 +36288,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-color-hex-alpha@8.0.4(postcss@8.4.35): + resolution: {integrity: sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-color-rebeccapurple@7.1.0(postcss@8.4.21): resolution: {integrity: sha512-1jtE5AKnZcKq4pjOrltFHcbEM2/IvtbD1OdhZ/wqds18//bh0UmQkffcCkzDJU+/vGodfIsVQeKn+45CJvX9Bw==} engines: {node: ^12 || ^14 || >=16} @@ -35843,6 +36326,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-color-rebeccapurple@7.1.0(postcss@8.4.35): + resolution: {integrity: sha512-1jtE5AKnZcKq4pjOrltFHcbEM2/IvtbD1OdhZ/wqds18//bh0UmQkffcCkzDJU+/vGodfIsVQeKn+45CJvX9Bw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-colormin@5.3.0(postcss@8.4.21): resolution: {integrity: sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==} engines: {node: ^10 || ^12 || >=14.0} @@ -35928,6 +36424,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-custom-media@8.0.2(postcss@8.4.35): + resolution: {integrity: sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-custom-properties@12.1.8(postcss@8.4.21): resolution: {integrity: sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==} engines: {node: ^12 || ^14 || >=16} @@ -35953,6 +36462,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-custom-properties@12.1.8(postcss@8.4.35): + resolution: {integrity: sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-custom-selectors@6.0.3(postcss@8.4.21): resolution: {integrity: sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==} engines: {node: ^12 || ^14 || >=16} @@ -35978,6 +36500,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-custom-selectors@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-dir-pseudo-class@6.0.4(postcss@8.4.21): resolution: {integrity: sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==} engines: {node: ^12 || ^14 || >=16} @@ -36003,6 +36538,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-dir-pseudo-class@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-discard-comments@5.1.2(postcss@8.4.21): resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} engines: {node: ^10 || ^12 || >=14.0} @@ -36126,6 +36674,20 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-double-position-gradients@3.1.1(postcss@8.4.35): + resolution: {integrity: sha512-jM+CGkTs4FcG53sMPjrrGE0rIvLDdCrqMzgDC5fLI7JHDO7o6QG8C5TQBtExb13hdBdoH9C2QVbG4jo2y9lErQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-env-function@4.0.6(postcss@8.4.21): resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==} engines: {node: ^12 || ^14 || >=16} @@ -36151,6 +36713,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-env-function@4.0.6(postcss@8.4.35): + resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-flexbugs-fixes@5.0.2(postcss@8.4.21): resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} peerDependencies: @@ -36172,6 +36747,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-flexbugs-fixes@5.0.2(postcss@8.4.35): + resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} + peerDependencies: + postcss: ^8.1.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-focus-visible@6.0.4(postcss@8.4.21): resolution: {integrity: sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==} engines: {node: ^12 || ^14 || >=16} @@ -36197,6 +36783,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-focus-visible@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-focus-within@5.0.4(postcss@8.4.21): resolution: {integrity: sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==} engines: {node: ^12 || ^14 || >=16} @@ -36222,6 +36821,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-focus-within@5.0.4(postcss@8.4.35): + resolution: {integrity: sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-font-variant@5.0.0(postcss@8.4.21): resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} peerDependencies: @@ -36243,6 +36855,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-font-variant@5.0.0(postcss@8.4.35): + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-gap-properties@3.0.3(postcss@8.4.21): resolution: {integrity: sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==} engines: {node: ^12 || ^14 || >=16} @@ -36266,6 +36889,18 @@ packages: postcss: 8.4.32 dev: true + /postcss-gap-properties@3.0.3(postcss@8.4.35): + resolution: {integrity: sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-html@0.36.0(postcss-syntax@0.36.2)(postcss@7.0.39): resolution: {integrity: sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==} peerDependencies: @@ -36305,6 +36940,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-image-set-function@4.0.6(postcss@8.4.35): + resolution: {integrity: sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-import@14.1.0(postcss@8.4.21): resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} engines: {node: '>=10.0.0'} @@ -36340,6 +36988,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-initial@4.0.1(postcss@8.4.35): + resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} + peerDependencies: + postcss: ^8.0.0 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-js@4.0.0(postcss@8.4.21): resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} engines: {node: ^12 || ^14 || >= 16} @@ -36379,6 +37038,20 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-lab-function@4.2.0(postcss@8.4.35): + resolution: {integrity: sha512-Zb1EO9DGYfa3CP8LhINHCcTTCTLI+R3t7AX2mKsDzdgVQ/GkCpHOTgOr6HBHslP7XDdVbqgHW5vvRPMdVANQ8w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-less@3.1.4: resolution: {integrity: sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==} engines: {node: '>=6.14.4'} @@ -36463,6 +37136,18 @@ packages: postcss: 8.4.32 dev: true + /postcss-logical@5.0.4(postcss@8.4.35): + resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-media-minmax@5.0.0(postcss@8.4.21): resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} engines: {node: '>=10.0.0'} @@ -36486,6 +37171,18 @@ packages: postcss: 8.4.32 dev: true + /postcss-media-minmax@5.0.0(postcss@8.4.35): + resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.1.0 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-media-query-parser@0.2.3: resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} requiresBuild: true @@ -36750,6 +37447,20 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-nesting@10.1.8(postcss@8.4.35): + resolution: {integrity: sha512-txdb3/idHYsBbNDFo1PFY0ExCgH5nfWi8G5lO49e6iuU42TydbODTzJgF5UuL5bhgeSlnAtDgfFTDG0Cl1zaSQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/selector-specificity': 2.0.1(postcss-selector-parser@6.0.10)(postcss@8.4.35) + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-normalize-charset@5.1.0(postcss@8.4.21): resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} engines: {node: ^10 || ^12 || >=14.0} @@ -37040,6 +37751,18 @@ packages: postcss: 8.4.32 dev: true + /postcss-overflow-shorthand@3.0.3(postcss@8.4.35): + resolution: {integrity: sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-page-break@3.0.4(postcss@8.4.21): resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} peerDependencies: @@ -37061,6 +37784,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-page-break@3.0.4(postcss@8.4.35): + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-place@7.0.4(postcss@8.4.21): resolution: {integrity: sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==} engines: {node: ^12 || ^14 || >=16} @@ -37086,6 +37820,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-place@7.0.4(postcss@8.4.35): + resolution: {integrity: sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: true + /postcss-prefix-selector@1.16.0(postcss@8.4.21): resolution: {integrity: sha512-rdVMIi7Q4B0XbXqNUEI+Z4E+pueiu/CS5E6vRCQommzdQ/sgsS4dK42U7GX8oJR+TJOtT+Qv3GkNo6iijUMp3Q==} peerDependencies: @@ -37097,7 +37844,7 @@ packages: postcss: 8.4.21 dev: false - /postcss-prefix-selector@1.16.0(postcss@8.4.32): + /postcss-prefix-selector@1.16.0(postcss@8.4.35): resolution: {integrity: sha512-rdVMIi7Q4B0XbXqNUEI+Z4E+pueiu/CS5E6vRCQommzdQ/sgsS4dK42U7GX8oJR+TJOtT+Qv3GkNo6iijUMp3Q==} peerDependencies: postcss: '>4 <9' @@ -37105,7 +37852,7 @@ packages: postcss: optional: true dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: true /postcss-preset-env@7.5.0(postcss@8.4.21): @@ -37221,6 +37968,63 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-preset-env@7.5.0(postcss@8.4.35): + resolution: {integrity: sha512-0BJzWEfCdTtK2R3EiKKSdkE51/DI/BwnhlnicSW482Ym6/DGHud8K0wGLcdjip1epVX0HKo4c8zzTeV/SkiejQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + '@csstools/postcss-color-function': 1.1.0(postcss@8.4.35) + '@csstools/postcss-font-format-keywords': 1.0.0(postcss@8.4.35) + '@csstools/postcss-hwb-function': 1.0.1(postcss@8.4.35) + '@csstools/postcss-ic-unit': 1.0.0(postcss@8.4.35) + '@csstools/postcss-is-pseudo-class': 2.0.5(postcss@8.4.35) + '@csstools/postcss-normalize-display-values': 1.0.0(postcss@8.4.35) + '@csstools/postcss-oklab-function': 1.1.0(postcss@8.4.35) + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.35) + '@csstools/postcss-stepped-value-functions': 1.0.0(postcss@8.4.35) + '@csstools/postcss-unset-value': 1.0.1(postcss@8.4.35) + autoprefixer: 10.4.13(postcss@8.4.35) + browserslist: 4.22.2 + css-blank-pseudo: 3.0.3(postcss@8.4.35) + css-has-pseudo: 3.0.4(postcss@8.4.35) + css-prefers-color-scheme: 6.0.3(postcss@8.4.35) + cssdb: 6.6.3 + postcss: 8.4.35 + postcss-attribute-case-insensitive: 5.0.1(postcss@8.4.35) + postcss-clamp: 4.1.0(postcss@8.4.35) + postcss-color-functional-notation: 4.2.3(postcss@8.4.35) + postcss-color-hex-alpha: 8.0.4(postcss@8.4.35) + postcss-color-rebeccapurple: 7.1.0(postcss@8.4.35) + postcss-custom-media: 8.0.2(postcss@8.4.35) + postcss-custom-properties: 12.1.8(postcss@8.4.35) + postcss-custom-selectors: 6.0.3(postcss@8.4.35) + postcss-dir-pseudo-class: 6.0.4(postcss@8.4.35) + postcss-double-position-gradients: 3.1.1(postcss@8.4.35) + postcss-env-function: 4.0.6(postcss@8.4.35) + postcss-focus-visible: 6.0.4(postcss@8.4.35) + postcss-focus-within: 5.0.4(postcss@8.4.35) + postcss-font-variant: 5.0.0(postcss@8.4.35) + postcss-gap-properties: 3.0.3(postcss@8.4.35) + postcss-image-set-function: 4.0.6(postcss@8.4.35) + postcss-initial: 4.0.1(postcss@8.4.35) + postcss-lab-function: 4.2.0(postcss@8.4.35) + postcss-logical: 5.0.4(postcss@8.4.35) + postcss-media-minmax: 5.0.0(postcss@8.4.35) + postcss-nesting: 10.1.8(postcss@8.4.35) + postcss-opacity-percentage: 1.1.2 + postcss-overflow-shorthand: 3.0.3(postcss@8.4.35) + postcss-page-break: 3.0.4(postcss@8.4.35) + postcss-place: 7.0.4(postcss@8.4.35) + postcss-pseudo-class-any-link: 7.1.4(postcss@8.4.35) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.4.35) + postcss-selector-not: 5.0.0(postcss@8.4.35) + postcss-value-parser: 4.2.0 + dev: true + /postcss-pseudo-class-any-link@7.1.4(postcss@8.4.21): resolution: {integrity: sha512-JxRcLXm96u14N3RzFavPIE9cRPuOqLDuzKeBsqi4oRk4vt8n0A7I0plFs/VXTg7U2n7g/XkQi0OwqTO3VWBfEg==} engines: {node: ^12 || ^14 || >=16} @@ -37246,6 +38050,19 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-pseudo-class-any-link@7.1.4(postcss@8.4.35): + resolution: {integrity: sha512-JxRcLXm96u14N3RzFavPIE9cRPuOqLDuzKeBsqi4oRk4vt8n0A7I0plFs/VXTg7U2n7g/XkQi0OwqTO3VWBfEg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.10 + dev: true + /postcss-reduce-initial@5.1.0(postcss@8.4.21): resolution: {integrity: sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==} engines: {node: ^10 || ^12 || >=14.0} @@ -37321,6 +38138,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-replace-overflow-wrap@4.0.0(postcss@8.4.35): + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-resolve-nested-selector@0.1.1: resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} requiresBuild: true @@ -37380,6 +38208,18 @@ packages: postcss: 8.4.32 dev: true + /postcss-selector-not@5.0.0(postcss@8.4.35): + resolution: {integrity: sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==} + peerDependencies: + postcss: ^8.1.0 + peerDependenciesMeta: + postcss: + optional: true + dependencies: + balanced-match: 1.0.2 + postcss: 8.4.35 + dev: true + /postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -37518,6 +38358,32 @@ packages: postcss: 8.4.32 dev: true + /postcss-syntax@0.36.2(postcss@8.4.35): + resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==} + peerDependencies: + postcss: '>=5.0.0' + postcss-html: '*' + postcss-jsx: '*' + postcss-less: '*' + postcss-markdown: '*' + postcss-scss: '*' + peerDependenciesMeta: + postcss: + optional: true + postcss-html: + optional: true + postcss-jsx: + optional: true + postcss-less: + optional: true + postcss-markdown: + optional: true + postcss-scss: + optional: true + dependencies: + postcss: 8.4.35 + dev: true + /postcss-unique-selectors@5.1.1(postcss@8.4.21): resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} engines: {node: ^10 || ^12 || >=14.0} @@ -37598,6 +38464,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} dev: false @@ -37831,6 +38705,7 @@ packages: /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + dev: true /promise-all-reject-late@1.0.1: resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} @@ -43818,8 +44693,8 @@ packages: fsevents: 2.3.3 dev: true - /rollup@3.20.7: - resolution: {integrity: sha512-P7E2zezKSLhWnTz46XxjSmInrbOCiul1yf+kJccMxT56vxjHwCbDfoLbiqFgu+WQoo9ij2PkraYaBstgB2prBA==} + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -44003,6 +44878,7 @@ packages: chokidar: 3.5.3 immutable: 4.1.0 source-map-js: 1.0.2 + dev: true /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -45927,6 +46803,10 @@ packages: /systemjs@6.14.2: resolution: {integrity: sha512-1TlOwvKWdXxAY9vba+huLu99zrQURDWA8pUTYsRIYDZYQbGyK+pyEP4h4dlySsqo7ozyJBmYD20F+iUHhAltEg==} + /systemjs@6.14.3: + resolution: {integrity: sha512-hQv45irdhXudAOr8r6SVSpJSGtogdGZUbJBRKCE5nsIS7tsxxvnIHqT4IOPWj+P+HcSzeWzHlGCGpmhPDIKe+w==} + dev: true + /table@6.8.0: resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==} engines: {node: '>=10.0.0'} @@ -46944,8 +47824,8 @@ packages: dependencies: '@alita/plugins': 3.1.0(@types/react-dom@18.0.10)(@types/react@18.0.26)(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2) '@umijs/max-plugin-openapi': 1.0.7(eslint@8.35.0)(jest@29.4.3)(prettier@2.8.4)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.5) - '@umijs/plugins': 4.0.90(@types/react-dom@18.0.10)(@types/react@18.0.26)(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2) - umi: 4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(eslint@8.35.0)(jest@29.4.3)(prettier@2.8.4)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.5) + '@umijs/plugins': 4.1.2(@types/react-dom@18.0.10)(@types/react@18.0.26)(antd@4.23.6)(react-dom@17.0.2)(react@17.0.2) + umi: 4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(eslint@8.35.0)(jest@29.4.3)(prettier@2.8.4)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.5) transitivePeerDependencies: - '@babel/core' - '@types/lodash.merge' @@ -47073,21 +47953,21 @@ packages: - webpack-plugin-serve dev: true - /umi@4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(eslint@8.35.0)(jest@29.4.3)(prettier@2.8.4)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.5): - resolution: {integrity: sha512-jgTiJv4fnHTC2a1hwM+dXmkyKoaZaDTqiJv2w4welDJUfpP+uyhR0zcDXwLuu9KTaUUlGxbIW0fGAeM//fxZ+Q==} + /umi@4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(eslint@8.35.0)(jest@29.4.3)(prettier@2.8.4)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.5): + resolution: {integrity: sha512-lP/qxOo4CmjcJKFrOy8PCIsW/nRLGXsH5CY0/WEUAkHd63M+osbv/t1fhAhsgKpb7/ofaiSpkNsev5XJz+M3aQ==} engines: {node: '>=14'} hasBin: true dependencies: - '@babel/runtime': 7.23.2 - '@umijs/bundler-utils': 4.0.90 - '@umijs/bundler-webpack': 4.0.90(typescript@4.9.5) - '@umijs/core': 4.0.90 - '@umijs/lint': 4.0.90(eslint@8.35.0)(jest@29.4.3)(typescript@4.9.5) - '@umijs/preset-umi': 4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5) - '@umijs/renderer-react': 4.0.90(react-dom@17.0.2)(react@17.0.2) - '@umijs/server': 4.0.90 - '@umijs/test': 4.0.90 - '@umijs/utils': 4.0.90 + '@babel/runtime': 7.23.6 + '@umijs/bundler-utils': 4.1.2 + '@umijs/bundler-webpack': 4.1.2(typescript@4.9.5) + '@umijs/core': 4.1.2 + '@umijs/lint': 4.1.2(eslint@8.35.0)(jest@29.4.3)(typescript@4.9.5) + '@umijs/preset-umi': 4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5) + '@umijs/renderer-react': 4.1.2(react-dom@17.0.2)(react@17.0.2) + '@umijs/server': 4.1.2 + '@umijs/test': 4.1.2 + '@umijs/utils': 4.1.2 prettier-plugin-organize-imports: 3.2.2(prettier@2.8.4)(typescript@4.9.5) prettier-plugin-packagejson: 2.4.3(prettier@2.8.4) transitivePeerDependencies: @@ -47123,21 +48003,21 @@ packages: - webpack-plugin-serve dev: true - /umi@4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(jest@29.4.3)(prettier@2.8.4)(react-dom@18.1.0)(react@18.1.0)(sass@1.65.1)(typescript@4.9.5): - resolution: {integrity: sha512-jgTiJv4fnHTC2a1hwM+dXmkyKoaZaDTqiJv2w4welDJUfpP+uyhR0zcDXwLuu9KTaUUlGxbIW0fGAeM//fxZ+Q==} + /umi@4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(jest@29.4.3)(prettier@2.8.4)(react-dom@18.1.0)(react@18.1.0)(sass@1.65.1)(typescript@4.9.5): + resolution: {integrity: sha512-lP/qxOo4CmjcJKFrOy8PCIsW/nRLGXsH5CY0/WEUAkHd63M+osbv/t1fhAhsgKpb7/ofaiSpkNsev5XJz+M3aQ==} engines: {node: '>=14'} hasBin: true dependencies: - '@babel/runtime': 7.23.2 - '@umijs/bundler-utils': 4.0.90 - '@umijs/bundler-webpack': 4.0.90(typescript@4.9.5) - '@umijs/core': 4.0.90 - '@umijs/lint': 4.0.90(jest@29.4.3)(typescript@4.9.5) - '@umijs/preset-umi': 4.0.90(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5) - '@umijs/renderer-react': 4.0.90(react-dom@18.1.0)(react@18.1.0) - '@umijs/server': 4.0.90 - '@umijs/test': 4.0.90 - '@umijs/utils': 4.0.90 + '@babel/runtime': 7.23.6 + '@umijs/bundler-utils': 4.1.2 + '@umijs/bundler-webpack': 4.1.2(typescript@4.9.5) + '@umijs/core': 4.1.2 + '@umijs/lint': 4.1.2(jest@29.4.3)(typescript@4.9.5) + '@umijs/preset-umi': 4.1.2(@types/node@18.11.18)(@types/react@18.0.26)(sass@1.65.1)(typescript@4.9.5) + '@umijs/renderer-react': 4.1.2(react-dom@18.1.0)(react@18.1.0) + '@umijs/server': 4.1.2 + '@umijs/test': 4.1.2 + '@umijs/utils': 4.1.2 prettier-plugin-organize-imports: 3.2.2(prettier@2.8.4)(typescript@4.9.5) prettier-plugin-packagejson: 2.4.3(prettier@2.8.4) transitivePeerDependencies: @@ -47985,11 +48865,48 @@ packages: '@types/node': 18.11.18 esbuild: 0.17.19 less: 4.1.3 - postcss: 8.4.21 - rollup: 3.20.7 + postcss: 8.4.35 + rollup: 3.29.4 sass: 1.65.1 optionalDependencies: fsevents: 2.3.3 + dev: true + + /vite@4.5.2(@types/node@18.11.18)(less@4.1.3): + resolution: {integrity: sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.18 + esbuild: 0.18.20 + less: 4.1.3 + postcss: 8.4.35 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 /vitest@0.16.0: resolution: {integrity: sha512-Ntp6jrM8wf2NMtamMBLkRBBdeqHkgAH/WMh5Xryts1j2ft2D8QZQbiSVFkSl4WmEQzcPP0YM069g/Ga1vtnEtg==} From 66b4809a6214c99f77dfba5492d95d6a7f91a1c8 Mon Sep 17 00:00:00 2001 From: Devin Date: Fri, 15 Mar 2024 00:45:52 +0800 Subject: [PATCH 02/21] chore: update TS and husky version used by generate command(#12191) * chore(dependencies): Update typescript version to ^5 * chore(preset-umi): Upgrade husky from v8 to v9 * chore(deps): Update swr and husky versions * chore: update version --------- Co-authored-by: fz6m <59400654+fz6m@users.noreply.github.com> --- examples/libs/package.json | 2 +- .../templates/max/.husky/commit-msg | 3 --- .../templates/max/.husky/pre-commit | 3 --- .../create-umi/templates/max/package.json.tpl | 4 ++-- .../src/commands/generators/jest.ts | 2 +- .../src/commands/generators/precommit.ts | 16 +++++---------- .../src/commands/generators/tsconfig.ts | 2 +- pnpm-lock.yaml | 20 +++++++++---------- 8 files changed, 19 insertions(+), 33 deletions(-) diff --git a/examples/libs/package.json b/examples/libs/package.json index e8c1c1254815..f5a9163355bb 100644 --- a/examples/libs/package.json +++ b/examples/libs/package.json @@ -15,7 +15,7 @@ "react-redux": "^8.0.5", "redux": "^4.2.1", "swiper": "^8.4.5", - "swr": "2.0.4", + "swr": "^2.2.5", "umi": "workspace:*" } } diff --git a/packages/create-umi/templates/max/.husky/commit-msg b/packages/create-umi/templates/max/.husky/commit-msg index d432988a8ce3..bdab3b586d0d 100755 --- a/packages/create-umi/templates/max/.husky/commit-msg +++ b/packages/create-umi/templates/max/.husky/commit-msg @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install max verify-commit $1 diff --git a/packages/create-umi/templates/max/.husky/pre-commit b/packages/create-umi/templates/max/.husky/pre-commit index 8f72062a2cb0..1f54b373ad9e 100755 --- a/packages/create-umi/templates/max/.husky/pre-commit +++ b/packages/create-umi/templates/max/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install lint-staged --quiet diff --git a/packages/create-umi/templates/max/package.json.tpl b/packages/create-umi/templates/max/package.json.tpl index 34e249cc6635..fc5385d67d44 100644 --- a/packages/create-umi/templates/max/package.json.tpl +++ b/packages/create-umi/templates/max/package.json.tpl @@ -5,7 +5,7 @@ "dev": "max dev", "build": "max build", "format": "prettier --cache --write .",{{#withHusky}} - "prepare": "husky install",{{/withHusky}} + "prepare": "husky",{{/withHusky}} "postinstall": "max setup", "setup": "max setup", "start": "npm run dev" @@ -19,7 +19,7 @@ "devDependencies": { "@types/react": "^18.0.33", "@types/react-dom": "^18.0.11",{{#withHusky}} - "husky": "^8.0.3",{{/withHusky}} + "husky": "^9",{{/withHusky}} "lint-staged": "^13.2.0", "prettier": "^2.8.7", "prettier-plugin-organize-imports": "^3.2.2", diff --git a/packages/preset-umi/src/commands/generators/jest.ts b/packages/preset-umi/src/commands/generators/jest.ts index 94001c11a255..df8af54818bb 100644 --- a/packages/preset-umi/src/commands/generators/jest.ts +++ b/packages/preset-umi/src/commands/generators/jest.ts @@ -41,7 +41,7 @@ export default (api: IApi) => { jest: jestMajorVersion, '@types/jest': jestMajorVersion, // we use `jest.config.ts` so jest needs ts and ts-node - typescript: '^4', + typescript: '^5', 'ts-node': '^10', 'cross-env': '^7', }; diff --git a/packages/preset-umi/src/commands/generators/precommit.ts b/packages/preset-umi/src/commands/generators/precommit.ts index ec8e366a4654..7a08d8c8ec8c 100644 --- a/packages/preset-umi/src/commands/generators/precommit.ts +++ b/packages/preset-umi/src/commands/generators/precommit.ts @@ -1,6 +1,6 @@ import { GeneratorType } from '@umijs/core'; -import { logger, execa } from '@umijs/utils'; -import { existsSync, writeFileSync, mkdirSync } from 'fs'; +import { execa, logger } from '@umijs/utils'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { join } from 'path'; import { IApi } from '../../types'; import { GeneratorHelper } from './utils'; @@ -20,13 +20,13 @@ export default (api: IApi) => { const cliName = api.appData.umi.cliName; h.addDevDeps({ - husky: '^8', + husky: '^9', prettier: '^2', - typescript: '^4', + typescript: '^5', 'lint-staged': '^13', }); - h.addScript('prepare', 'husky install'); + h.addScript('prepare', 'husky'); // create .lintstagedrc if ( @@ -69,9 +69,6 @@ export default (api: IApi) => { writeFileSync( join(api.cwd, '.husky/commit-msg'), ` -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install ${cliName} verify-commit $1 `.trimStart(), ); @@ -86,9 +83,6 @@ npx --no-install ${cliName} verify-commit $1 writeFileSync( join(api.cwd, '.husky/pre-commit'), ` -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install lint-staged --quiet `.trimStart(), ); diff --git a/packages/preset-umi/src/commands/generators/tsconfig.ts b/packages/preset-umi/src/commands/generators/tsconfig.ts index 3fc6a2055385..4d4985bc6779 100644 --- a/packages/preset-umi/src/commands/generators/tsconfig.ts +++ b/packages/preset-umi/src/commands/generators/tsconfig.ts @@ -33,7 +33,7 @@ export default (api: IApi) => { const reactMajorVersion = parseInt(reactVersion.split('.')[0], 10) || 18; h.addDevDeps({ - typescript: '^4', + typescript: '^5', '@types/react': `^${reactMajorVersion}`, '@types/react-dom': `^${reactMajorVersion}`, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6403eff4970..6852c0884cf0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -698,8 +698,8 @@ importers: specifier: ^8.4.5 version: 8.4.5 swr: - specifier: 2.0.4 - version: 2.0.4(react@18.1.0) + specifier: ^2.2.5 + version: 2.2.5(react@18.1.0) umi: specifier: workspace:* version: link:../../packages/umi @@ -20020,9 +20020,6 @@ packages: /ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependenciesMeta: - ajv: - optional: true dependencies: ajv: 8.11.0 @@ -22962,6 +22959,10 @@ packages: - react-dom dev: true + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + /clipboardy@2.1.0: resolution: {integrity: sha512-2pzOUxWcLlXWtn+Jd6js3o12TysNOOVes/aQfg+MT/35vrxWzedHlLwyoJpXjsFKWm95BTNEcMGD9+a7mKzZkQ==} engines: {node: '>=8'} @@ -46776,15 +46777,15 @@ packages: react: 18.1.0 dev: false - /swr@2.0.4(react@18.1.0): - resolution: {integrity: sha512-4GUiTjknRUVuw4MWUHR7mzJ9G/DWL+yZz/TgWDfiA0OZ9tL6qyrTkN2wPeboBpL3OJTkej3pexh3mWCnv8cFkQ==} - engines: {pnpm: '7'} + /swr@2.2.5(react@18.1.0): + resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 peerDependenciesMeta: react: optional: true dependencies: + client-only: 0.0.1 react: 18.1.0 use-sync-external-store: 1.2.0(react@18.1.0) dev: false @@ -46821,9 +46822,6 @@ packages: resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==} engines: {node: '>=12.13.0'} hasBin: true - peerDependenciesMeta: - postcss: - optional: true dependencies: arg: 5.0.2 chokidar: 3.5.3 From 23fd3c7623fd4fb166ea125d580eef62c5c64b47 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 17 Mar 2024 01:13:48 +0800 Subject: [PATCH 03/21] docs(antd): conditions for runtime config to take effect (#12202) * docs: update `antd.md` * docs: update --------- Co-authored-by: fz6m <59400654+fz6m@users.noreply.github.com> --- docs/docs/docs/max/antd.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/docs/docs/max/antd.md b/docs/docs/docs/max/antd.md index c5dc72f9f827..511a5801f8ee 100644 --- a/docs/docs/docs/max/antd.md +++ b/docs/docs/docs/max/antd.md @@ -139,7 +139,17 @@ export default { ### 运行时配置 -在 app.ts(x) 文件中可以对 antd 进行更丰富的配置,比如配置 antd5 的预设算法和 message 最大显示数: +在 `app.ts(x)` 运行时配置中可以修改 antd `ConfigProvider` 的值,使用此功能前,**确保你已经打开了 `antd.configProvider` 选项**,否则对 `ConfigProvider` 的修改不会生效: + +```ts +// .umirc.ts + + antd: { + configProvider: {} + } +``` + +如配置 antd 5 的主题预设算法和 `message` 弹出框最大数: ```ts // app.ts From c952494d991919dcc9963f3c6335d23553b92a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=87=AF=E6=96=8C?= <799870386@qq.com> Date: Sun, 17 Mar 2024 09:10:45 +0800 Subject: [PATCH 04/21] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8FUMI=5FDEV=5FSERVER=5FCOMPRESS?= =?UTF-8?q?=E6=9D=A5=E6=8E=A7=E5=88=B6dev=20server=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E5=8E=8B=E7=BC=A9=20(#12166)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 使用环境变量UMI_DEV_SERVER_COMPRESS来控制dev server是否进行压缩 * feat: 使用环境变量UMI_DEV_SERVER_COMPRESS来控制dev server是否进行压缩 * example: update * docs: `UMI_DEV_SERVER_COMPRESS` * example: update * docs: comment --------- Co-authored-by: 王凯斌 Co-authored-by: fz6m <59400654+fz6m@users.noreply.github.com> --- docs/docs/docs/guides/env-variables.md | 8 +++ examples/with-no-compress-for-sse/.umirc.ts | 1 + .../with-no-compress-for-sse/package.json | 15 ++++++ .../with-no-compress-for-sse/pages/index.tsx | 52 +++++++++++++++++++ examples/with-no-compress-for-sse/plugin.ts | 8 +++ examples/with-no-compress-for-sse/readme.md | 9 ++++ .../sse-middleware.ts | 29 +++++++++++ packages/bundler-webpack/src/server/server.ts | 6 ++- pnpm-lock.yaml | 10 ++++ 9 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 examples/with-no-compress-for-sse/.umirc.ts create mode 100644 examples/with-no-compress-for-sse/package.json create mode 100644 examples/with-no-compress-for-sse/pages/index.tsx create mode 100644 examples/with-no-compress-for-sse/plugin.ts create mode 100644 examples/with-no-compress-for-sse/readme.md create mode 100644 examples/with-no-compress-for-sse/sse-middleware.ts diff --git a/docs/docs/docs/guides/env-variables.md b/docs/docs/docs/guides/env-variables.md index 8f18c40e7db8..9114a8e4c38a 100644 --- a/docs/docs/docs/guides/env-variables.md +++ b/docs/docs/docs/guides/env-variables.md @@ -198,6 +198,14 @@ $ UMI_PLUGINS=./path/to/plugin1,./path/to/plugin2 umi dev $ UMI_PRESETS=./path/to/preset1,./path/to/preset2 umi dev ``` +### UMI_DEV_SERVER_COMPRESS + +默认 Umi 开发服务器自带 [compress](https://github.com/expressjs/compression) 压缩中间件,这会使开发时 SSE 数据的传输 [无法流式获取](https://github.com/umijs/umi/issues/12144) ,通过指定 `UMI_DEV_SERVER_COMPRESS=none` 来关闭 compress 压缩功能: + +```bash + UMI_DEV_SERVER_COMPRESS=none umi dev +``` + ### WEBPACK_FS_CACHE_DEBUG 开启 webpack 的物理缓存 debug 日志。 diff --git a/examples/with-no-compress-for-sse/.umirc.ts b/examples/with-no-compress-for-sse/.umirc.ts new file mode 100644 index 000000000000..ff8b4c56321a --- /dev/null +++ b/examples/with-no-compress-for-sse/.umirc.ts @@ -0,0 +1 @@ +export default {}; diff --git a/examples/with-no-compress-for-sse/package.json b/examples/with-no-compress-for-sse/package.json new file mode 100644 index 000000000000..949318ff8d8f --- /dev/null +++ b/examples/with-no-compress-for-sse/package.json @@ -0,0 +1,15 @@ +{ + "name": "@example/with-no-compress-for-sse", + "private": true, + "scripts": { + "build": "umi build", + "dev": "umi dev", + "dev:nocompress": "cross-env UMI_DEV_SERVER_COMPRESS=none npm run dev" + }, + "dependencies": { + "umi": "workspace:*" + }, + "devDependencies": { + "cross-env": "^7.0.3" + } +} diff --git a/examples/with-no-compress-for-sse/pages/index.tsx b/examples/with-no-compress-for-sse/pages/index.tsx new file mode 100644 index 000000000000..2ef38e04f2ef --- /dev/null +++ b/examples/with-no-compress-for-sse/pages/index.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; + +class Event { + data: string; + timeString: string; + + constructor(data: string) { + this.data = data; + this.timeString = new Date().toLocaleTimeString(); + } +} + +export default function HomePage() { + const [events, setEvents] = useState([]); + + useEffect(() => { + console.log('开始请求'); + const eventSource = new EventSource('/events/number'); + let startEvent = new Event('开始请求'); + setEvents((prev) => [...prev, startEvent]); + eventSource.onmessage = function (e: any) { + let item = new Event(e.data); + setEvents((prev) => [...prev, item]); + }; + eventSource.onerror = (e) => { + console.log('EventSource failed:', e); + eventSource.close(); + }; + }, []); + + return ( +
+

{`演示:当默认存在 compress 时,数据无法流式获取。`}

+ + + + + + + + + {events.map((event, index) => ( + + + + + ))} + +
事件内容接收时间
{event.data}{event.timeString}
+
+ ); +} diff --git a/examples/with-no-compress-for-sse/plugin.ts b/examples/with-no-compress-for-sse/plugin.ts new file mode 100644 index 000000000000..e3d4901cdbbb --- /dev/null +++ b/examples/with-no-compress-for-sse/plugin.ts @@ -0,0 +1,8 @@ +import { IApi } from 'umi'; +import { sseMiddleware } from './sse-middleware'; + +export default (api: IApi) => { + api.onBeforeMiddleware(({ app }) => { + sseMiddleware(app); + }); +}; diff --git a/examples/with-no-compress-for-sse/readme.md b/examples/with-no-compress-for-sse/readme.md new file mode 100644 index 000000000000..15e5513baac3 --- /dev/null +++ b/examples/with-no-compress-for-sse/readme.md @@ -0,0 +1,9 @@ +# with-no-compress-for-sse + +### 背景 + +[来源](https://github.com/umijs/umi/issues/12144),在开发环境下由于 umi dev server 内置了 `compress` 中间件,导致 SSE 流在开发时传递不符合预期。 + +### 解决 + +本示例演示了此问题,并通过启动时附加 `UMI_DEV_SERVER_COMPRESS=none` 来关闭 `compress` 中间件,使 SSE 在本地开发时正常运作。 diff --git a/examples/with-no-compress-for-sse/sse-middleware.ts b/examples/with-no-compress-for-sse/sse-middleware.ts new file mode 100644 index 000000000000..bddbfcb10b11 --- /dev/null +++ b/examples/with-no-compress-for-sse/sse-middleware.ts @@ -0,0 +1,29 @@ +import type { Express } from '@umijs/bundler-utils/compiled/express'; + +export const sseMiddleware = (app: Express) => { + app.get('/events/number', (req, res) => { + console.log('new connection'); + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + }); + + let counter = 1; + const intervalId = setInterval(() => { + if (counter === 5) { + clearInterval(intervalId); + res.end(`data: 事件${counter}\n\n`); + return; + } + res.write(`data: 事件${counter}\n\n`); + + counter++; + }, 1000); + + req.on('close', () => { + clearInterval(intervalId); + res.end(); + }); + }); +}; diff --git a/packages/bundler-webpack/src/server/server.ts b/packages/bundler-webpack/src/server/server.ts index 2553b66e7ded..b6296e867bae 100644 --- a/packages/bundler-webpack/src/server/server.ts +++ b/packages/bundler-webpack/src/server/server.ts @@ -44,8 +44,10 @@ export async function createServer(opts: IOpts): Promise { }), ); - // compression - app.use(require('@umijs/bundler-webpack/compiled/compression')()); + // See https://github.com/umijs/umi/issues/12144 + if (process.env.UMI_DEV_SERVER_COMPRESS !== 'none') { + app.use(require('@umijs/bundler-webpack/compiled/compression')()); + } // debug all js file app.use((req, res, next) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6852c0884cf0..c40b3bfe9894 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1383,6 +1383,16 @@ importers: specifier: ^18.0.10 version: 18.0.10 + examples/with-no-compress-for-sse: + dependencies: + umi: + specifier: workspace:* + version: link:../../packages/umi + devDependencies: + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + examples/with-react-17: dependencies: react: From 4957ad26e1ed7ed0387ff7854b3e0033842f513e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?chencheng=20=28=E4=BA=91=E8=B0=A6=29?= Date: Sun, 17 Mar 2024 19:51:37 +0800 Subject: [PATCH 05/21] docs: support en docs (#12210) * docs: directory structure for en docs * docs: translate * chore: update --------- Co-authored-by: PeachScript --- docs/.dumi/global.less | 9 + docs/.dumi/pages/index.en-US.tsx | 1 + docs/.dumirc.ts | 175 +---- docs/docs/blog/code-splitting.en-US.md | 50 ++ .../docs/blog/develop-blog-using-umi.en-US.md | 613 +++++++++++++++ docs/docs/blog/legacy-browser.en-US.md | 127 +++ docs/docs/blog/mfsu-faster-than-vite.en-US.md | 41 + .../docs/blog/mfsu-independent-usage.en-US.md | 257 ++++++ docs/docs/blog/umi-4-rc.en-US.md | 150 ++++ docs/docs/blog/webpack-5-prod-cache.en-US.md | 99 +++ docs/docs/docs/api/api.en-US.md | 741 ++++++++++++++++++ docs/docs/docs/api/commands.en-US.md | 308 ++++++++ docs/docs/docs/api/config.en-US.md | 591 ++++++++++++++ docs/docs/docs/api/plugin-api.en-US.md | 429 ++++++++++ docs/docs/docs/api/runtime-config.en-US.md | 319 ++++++++ docs/docs/docs/guides/boilerplate.en-US.md | 54 ++ docs/docs/docs/guides/client-loader.en-US.md | 60 ++ docs/docs/docs/guides/debug.en-US.md | 78 ++ .../docs/guides/directory-structure.en-US.md | 307 ++++++++ docs/docs/docs/guides/env-variables.en-US.md | 216 +++++ docs/docs/docs/guides/generator.en-US.md | 352 +++++++++ .../docs/docs/guides/getting-started.en-US.md | 219 ++++++ docs/docs/docs/guides/lint.en-US.md | 146 ++++ docs/docs/docs/guides/mfsu.en-US.md | 156 ++++ docs/docs/docs/guides/mock.en-US.md | 159 ++++ docs/docs/docs/guides/mpa.en-US.md | 160 ++++ docs/docs/docs/guides/plugins.en-US.md | 177 +++++ docs/docs/docs/guides/prepare.en-US.md | 71 ++ docs/docs/docs/guides/proxy.en-US.md | 35 + docs/docs/docs/guides/routes.en-US.md | 500 ++++++++++++ docs/docs/docs/guides/styling.en-US.md | 154 ++++ docs/docs/docs/guides/test.en-US.md | 203 +++++ docs/docs/docs/guides/typescript.en-US.md | 27 + docs/docs/docs/guides/use-plugins.en-US.md | 46 ++ docs/docs/docs/guides/use-vue.en-US.md | 221 ++++++ .../docs/docs/introduce/contributing.en-US.md | 198 +++++ docs/docs/docs/introduce/faq.en-US.md | 207 +++++ docs/docs/docs/introduce/introduce.en-US.md | 57 ++ docs/docs/docs/introduce/philosophy.en-US.md | 66 ++ .../docs/introduce/upgrade-to-umi-4.en-US.md | 305 +++++++ docs/docs/docs/max/access.en-US.md | 168 ++++ docs/docs/docs/max/analytics.en-US.md | 31 + docs/docs/docs/max/antd.en-US.md | 232 ++++++ docs/docs/docs/max/charts.en-US.md | 535 +++++++++++++ docs/docs/docs/max/data-flow.en-US.md | 232 ++++++ docs/docs/docs/max/dva.en-US.md | 343 ++++++++ docs/docs/docs/max/i18n.en-US.md | 529 +++++++++++++ docs/docs/docs/max/introduce.en-US.md | 49 ++ docs/docs/docs/max/layout-menu.en-US.md | 243 ++++++ docs/docs/docs/max/mf.en-US.md | 432 ++++++++++ docs/docs/docs/max/micro-frontend.en-US.md | 470 +++++++++++ docs/docs/docs/max/react-query.en-US.md | 93 +++ docs/docs/docs/max/request.en-US.md | 406 ++++++++++ docs/docs/docs/max/styled-components.en-US.md | 94 +++ docs/docs/docs/max/valtio.en-US.md | 198 +++++ 55 files changed, 11991 insertions(+), 148 deletions(-) create mode 100644 docs/.dumi/global.less create mode 100644 docs/.dumi/pages/index.en-US.tsx create mode 100644 docs/docs/blog/code-splitting.en-US.md create mode 100644 docs/docs/blog/develop-blog-using-umi.en-US.md create mode 100644 docs/docs/blog/legacy-browser.en-US.md create mode 100644 docs/docs/blog/mfsu-faster-than-vite.en-US.md create mode 100644 docs/docs/blog/mfsu-independent-usage.en-US.md create mode 100644 docs/docs/blog/umi-4-rc.en-US.md create mode 100644 docs/docs/blog/webpack-5-prod-cache.en-US.md create mode 100644 docs/docs/docs/api/api.en-US.md create mode 100644 docs/docs/docs/api/commands.en-US.md create mode 100644 docs/docs/docs/api/config.en-US.md create mode 100644 docs/docs/docs/api/plugin-api.en-US.md create mode 100644 docs/docs/docs/api/runtime-config.en-US.md create mode 100644 docs/docs/docs/guides/boilerplate.en-US.md create mode 100644 docs/docs/docs/guides/client-loader.en-US.md create mode 100644 docs/docs/docs/guides/debug.en-US.md create mode 100644 docs/docs/docs/guides/directory-structure.en-US.md create mode 100644 docs/docs/docs/guides/env-variables.en-US.md create mode 100644 docs/docs/docs/guides/generator.en-US.md create mode 100644 docs/docs/docs/guides/getting-started.en-US.md create mode 100644 docs/docs/docs/guides/lint.en-US.md create mode 100644 docs/docs/docs/guides/mfsu.en-US.md create mode 100644 docs/docs/docs/guides/mock.en-US.md create mode 100644 docs/docs/docs/guides/mpa.en-US.md create mode 100644 docs/docs/docs/guides/plugins.en-US.md create mode 100644 docs/docs/docs/guides/prepare.en-US.md create mode 100644 docs/docs/docs/guides/proxy.en-US.md create mode 100644 docs/docs/docs/guides/routes.en-US.md create mode 100644 docs/docs/docs/guides/styling.en-US.md create mode 100644 docs/docs/docs/guides/test.en-US.md create mode 100644 docs/docs/docs/guides/typescript.en-US.md create mode 100644 docs/docs/docs/guides/use-plugins.en-US.md create mode 100644 docs/docs/docs/guides/use-vue.en-US.md create mode 100644 docs/docs/docs/introduce/contributing.en-US.md create mode 100644 docs/docs/docs/introduce/faq.en-US.md create mode 100644 docs/docs/docs/introduce/introduce.en-US.md create mode 100644 docs/docs/docs/introduce/philosophy.en-US.md create mode 100644 docs/docs/docs/introduce/upgrade-to-umi-4.en-US.md create mode 100644 docs/docs/docs/max/access.en-US.md create mode 100644 docs/docs/docs/max/analytics.en-US.md create mode 100644 docs/docs/docs/max/antd.en-US.md create mode 100644 docs/docs/docs/max/charts.en-US.md create mode 100644 docs/docs/docs/max/data-flow.en-US.md create mode 100644 docs/docs/docs/max/dva.en-US.md create mode 100644 docs/docs/docs/max/i18n.en-US.md create mode 100644 docs/docs/docs/max/introduce.en-US.md create mode 100644 docs/docs/docs/max/layout-menu.en-US.md create mode 100644 docs/docs/docs/max/mf.en-US.md create mode 100644 docs/docs/docs/max/micro-frontend.en-US.md create mode 100644 docs/docs/docs/max/react-query.en-US.md create mode 100644 docs/docs/docs/max/request.en-US.md create mode 100644 docs/docs/docs/max/styled-components.en-US.md create mode 100644 docs/docs/docs/max/valtio.en-US.md diff --git a/docs/.dumi/global.less b/docs/.dumi/global.less new file mode 100644 index 000000000000..a94c13e02d50 --- /dev/null +++ b/docs/.dumi/global.less @@ -0,0 +1,9 @@ +// hide mf related docs +.dumi-default-sidebar-group { + a[href="/docs/guides/mfsu"], + a[href="/en-US/docs/guides/mfsu"], + a[href="/docs/max/mf"], + a[href="/en-US/docs/max/mf"] { + display: none; + } +} diff --git a/docs/.dumi/pages/index.en-US.tsx b/docs/.dumi/pages/index.en-US.tsx new file mode 100644 index 000000000000..701433ce0736 --- /dev/null +++ b/docs/.dumi/pages/index.en-US.tsx @@ -0,0 +1 @@ +export { default } from '.'; diff --git a/docs/.dumirc.ts b/docs/.dumirc.ts index 9a629d704079..0d225d02bfb9 100644 --- a/docs/.dumirc.ts +++ b/docs/.dumirc.ts @@ -32,6 +32,10 @@ export default defineConfig({ define: { 'process.env.UMI_VERSION': version, }, + locales: [ + { id: 'zh-CN', name: '中文' }, + { id: 'en-US', name: 'EN' }, + ], themeConfig: { name: 'UmiJS', socialLinks: { @@ -39,8 +43,7 @@ export default defineConfig({ }, footer: 'Open-source MIT Licensed | Copyright © 2017-present', nav: { - mode: 'override', - value: [ + 'zh-CN': [ { title: '介绍', link: '/docs/introduce/introduce', @@ -67,155 +70,31 @@ export default defineConfig({ activePath: '/blog', }, ], - }, - sidebar: { - '/docs/guides': [ + 'en-US': [ { - children: [ - { - title: '快速上手', - link: '/docs/guides/getting-started', - }, - { - title: '开发环境', - link: '/docs/guides/prepare', - }, - { - title: '目录结构', - link: '/docs/guides/directory-structure', - }, - { - title: '路由', - link: '/docs/guides/routes', - }, - { - title: '插件', - link: '/docs/guides/use-plugins', - }, - { - title: 'Mock', - link: '/docs/guides/mock', - }, - { - title: '代理', - link: '/docs/guides/proxy', - }, - { - title: '样式', - link: '/docs/guides/styling', - }, - { - title: '路由数据加载', - link: '/docs/guides/client-loader', - }, - { - title: 'TypeScript', - link: '/docs/guides/typescript', - }, - { - title: '环境变量', - link: '/docs/guides/env-variables', - }, - { - title: '脚手架', - link: '/docs/guides/boilerplate', - }, - { - title: '微生成器', - link: '/docs/guides/generator', - }, - { - title: '编码规范', - link: '/docs/guides/lint', - }, - { - title: '调试', - link: '/docs/guides/debug', - }, - { - title: '测试', - link: '/docs/guides/test', - }, - { - title: '开发插件', - link: '/docs/guides/plugins', - }, - { - title: '使用 Vue', - link: '/docs/guides/use-vue', - }, - { - title: 'MPA 模式', - link: '/docs/guides/mpa', - }, - // 暂不开放 - // { - // title: 'MFSU', - // link: 'docs/guides/mfsu', - // }, - ], + title: 'Introduce', + link: '/en-US/docs/introduce/introduce', + activePath: '/en-US/docs/introduce', + }, + { + title: 'Guide', + link: '/en-US/docs/guides/getting-started', + activePath: '/en-US/docs/guides', + }, + { + title: 'API', + link: '/en-US/docs/api/api', + activePath: '/en-US/docs/api', + }, + { + title: 'Umi Max', + link: '/en-US/docs/max/introduce', + activePath: '/en-US/docs/max', }, - ], - '/docs/max': [ { - children: [ - { - title: 'Umi Max 简介', - link: '/docs/max/introduce', - }, - { - title: '布局与菜单', - link: '/docs/max/layout-menu', - }, - { - title: 'antd', - link: '/docs/max/antd', - }, - { - title: '图表', - link: '/docs/max/charts', - }, - { - title: '数据流', - link: '/docs/max/data-flow', - }, - { - title: '请求', - link: '/docs/max/request', - }, - { - title: '权限', - link: '/docs/max/access', - }, - { - title: '国际化', - link: '/docs/max/i18n', - }, - { - title: '微前端', - link: '/docs/max/micro-frontend', - }, - { - title: 'styled-components', - link: '/docs/max/styled-components', - }, - { - title: 'react-query', - link: '/docs/max/react-query', - }, - { - title: 'valtio', - link: '/docs/max/valtio', - }, - { - title: 'dva', - link: '/docs/max/dva', - }, - { - title: '站点统计', - link: '/docs/max/analytics', - }, - ], + title: 'Blog', + link: '/en-US/blog/umi-4-rc', + activePath: '/en-US/blog', }, ], }, diff --git a/docs/docs/blog/code-splitting.en-US.md b/docs/docs/blog/code-splitting.en-US.md new file mode 100644 index 000000000000..5cac55c7c7e3 --- /dev/null +++ b/docs/docs/blog/code-splitting.en-US.md @@ -0,0 +1,50 @@ +--- +toc: content +order: 5 +group: + title: Blog +--- + +# 代码拆分指南 + +Umi 4 默认 按页拆包、按需加载(这近似等同于 Umi 3 中的 `dynamicImport`),通过 [`loading.tsx`](../docs/guides/directory-structure#loadingtsxjsx) 来自定义加载动画。 + +### 使用分包策略 + +Umi 4 内置了不同的代码拆分策略 ( [codeSplitting](../docs/api/config#codesplitting) ) ,通过配置开启: + +```ts +// .umirc.ts +export default { + codeSplitting: { + jsStrategy: 'granularChunks', + }, +}; +``` + +这会按照一定的优化策略进行自动分包,若需手动进行更细致的分包,请参见下文。 + +### 手动拆分 + +当你的产物体积变大时,可进一步手动拆包: + +```ts +import { lazy, Suspense } from 'react' + +// './Page' 该组件将被自动拆出去 +const Page = lazy(() => import('./Page')) + +export default function() { + return ( + loading... + + + ) +} +``` + +通常情况下,我们会手动拆分引用了较大第三方库的组件,实现按需加载。 + +### 分析产物构成 + +通过指定 [ANALYZE](../docs/guides/env-variables#analyze) 环境变量可以分析产物构成,根据分析结果来修改代码和进一步决策。 diff --git a/docs/docs/blog/develop-blog-using-umi.en-US.md b/docs/docs/blog/develop-blog-using-umi.en-US.md new file mode 100644 index 000000000000..43b3d49e9c2b --- /dev/null +++ b/docs/docs/blog/develop-blog-using-umi.en-US.md @@ -0,0 +1,613 @@ +--- +order: 1 +toc: content +group: + title: Blog +--- + +# 使用 Umi 开发一个 Blog + +这篇文章将带领你使用 Umi.js 搭配 [PlanetScale](https://planetscale.com/), [Prisma](https://www.prisma.io/) 和 [Tailwindcss](https://tailwindcss.com/)等服务与技术,开发一个简单的博客网站,并部署到 [Vercel](https://vercel.com) 服务。 + +## 成果展示 + +成果看起来是这样的:你会有一个博客首页展示你的文章 [https://umi-blog-example.vercel.app/](https://umi-blog-example.vercel.app/) + +![博客首页](https://img.alicdn.com/imgextra/i2/O1CN01a9YcgY24tEdndfXsw_!!6000000007448-2-tps-3104-1974.png) + +点击某一篇文章可以看到这篇文章的完整内容:[https://umi-blog-example.vercel.app/posts/5](https://umi-blog-example.vercel.app/posts/5) + +![博客文章页](https://img.alicdn.com/imgextra/i4/O1CN01k84YL21wHCpYx02Yc_!!6000000006282-2-tps-3104-1974.png) + +当然,你还可以在博客中发表新文章:[https://umi-blog-example.vercel.app/posts/create](https://umi-blog-example.vercel.app/posts/create) + +![发表文章页](https://img.alicdn.com/imgextra/i4/O1CN01DZkDt41jvt0BJMZqi_!!6000000004611-2-tps-3104-1974.png) + +前提是你有登入:[https://umi-blog-example.vercel.app/login](https://umi-blog-example.vercel.app/login) + +![博客登入页](https://img.alicdn.com/imgextra/i1/O1CN015ce0oY1uKkfMCa1vq_!!6000000006019-2-tps-3104-1974.png) + +准备好了吗,让我们马上开始吧! + +## 环境准备 + +首先,你必须确保你的本地环境已经准备好进行一个 Umi.js 项目的开发。如果你目前还没有开发过 Umi.js 项目,也没有在本地搭建过开发环境,建议你先阅读 [开发环境](../docs/guides/prepare) 这篇教学。 + +配置完本地环境以后,你已经准备好开始开发 Umi.js 项目了!跟着 [脚手架](../docs/guides/boilerplate) 这篇文档的教学,快速初始化一个 Umi.js 项目吧。 + +### 调整目录结构 + +因为我们的博客网站会使用到 Umi 4 的 API 路由功能,所以我们需要对脚手架自动产生的目录结构进行一些调整。你现在的目录结构应该看起来是这样的: + +```text +. +├── assets +│ └── yay.jpg +├── layouts +│ ├── index.less +│ └── index.tsx +├── node_modules +├── package.json +├── pages +│ ├── docs.tsx +│ └── index.tsx +├── pnpm-lock.yaml +├── tsconfig.json +└── typings.d.ts +``` + +我们需要把 `assets`, `layouts`, `pages` 目录从根目录移动到 `src` 目录下,移动之后他看起来是这样的: + +```text +. +├── src +│ ├── assets +│ │ └── yay.jpg +│ ├── layouts +│ │ ├── index.less +│ │ └── index.tsx +│ └──── pages +│ ├── docs.tsx +│ └── index.tsx +├── node_modules +├── package.json +├── pnpm-lock.yaml +├── tsconfig.json +└── typings.d.ts +``` + +:::info +为什么要这样做呢?这是因为根目录下的 `api` 目录会被我们作为 API 路由生成构建产物的地方,如果我们没有多一层 `src` 目录的话,我们的 API 路由目录就会和构建产物目录冲突啦~ +::: + +### 注册 PlanetScale 服务 + +我们的博客将会把用户和文章的数据保存在 MySQL 数据库中。然而,我们不需要真的自己准备一台服务器来运行数据库,我们可以使用免费的 [PlanetScale](https://planetscale.com/)来一键部署一个开箱即用的数据库! + +首先从 [https://auth.planetscale.com/sign-in](https://auth.planetscale.com/sign-in)登入你的账号,如果你没有注册过,可以选择使用 GitHub 一键登入或是点击 [Sign up for an account](https://auth.planetscale.com/sign-up) 注册一个账号。 + +![登入 PlanetScale](https://img.alicdn.com/imgextra/i4/O1CN01BVVAju1eONxEM9wr5_!!6000000003861-2-tps-2506-1464.png) + +登入之后,在你的 PlanetScale 账号建立一个数据库(如果你是第一次注册,则可以看到他的教学步骤带领你一步步建立一个数据库): + +![建立数据库](https://img.alicdn.com/imgextra/i4/O1CN01St4IQW21lV6f8bpKg_!!6000000007025-2-tps-3104-1974.png) + +建立完成后,点击数据库页面右上角的 **Connect** 按钮: + +![Connect 按钮](https://img.alicdn.com/imgextra/i4/O1CN01Hnyqbo26g4UNnSqoQ_!!6000000007690-2-tps-3104-1974.png) + +你会在弹窗里面看到一个 **Connect With** 的下拉选单,选择 `Prisma`,然后就能获得一串这个格式的字符串: + +```dotenv +DATABASE_URL='mysql://************:************@************.ap-southeast-2.psdb.cloud/umi-blog-example?sslaccept=strict' +``` + +这个字符串就是我们要用来让 Prisma 连接数据库的连线信息,暂时先把他记录起来就可以了 👍 + +### 安装依赖 + +接下来,帮我们的 Umi 项目安装这次教程会用到的依赖: + +```shell +pnpm i -d prisma @types/bcryptjs @types/jsonwebtoken +pnpm i @prisma/client bcryptjs jsonwebtoken +``` + +这两行命令帮我们安装了这些包: + +1. [prisma](https://github.com/prisma/prisma)和 [@prisma/client](https://www.npmjs.com/package/@prisma/client):这两个包让我们可以用 [Prisma](https://www.prisma.io/)来方便地串接数据库,不需要担心任何复杂的 SQL 命令。 +2. [bcryptjs](https://github.com/dcodeIO/bcrypt.js):这个包让我们将用户注册后的密码使用 [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt)哈希加密算法来安全的存储在数据库中。 +3. [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken):这个包让我们可以方便地使用用 [JWT(Json Web Token)](https://jwt.io/) 实现用户鉴权。 + +然后将 `package.json` 中 `scripts` 里面的 `build` 脚本从 + +``` +"scripts": { + "dev": "umi dev", + "build": "umi build", + "postinstall": "umi setup", + "start": "npm run dev" +}, +``` + +改成 + +``` +"scripts": { + "dev": "umi dev", + "build": "prisma generate && umi build", + "postinstall": "umi setup", + "start": "npm run dev" +}, +``` + +这可以确保每次开始构建以前都已经生成好 Prisma 客户端。 + +### 安装 Tailwindcss + +使用 Umi 提供的微生成器来在项目中启用 Tailwindcss : + +```shell +npx umi g tailwindcss +``` + +他会帮我们安装 Tailwindcss 所需要的依赖,然后生成需要的文件。 + +### 初始化页面组件 + +接下来,当你使用 `pnpm dev`启动项目后,可能会看到错误讯息并且启动失败了。这是因为我们在配置中声明了一些页面,但并没有帮他建立对应的页面组件! + +我们可以使用 Umi 的微生成器来自动生成这些页面:`login.tsx`, `posts/post.tsx`, `posts/create.tsx`: + +```shell +npx umi g page login posts/post posts/create +``` + +新增后的目录结构是这样的: + +```text +src +├── assets +│ └── yay.jpg +├── layouts +│ ├── index.less +│ └── index.tsx +└── pages + ├── index.less + ├── index.tsx + ├── login.less + ├── login.tsx + └── posts + ├── create.less + ├── create.tsx + ├── post.less + └── post.tsx +``` + +现在再输入一次 `pnpm dev` 就可以看到我们的网站正常启动了。 + +### 配置 umi 项目 + +最后一步,就是要来对 Umi 项目进行配置,完整的配置可以参考 [配置](../docs/api/config) 这篇教学文档,在本次的教学中,只要按照以下配置即可: + +```ts +// .umirc.ts + +export default { + npmClient: 'pnpm', + apiRoute: { + platform: 'vercel', + }, + routes: [ + { path: '/', component: 'index' }, + { path: '/posts/create', component: 'posts/create' }, + { path: '/login', component: 'login' }, + { path: '/posts/:postId', component: 'posts/post' }, + ], + plugins: [require.resolve('@umijs/plugins/dist/tailwindcss')], + tailwindcss: {}, +}; +``` + +其中,`apiRoute` 这个配置项告诉 Umi 我们的项目启用了 **API 路由** 这个功能,而 `platform: 'vercel'` 代表我们要部署到[Vercel](https://vercel.com) 平台,在 `umi build` 的时候会针对这个平台来将 API 路由进行打包。 + +为了顺利部署项目到 Vercel ,你需要在项目根目录下加入一个 `vercel.json` 配置文件: + +```json +{ + "build": { + "env": { + "ENABLE_FILE_SYSTEM_API": "1" + } + }, + "rewrites": [ + { + "source": "/api/:match*", + "destination": "api/:match*" + } + ] +} +``` + +`route` 这个配置项则声明了我们网站的路由架构,可以看到我们的博客网站有这些页面: + +- `/`: 首页 +- `/posts/create`: 建立文章 +- `/login`: 登入 +- `/posts/:postId`: 某篇文章 + +`plugins` 配置项代表我们启用了哪些 Umi 插件,其中 `@umijs/plugins/dist/tailwindcss` 是在 Umi 中使用 Tailwindcss +的插件。下面一项 `tailwindcss: {}` 则是从配置来启用该插件的意思。 + +## API 路由设计 + +我们的整个博客网站由两大部分构成,一半是运行在浏览器内的前端代码,另一半则是运行在 Serverless Function 中的服务端代码。 + +为什么需要分成两边呢?这是因为有些代码我们不能让他在浏览器内运行,比如说用户鉴权、串接数据库等等的功能,这些必须作为一个服务然后以 API 的形式暴露给前端页面调用,这个部分可以透过 Umi 4 的 API 路由功能来实现。 + +> (这里好像放一张图可以比较清楚地解释) + +因为我们已经在 `.umirc.ts` 配置文件中声明了我们要启用 API 路由功能,现在可以直接在 `src` 目录下添加一个 `api`目录,这个目录下以约定式路由的设计来提供 API 路由的开发。 + +作为一个博客的 API 服务,不难想到我们会需要这些接口来供用户调用: + +1. 用户注册: `POST /api/register` +2. 用户登入: `POST /api/login` +3. 发表文章: `POST /api/posts` +4. 查询所有文章: `GET /api/posts` +5. 查询一篇文章: `GET /api/posts/{postId}` + +所以我们可以在 `src/api` 目录下建立这些新文件: + +```text +src +├── api +│ ├── login.ts +│ ├── register.ts +│ └── posts +│ ├── [postId].ts +│ └── index.ts +... +``` + +:::info +你可能注意到了,这里有一个文件叫做 `[postId].ts`,这个写法代表这个路由可以动态匹配不同的值。例如 `/api/posts/1` 和 `/api/posts/2` 两个请求都会交给 `src/api/posts/[postId].ts`处理,但他们的 `req.params` 分别是 `{ postId: 1 }` 和 `{ postId: 2 }`。 +::: + +这里的每个 `.ts` 文件就是一个 **API Handler**,他们默认导出一个函数用来处理发送到该路径的请求,我们可以暂时先这样写: + +```ts +import type { UmiApiRequest, UmiApiResponse } from 'umi'; + +export default async function (req: UmiApiRequest, res: UmiApiResponse) { + res.status(400).json({ error: 'This API is not implemented yet.' }); +} +``` + +然后你可以试着用浏览器或 [Postman](https://www.postman.com) 访问看看这些 API 路由(例如 `http://localhost:8000/api/login` ),就可以看到你刚刚写的响应数据了 🎉 + +![Not implemented yet](https://img.alicdn.com/imgextra/i3/O1CN01IRTlsd1HXmvCRJUt1_!!6000000000768-2-tps-1302-666.png) + +我们等一下再回来实作这些 API 路由的实际功能,因为有一个更重要的事情要先做。 + +## 定义 Schema + +现在必须要先确定一件事情:我们要保存哪些数据、以怎么样的形式保存在数据库,又是以怎么样的形式响应给前端的? + +### 文章数据 + +文章数据(Post)每笔数据就代表了一篇博客里面的文章,我们可以按自己的系统需求来设计他需要保存的内容,例如我们的范例保存了这些数据: + +- `id`: 文章 ID +- `title`: 文章标题 +- `authorId`: 作者的 ID +- `tags`: 文章的标签(以逗号隔开) +- `imageUrl`: 文章封面图片的链接 +- `content`: 文章的内文(markdown 格式) + +### 用户数据 + +用户数据 (User) 每笔数据代表一个在我们博客注册的用户数据,我们可以按照自己的系统需求来设计他需要保存的内容,例如我们的范例保存了这些数据: + +- `id`: 用户 ID +- `name`: 名称 +- `email`: 邮箱 +- `avatarUrl`: 头像链接 +- `passwordHash`: 加密过的密码 + +### 生成配置 + +> 这个章节可以考虑阅读 [Prisma 官方的教学文档](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-mysql) + +定义好数据格式以后,我们要让 Prisma 帮我们根据 Schema 设计来生成对应的客户端,并且自动的将数据库迁移至为我们设计的格式, + +#### 连线到数据库 + +第一步,我们在根目录建立一个 `.env` 文件,并且在里面加入刚刚在 [注册 PlanetScale 服务](#注册-planetscale-服务) 章节拿到的连线信息。 + +```dotenv +# .env + +DATABASE_URL='mysql://************:************@************.ap-southeast-2.psdb.cloud/umi-blog-example?sslaccept=strict' +``` + +#### 编写 Prisma 配置 + +第二步,在根目录下建立一个 `prisma/schema.prisma` 文件,并把我们设计的 Schema 按照 [Prisma 语法](https://pris.ly/d/prisma-schema) 写进去文件中: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["referentialIntegrity"] +} + +datasource db { + provider = "mysql" + referentialIntegrity = "prisma" + url = env("DATABASE_URL") +} + +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String @db.VarChar(255) + content String? + author User @relation(fields: [authorId], references: [id]) + authorId Int + imageUrl String? + tags String + + @@index(authorId) +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + passwordHash String + name String? + posts Post[] + avatarUrl String? +} +``` + +完成后,在命令行输入 + +```shell +npx prisma migrate dev --name init +``` + +他会帮我们将 MySQL 数据库迁移为我们定义的格式。 接下来,在命令行输入 + +```shell +npx prisma generate +``` + +他会帮我们生成一个按照我们的 Schema 设计量身定制的客户端包。 + +--- + +至此,我们已经顺利处理完数据库的部分,接下来只要专注于如何在 API 路由中使用 Prisma 客户端包来获取与更新数据即可。 + +## 实现 API 路由 + +我们现在要回头来实现刚刚建立的那些 `api` 目录下的 `.ts` 文件了,只要我们自己清楚: + +1. API 会如何被调用 (path, request header, request body) +2. 我们应该在 API 内做什么事 +3. 响应什么内容回去 (status, response header, response body) + +那么 API 路由的开发就像写编写一个简单的函数一样。 + +### 用户注册 + +当用户对 `/api/register` 发起 `POST` 请求时,代表他们想要在我们的博客网站注册一个账号。 + +:::info +你可以在 [https://github.com/umijs/umi-blog-example/blob/main/src/api/register.ts](https://github.com/umijs/umi-blog-example/blob/main/src/api/register.ts) 找到这个示范的源代码! +::: + +```ts +// src/api/register.ts + +import type { UmiApiRequest, UmiApiResponse } from 'umi'; +import { PrismaClient } from '@prisma/client'; +import bcrypt from 'bcryptjs'; +import { signToken } from '@/utils/jwt'; + +export default async function (req: UmiApiRequest, res: UmiApiResponse) { + switch (req.method) { + // 如果对这个路径发起 POST 请求,代表他想要注册一个账号 + case 'POST': + try { + // 建立一个 Prisma 客户端,他可以帮助我们连线到数据库 + const prisma = new PrismaClient(); + + // 在数据库的 User 表中建立一个新的数据 + const user = await prisma.user.create({ + data: { + email: req.body.email, + + // 密码是经过 bcrypt 加密的 + passwordHash: bcrypt.hashSync(req.body.password, 8), + name: req.body.name, + avatarUrl: req.body.avatarUrl, + }, + }); + + // 把建立成功的用户数据(不包含密码)和 JWT 回传给前端 + res + .status(201) + .setCookie('token', await signToken(user.id)) + .json({ ...user, passwordHash: undefined }); + + // 处理完请求以后记得断开数据库链接 + await prisma.$disconnect(); + } catch (e: any) { + // 如果发生未预期的错误,将对应的错误说明的 Prisma 文档发给用户 + res.status(500).json({ + result: false, + message: + typeof e.code === 'string' + ? 'https://www.prisma.io/docs/reference/api-reference/error-reference#' + + e.code.toLowerCase() + : e, + }); + } + break; + default: + // 如果不是 POST 请求,代表他正在用错误的方式访问这个 API + res.status(405).json({ error: 'Method not allowed' }); + } +} +``` + +完成开发后,可以使用 Postman 对这个 API 发起请求,测试功能是否正常运作。 + +### 用户登入 + +当用户对 `/api/login` 发起 `POST` 请求时,代表他们想要登入我们的博客网站并取得一个 JWT 令牌,这可以让他用于建立新文章。 + +:::info +这个部分留给读者练习,你可以在 [https://github.com/umijs/umi-blog-example/blob/main/src/api/login.ts](https://github.com/umijs/umi-blog-example/blob/main/src/api/login.ts) 找到这个示范的源代码! +::: + +### 发表文章 + +当用户对 `/api/posts` 发起 `POST` 请求时,代表他们想要在我们的博客网站发表一篇文章。 + +:::info +这个部分留给读者练习,你可以在 [https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/index.ts](https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/index.ts) 找到这个示范的源代码! +::: + +### 查询所有文章 + +当用户对 `/api/posts` 发起 `GET` 请求时,代表他们想要查询所有文章的数据。 + +:::info +这个部分留给读者练习,你可以在 [https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/index.ts](https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/index.ts) 找到这个示范的源代码! +::: + +### 查询某篇文章 + +当用户对 `/api/posts/{postId}` 发起 `GET` 请求时,代表他们想要查询某篇文章的数据。 + +:::info +这个部分留给读者练习,你可以在 [https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/%5BpostId%5D.ts](https://github.com/umijs/umi-blog-example/blob/main/src/api/posts/%5BpostId%5D.ts) 找到这个示范的源代码! +::: + +## 实作页面组件 + +在这个章节,我们主要要学习如何在页面组件调用 API,来实现获取文章或注册等前后端交互的行为: + +```jsx +// pages/index.tsx + +import React, { useEffect, useState } from 'react'; +import { history } from "umi"; + +export default function HomePage() { + const [posts, setPosts] = useState(); + return ( +
+ {!posts &&

Loading...

} + {posts &&
+ {posts.map(post =>
+
history.push(`/posts/${post.id}`)}> +

{post.title}

+
+
)} +
} +
+ ); +} +``` + +可以看到我们在首页组件维护了一个 `posts` 状态,当 `posts` 是 `undefined` 时,我们认为是数据尚未加载完成。所以我们可以加入一个 `useEffect`让他在组件加载后对 API 路由发起一个请求,去查询目前所有的文章列表: + +```tsx +// pages/index.tsx + +import React, { useEffect, useState } from 'react'; +import { history } from 'umi'; + +export default function HomePage() { + const [posts, setPosts] = useState(); + + async function refresh() { + try { + const res = await fetch('/api/posts'); + if (res.status !== 200) { + console.error(await res.text()); + } + setPosts(await res.json()); + } catch (err) { + console.error(err); + } + } + + useEffect(() => { + refresh(); + }, []); + + return ( +
+ {!posts &&

Loading...

} + {posts && ( +
+ {posts.map((post) => ( +
+
history.push(`/posts/${post.id}`)}> +

{post.title}

+
+
+ ))} +
+ )} +
+ ); +} +``` + +可以看到我们加入了一个 `refresh` 函数,他会帮我们从 API 路由查询目前的文章列表。若你现在访问这个页面,应该可以看到一开始是 `Loading ...` +等一阵子就会有全部文章的标题被渲染出来的效果。 + +![titles](https://img.alicdn.com/imgextra/i1/O1CN01n3CA371n0PlgkdEvQ_!!6000000005027-2-tps-3104-1974.png) + +最后只要帮他加一点样式: + +![titles-with-style](https://img.alicdn.com/imgextra/i1/O1CN01sfpTVd1IGDLK068gY_!!6000000000865-2-tps-3104-1974.png) + +--- + +其他页面留给读者实作,可以加入自己的想法及样式的设计来实现,源代码可参考:[https://github.com/umijs/umi-blog-example/blob/main/src/pages](https://github.com/umijs/umi-blog-example/blob/main/src/pages) + +## 部署 + +最后,将你的项目提交到 git 服务上,然后登入 [Vercel](https://vercel.com): + +![Vercel](https://img.alicdn.com/imgextra/i2/O1CN01X7LqFx1LbEMYzLT3k_!!6000000001317-2-tps-2720-1710.png) + +如果你的项目代码是提交到 GitHub,那么建议你选择 GitHub 登入,这样你就可以在 Vercel 直接导入现有的代码仓库了 👍 + +![Vercel New Project](https://img.alicdn.com/imgextra/i1/O1CN014qIgle23G171pvX0V_!!6000000007227-2-tps-2720-1818.png) + +导入以后,他会自动检测到这个项目是使用 Umi.js 框架搭建的,并且自动化完成相关的配置,因此直接点击 **Deploy** 即可开始部署! + +![Deploy](https://img.alicdn.com/imgextra/i3/O1CN013Ts04x1tqyv4VhzbW_!!6000000005954-2-tps-1468-1064.png)等他部署完成以后,你的博客就正式上线啦 👍 + +--- + +但这个时候你会发现,网站内的 API 路由没有办法正常工作,这是因为他缺少了连线到数据库需要的环境变量。我们需要帮他配置一下: + +![Settings](https://img.alicdn.com/imgextra/i1/O1CN01OYPPB61G61IKR8chz_!!6000000000572-2-tps-2632-1934.png) + +在项目配置页面,下面有可以设置环境变量的地方: + +![Env](https://img.alicdn.com/imgextra/i4/O1CN01nOLflV1I4lRMwsYHk_!!6000000000840-2-tps-2632-1934.png) + +你可以把 `DATABASE_URL`, `JWT_KEY` 等等你用到的环境变量从这里传入。 + +![Redeploy](https://img.alicdn.com/imgextra/i3/O1CN013dlBGs2506aFTigki_!!6000000007463-2-tps-2644-1890.png) + +加入环境变量以后,点击 **Redeploy** 重新部署一次版本,你的博客就能正常运作啦~ diff --git a/docs/docs/blog/legacy-browser.en-US.md b/docs/docs/blog/legacy-browser.en-US.md new file mode 100644 index 000000000000..d3b3d7e09c74 --- /dev/null +++ b/docs/docs/blog/legacy-browser.en-US.md @@ -0,0 +1,127 @@ +--- +toc: content +order: 6 +group: + title: Blog +--- + +# 非现代浏览器兼容 + +## 默认兼容说明 + +Umi 4 默认不支持 IE ,编译兼容目标 `targets` 为 `chrome: 80` ,如需调整,请指定明确的 [targets](../docs/api/config#targets) : + +```ts +// .umirc.ts + +export default { + targets: { chrome: 67 }, +}; +``` + +若想反馈更多关于兼容性的问题,或参与讨论,请前往:[issue / 8656](https://github.com/umijs/umi/issues/8658) + +## 兼容非现代浏览器 + +如果你并不需要兼容至 IE ,只为了提升项目对非现代浏览器的兼容性,可调整兼容目标 [targets](../docs/api/config#targets) 。 + +Umi 4 默认使用现代构建工具,产物生成至 `es6` ,如果你有要打包为 `es5` 产物的考量,请调整配置: + +```ts +// .umirc.ts + +export default { + jsMinifier: 'terser', + cssMinifier: 'cssnano', +}; +``` + +## 兼容旧时代浏览器 ( IE 11 ) + +由于 IE 已经淘汰不再主流,当需要兼容至 IE 时,请阅读以下对策。 + +### 框架自带的 legacy mode + +Umi 4 自带提供一个 `legacy` 配置用于构建降级(使用限制等详见 [legacy](../docs/api/config#legacy) ): + +```ts +// .umirc.ts + +export default { + legacy: {}, +}; +``` + +默认仅在构建时生效,将尝试构建能使 IE 兼容的产物。 + +### legacy mode 的更多自定义 + +`legacy` 开启时,默认会转译全部 `node_modules` ,这在大型项目中,会极大的增加构建时间。 + +若你了解当前项目使用的第三方依赖情况(知道哪些不再提供 `es5` 产物了),可以关闭 `node_modules` 的转换,改为使用 [`extraBabelIncludes`](https://umijs.org/docs/api/config#extrababelincludes) 定点配置那些需要额外纳入转换范围的包。 + +一个例子: + +```ts +// .umirc.ts + +export default { + legacy: { + nodeModulesTransform: false, + }, + extraBabelIncludes: ['some-es6-pkg', /@scope\//], +}; +``` + +### 提高兼容的鲁棒性 + +`legacy` 选项并不能 100% 保证产物 **没有边界情况** 的运行在被淘汰的浏览器内,你可能还需要添加 **前置的** 全量 polyfill 来增强项目的 [鲁棒性](https://baike.baidu.com/item/%E9%B2%81%E6%A3%92%E6%80%A7/832302) 。 + +```ts +// .umirc.ts + +export default { + headScripts: [ + 'http://polyfill.alicdn.com/v3/polyfill.min.js', // or https://polyfill.io/v3/polyfill.min.js + ], + legacy: {}, +}; +``` + +参考的思路有: + +| 方案 | 说明 | +| :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CDN 引入 | 以 cdn 形式引入 **script 形式且前置的** 、目标浏览器环境缺少的 polyfill js 文件,如 [es6-shim](https://github.com/paulmillr/es6-shim) 。 | +| 人工 core-js | 利用 [core-js](https://github.com/zloirock/core-js) 系工具,如通过 [core-js-builder](https://github.com/zloirock/core-js/tree/master/packages/core-js-builder) 构建自己需要的 polyfill 产物,再以 **前置 script 脚本** 形式引入项目。 | +| 动态 polyfill 服务 | 使用根据当前浏览器请求 UA 动态下发所需 polyfill 的服务,比如 [polyfill.io](https://polyfill.io/v3/polyfill.min.js) ,考虑到速度,可使用国内的 [alicdn polyfill.io](http://polyfill.alicdn.com/v3/polyfill.min.js) 服务。另外,你还可以使用 [polyfill-service](https://github.com/Financial-Times/polyfill-service) 自建相同的动态 polyfill 下发服务。 | + +注: + +1. 当你处于内外网隔离开发环境时,可以考虑将全部 polyfill 的 js 内容传入内网,在内网的 CDN 使用,或放入 public 目录等方式使用。 + +2. 使用 script 前置引入的意义在于,在项目 js 资源运行前就准备好一个完整的、被 polyfill 过 api 的环境。 + +### 在开发环境验证 + +推荐的做法是:构建后在本地通过 [`umi preview`](../docs/api/commands#preview) 或 [`serve`](https://www.npmjs.com/package/serve) 、nginx 等启动服务,来验证产物的 IE 11 运行可行性。 + +当你需要在开发环境验证时: + +1. 将 `legacy.buildOnly` 置为 `false` 。 + +2. 由于 react fresh 、hmr 等开发注入的 es6 代码始终在第一位运行,你需要以 script 形式添加一个前置的 polyfill ,提前准备好环境。 + +```ts +// .umirc.ts + +const isProd = process.env.NODE_ENV === 'production'; +export default { + legacy: { + buildOnly: false, + }, + headScripts: isProd ? [] : ['http://polyfill.alicdn.com/v3/polyfill.min.js'], +}; +``` + +注:IE 11 并不能完整支持开发时的热更新,且缓存可能需要人为在控制台进行清除后才能看到最新的页面,请做好准备。 diff --git a/docs/docs/blog/mfsu-faster-than-vite.en-US.md b/docs/docs/blog/mfsu-faster-than-vite.en-US.md new file mode 100644 index 000000000000..19df5e5a07f9 --- /dev/null +++ b/docs/docs/blog/mfsu-faster-than-vite.en-US.md @@ -0,0 +1,41 @@ +--- +order: 3 +toc: content +group: + title: Blog +--- + +# 比 Vite 还快的 MFSU + +

+ 编者按:Change the code, don't Workaround! Webpack + 慢就去改他,优化到位后,Bundle 也可以很快。此方案会在 Umi 4 + 中默认开启,适用于既要 Webpack 功能与生态,又想要 Vite 速度的同学们。 +

+ +Umi 4 中同时支持 webpack 和 vite 两种构建方式,跑通了后,迫不及待对比了 Vite 和 Webpack + MFSU 的效果,结果有点意外。关于什么是 MFSU,我在[《SEE Conf: Umi 4 设计思路文字稿》](https://mp.weixin.qq.com/s?__biz=MjM5NDgyODI4MQ%3D%3D&mid=2247484533&idx=1&sn=9b15a67b88ebc95476fce1798eb49146)中有一段详细介绍。 + +

+ + 两个示例、四种模式、四个维度的对比。 + + 两个示例分别是大型的全量 ant-design-pro 和小型的 libs example;四种模式分别是 webpack、webpack + MFSU、webpack + MFSU with esbuild mode、Vite in umi;四个维度分别是无缓存的冷启动、有缓存的热启动、修改代码后的热更新、页面打开速度。 +

+ +多说几点和统计相关的。上述 webpack 相关模式全部开启物理缓存;Vite 是 Umi 中集成后的 Vite,也有担心是不是 Umi 对于 Vite 的误用,经开发者确认,基本排除误用的可能性,大段时间消耗在预编译依赖上;Ant Design Pro 中包含 less 的使用,这是使用 esbuild 无法加速的部分,这有影响,但对于不同模式应该是公平的;下图数据是本地用 13-inch M1 2022 重启电脑后跑 5 次后平均取值的结果;Vite 的热更速度没统计是因为由于 esm 的特性,改完后要等请求过来后处理完才算结束,无法统计,但肯定是很快的。 + +直接上结果。有兴趣手动验证的同学可到 umijs/umi 仓库的不同 example 目录下执行 npm run dev 验证。 + +![](https://img.alicdn.com/imgextra/i4/O1CN01Gz9AA81szqy3BbRfK_!!6000000005838-2-tps-2150-1084.png) + +

+ 图:全量 ant-design-pro 速度对比图 +

+ +![](https://img.alicdn.com/imgextra/i1/O1CN01HNfH7l23L3SRjJUka_!!6000000007238-2-tps-2058-1078.png) + +

+ 图:libs example 速度对比图 +

+ +可以看到,**在这几个场景下,MFSU with esbuild 数据领先。** 四个模式的页面打开速度差不多,所以对比数据没在图中列出,这也是让我意外的点,原以为 Vite 请求多会让页面打开速度变慢,也有可能项目还不够复杂? diff --git a/docs/docs/blog/mfsu-independent-usage.en-US.md b/docs/docs/blog/mfsu-independent-usage.en-US.md new file mode 100644 index 000000000000..68857e23e3c4 --- /dev/null +++ b/docs/docs/blog/mfsu-independent-usage.en-US.md @@ -0,0 +1,257 @@ +--- +toc: content +order: 4 +group: + title: Blog +--- + +# 独立使用 MFSU + +`MFSU` 支持独立在非 umijs 项目中使用,本文将会介绍如何将 `MFSU` 接入你的 webpack 项目。 + +## 示例项目 + +如何接入 MFSU ?提供以下几个 示例项目 配置供参考: + +Webpack 配置示例:examples/mfsu-independent + +CRA v5 配置示例:cra-v5-with-mfsu-example + +## 安装 + +首先安装 `mfsu` 的依赖: + +```bash + pnpm add -D @umijs/mfsu +``` + +## 配置 MFSU + +配置 MFSU 一共需要简单的四步操作,请确保以下所有行为都只在开发环境生效。 + +### 1. 初始化实例 + +第一步,初始化一个 `MFSU` 实例,这是 `MFSU` 的基础: + +```js +// webpack.config.js + +const { MFSU } = require('@umijs/mfsu'); +const webpack = require('webpack'); + +// [mfsu] 1. init instance +const mfsu = new MFSU({ + implementor: webpack, + buildDepWithESBuild: true, +}); +``` + +### 2. 添加中间件 + +第二步,添加 `MFSU` 的 `devServer` 中间件到 webpack-dev-server 中,他将为你提供 `MFSU` 所需打包后的资源: + +#### webpack 5 + +```js +// webpack.config.js + +module.exports = { + devServer: { + // [mfsu] 2. add mfsu middleware + setupMiddlewares(middlewares, devServer) { + middlewares.unshift(...mfsu.getMiddlewares()); + return middlewares; + }, + }, +}; +``` + +#### webpack 4 + +```js +// webpack.config.js + +module.exports = { + devServer: { + // [mfsu] 2. add mfsu middleware + onBeforeSetupMiddleware(devServer) { + for (const middleware of mfsu.getMiddlewares()) { + devServer.app.use(middleware); + } + }, + }, +}; +``` + +### 3. 配置转换器 + +第三步,你需要配置一种源码转换器,他的作用是用来收集、转换依赖导入路径,替换为 `MFSU` 的模块联邦地址(中间件所提供的)。 + +此处提供两种方案:`babel plugins` 或 `esbuild handler` ,一般情况下选择 `babel plugins` 即可。 + +#### Babel Plugins + +向 `babel-loader` 添加 `MFSU` 的 `babel plugins` 即可。 + +```js +// webpack.config.js + +module.exports = { + module: { + rules: [ + // handle javascript source loader + { + test: /\.[jt]sx?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + plugins: [ + // [mfsu] 3. add mfsu babel plugins + ...mfsu.getBabelPlugins(), + ], + }, + }, + }, + ], + }, +}; +``` + +#### Esbuild handler + +另一种方案是使用内置提供的 `esbuild-loader` 来处理 `js/ts` 资源,**仅用于开发环境** 。 + +:::info +使用这种方案的好处是:在开发环境获得比 `babel` 更快的编译和启动速度 +::: + +```js +// webpack.config.js + +const { esbuildLoader } = require('@umijs/mfsu'); +const esbuild = require('esbuild'); + +module.exports = { + module: { + rules: [ + { + test: /\.[jt]sx?$/, + exclude: /node_modules/, + use: { + loader: esbuildLoader, + options: { + handler: [ + // [mfsu] 3. add mfsu esbuild loader handlers + ...mfsu.getEsbuildLoaderHandler(), + ], + target: 'esnext', + implementation: esbuild, + }, + }, + }, + ], + }, +}; +``` + +:::warning +什么时候我不应该使用 esbuild 方案?
1. 我有自定义的 `babel plugins` 必须在开发环境使用
2. 我需要显示 `css-in-js` 的开发环境友好类名(一般由 babel plugin 提供支持)
3. 在开发环境多适配一套 `esbuild-loader` 的成本大于配置 `babel plugins` 的成本 +::: + +### 4. 设定 webpack 配置 + +第四步,调用 `MFSU` 提供的方法改变你的 webpack 配置,在这里只有增量行为,你无需担心会影响到你原来的配置内容。 + +如下代码所示,`mfsu.setWebpackConfig` 是一个异步方法,为了调用他你需要将原来的 webpack 配置单独抽为一个对象 `config` 之后,再将调用此方法的返回值导出。 + +```js +// webpack.config.js + +const config = { + // origin webpack config +}; + +const depConfig = { + // webpack config for dependencies +}; + +// [mfsu] 4. inject mfsu webpack config +const getConfig = async () => { + await mfsu.setWebpackConfig({ + config, + depConfig, + }); + return config; +}; + +module.exports = getConfig(); +``` + +到此为止,`MFSU` 完全配置完毕,下面可以开始启动项目使用。 + +## 使用 + +进行完 4 步配置后,启动你的项目,你可以从项目根目录得到 `.mfsu` 文件夹,即 `MFSU` 缓存文件夹,请将其添加到 git 的忽略列表(这些缓存文件你不应该提交他们): + +```bash +# .gitignore + +.mfsu +``` + +符合预期时,你已经可以享受 `MFSU` 带来的好处,包括 `esbuild` 快速的打包和二次热启动的提速。 + +## 其他配置 + +以下是其他你可能会用到的 `MFSU` 实例配置: + +```js +const mfsu = new MFSU({ + cwd: process.cwd(), +}); +``` + +其他 Options: + +| option | default | description | +| :-------------------- | :----------------------- | :--------------------------------------------------------------------- | +| `cwd` | `process.cwd()` | 项目根目录 | +| `getCacheDependency` | `() => {}` | 用返回值来对比,使 MFSU cache 无效的函数 | +| `tmpBase` | `${process.cwd()}/.mfsu` | MFSU 缓存存放目录 | +| `unMatchLibs` | `[]` | 手动排除某些不需要被 MFSU 处理的依赖 | +| `runtimePublicPath` | `undefined` | 同 umijs > [`runtimePublicPath`](../docs/api/config#runtimepublicpath) | +| `implementor` | `undefined` | webpack 实例,需要和项目内使用的唯一实例一致 | +| `buildDepWithESBuild` | `false` | 是否使用 `esbuild` 打包依赖 | +| `onMFSUProgress` | `undefined` | 获取 MFSU 编译进度的回调 | + +## 常见问题 + +#### 如何保证我的 MFSU 配置只在开发环境生效? + +使用环境标识避免所有 `MFSU` 在生产环境构建时的配置侵入: + +```js +const isDev = process.env.NODE_ENV === 'development' + +const mfsu = isDev + ? new MFSU({ + implementor: webpack, + buildDepWithESBuild: true, + }) + : undefined + +// e.g. +{ + test: /\.[jt]sx?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + plugins: [ + ...(isDev ? [] : mfsu.getBabelPlugins()) + ] + } + } +} +``` diff --git a/docs/docs/blog/umi-4-rc.en-US.md b/docs/docs/blog/umi-4-rc.en-US.md new file mode 100644 index 000000000000..a7a85d7498f3 --- /dev/null +++ b/docs/docs/blog/umi-4-rc.en-US.md @@ -0,0 +1,150 @@ +--- +toc: content +order: 2 +group: + title: Blog +--- + +# Umi 4 RC 发布 + +大家好,Umi 4 经过几个月的开发,终于要和大家见面了。相比 Umi 2 到 3,3 到 4 的变化是巨大的,开发时间也更长,但我们尽量把对于开发者的影响降低到最小。按捺住激动的心情,在此先和大家分享下都有哪些变化。 + +🎉 新官网和文档
🚀 MFSU V3 & 默认开启
🎭 双构建引擎和 ESMi
🕸 Webpack 5
⛹🏾‍♂️ React Router 6 & 新路由
🐹 最佳实践迭代
🛡️ 依赖预打包
🤺 Umi Max
🐛 Low Import 研发模式
⚠️ 强约束功能集成
🎈 Import All From Umi 迭代
🍀 srcTranspiler 和 depTranspiler
🌼 jsMinifier 和 cssMinifier
🌸 应用元数据
❄️ 微生成器
🧪 贴心小改进
+ +

+ 新官网和文档。 + 下图是新官网的首页,包括重新梳理的文档、信息结构、以及新写的文档插件。目前包含基础的配置、API、升级和快速上手等基础文档,剩余文档还在紧张编写中。有个变化是之前插件的文档集成到 + Umi 官网中,成为 Umi Max 的一部分,之后无需跳转。 +

+ +![](https://img.alicdn.com/imgextra/i1/O1CN014dDq4L1Zc3guRwcse_!!6000000003214-2-tps-1600-941.png) + +

+ MFSU V3 & 默认开启。 + MFSU 更新了他的第三个大版本,如果你有用 Umi 3 内置的 MFSU 并遇到问题,建议重新尝试,这个版本有很多改进,解决基本所有之前可能会遇到的诡异问题,并且编译速度和页面打开速度都更快。昨天我还有写一篇 + [《比 Vite 更快的 MFSU》](https://mp.weixin.qq.com/s?__biz=MjM5NDgyODI4MQ==&mid=2247484624&idx=1&sn=2addfa8cc2511fbea91faf831195788f)。基于此,我们自信地把这个功能在 + Umi 4 中默认开启。还有值得一提的是,MFSU 可脱离 Umi 运行。 +

+ +![](https://img.alicdn.com/imgextra/i2/O1CN01Znj8HD1mCwz72voXv_!!6000000004919-2-tps-1600-807.png) + +

+ 双构建引擎和 ESMi。 + Umi 4 提供 Vite 和 Webpack 两种构建模式供开发者选择,并尽可能保证他们之间功能的一致性,可能有些同学会喜欢 + dev 用 vite,build 用 webpack 这样的组合。同时基于 Vite 模式实现了 ESMi 的 Client + 端,ESMi 依赖服务端,在外网还无法使用。 +

+ +

+ Webpack 5。Umi 4 + 默认使用 webpack 5 并开启物理缓存。 +

+ +

+ + React Router 6 & 新路由。 + + Umi 4 的路由基于 React Router 6 实现,个人非常喜欢这个版本,因为 Remix 的原因,React + Router 6 从设计上考虑了配置式路由的场景,让我得以删除大量 Umi 3 中关于路由渲染的代码。同时基于此,设计了新的路由结构,方便扩展和在未来处理路由的约定式请求。 +

+ +

+ 最佳实践迭代。 + 针对之前 umijs/plugins 仓库中的插件进行了重写、升级,并整合到主仓库。这么做是为了更好的顶层设计,让官方插件之间的风格更一致。 +

+ +

+ ​​依赖预打包。 + 由于服务企业内部,安全和稳定是其中很重要点,加上最近 colors 和 faker.js 闹得社区沸沸扬扬,谁都不希望睡一觉醒来,自己负责的业务挂了,还背个故障。Umi + 4 接着 Umi 3 继续做依赖预打包的事,并且更彻底,不仅是 node 侧的依赖,部分运行时的依赖也会做锁定,比如 + core-js 和 @babel/runtime。 +

+ +![](https://img.alicdn.com/imgextra/i1/O1CN01h44iJg1T09DNuYOlm_!!6000000002319-2-tps-1600-758.png) + +

+ Umi Max。Umi Max + 是内部 Bigfish + 框架的对外版本,解我们自己的问题,同时也给社区另一个集中化框架的选择。 +

+ +

+ + Low Import 研发模式。 + + 这是 Umi 4 的试验性功能之一,目前已开发完成,解的问题是让开发者少些或不写 import + 语句。项目中大量的 import 其实都可以通过工程化的方式自动处理。Umi 4 里通过 lowImport:{' '} + {} 开启,然后就可以无 import 直接用路由相关的 Link、useLocation 等,数据流相关的 + connect、useModel,antd 组件 Button、Calendar 等,以及其他更多。 +

+ +![](https://img.alicdn.com/imgextra/i4/O1CN0142Vcpt25kMZqjmioe_!!6000000007564-2-tps-1600-631.png) + +

+ 强约束功能集成。Umi + 4 提供 API 让强约束和代码校验变得非常容易。API 包括 + api.onCheck、api.onCheckConfig、api.onCheckPkgJSON 和 + api.onCheckCode,顾名思义,非常好理解他们分别是干嘛的,可以分别对依赖类、代码类和配置类的内容做校验和卡点,适用于团队。 +

+ +

+ + Import All From Umi 迭代。 + + 这是两年前 Umi 3 加的功能,最近发现 Remix、prisma、vitekit 等框架和工具都有类似实现。这种方式有好有坏。好处是通过 + umi 将大量依赖管理起来,用户无需手动安装。坏处是更黑盒,同时有点 Hack。Umi 4 不能解其黑盒问题,但解了 + Hack 问题,让实现无副作用,可以和 Vite、MFSU 等方案无缝结合。 +

+ +

+ + srcTranspiler 和 depTranspiler。 + + 提供针对源码编译和依赖编译更多选择。源码编译可选 babel、swc 和 esbuild,目前没有银弹,合适场景做合适的选择。比如 + swc 由于不支持 top level await,和 mfsu 会有些冲突,但他适用于 build,因为有补丁可以兼容到 + es7;比如 esbuild 适用于 dev,因为快。数据方面以 ant-design-pro 项目为例,源码编译用 + esbuild 相比 babel 在 M1 2020 无缓存情况下会快 3s。 +

+ +

+ + jsMinifier 和 cssMinifier。 + + js 压缩和 css 压缩 Umi 4 默认都用的 esbuild,因为快。同时也提供更多选择,js 压缩还支持 + swc、terser 和 uglifyJs,css 压缩还支持 cssnano。 +

+ +

+ 应用元数据。Umi 4 + 有通过 api.appData + 收集各种项目数据,从配置、路由、package.json、tsconfig.json、npmClient + 到数据流、国际化、antd 用了哪个版本、react 和 react-dom + 的版本等,应有尽有,这对于插件开发者会非常实用,也适用于有统计需求的场景。 +

+ +

+ 微生成器。没错,就是 + modern.js 的微生成器,这功能从 modern.js + 里学习了不少,名字就不改了。举个例子,比如 prettier + 功能,可能不是每个项目都需要,就比较适用于微生成器,按需启用、添加配置、安装依赖。 +

+ +

+ 贴心小改进。 + 还有不少贴心小改进,举两个例子。1 是项目中新增 plugin.ts,会默认作为插件添加,方便项目进行一些插件级的扩展;2 + 是调试问题时通常需要修改编译后的代码看看有没有改对,你把 umi.js 下下来存到项目根目录,umi + 会优先使用这份代码。 +

+ +以上是 Umi 4 目前的新功能。 + +除此之外,还有一些计划在正式版发布之前做的事情。包括 api route、umi server and adapter、route loader、稳定的 lint、更多命令、组件研发 father 4、文档工具 dumi 2 等,会在之后的 RC 版本中与大家见面。 + +欢迎大家尝鲜 Umi 4,官方文档有准备 ant-design-pro 从 Umi 3 到 4 的升级文档。同时 RC 阶段,还准备了一个手把手升级的微信交流群,欢迎 Umi 4 的先行者们加入,祝大家升级顺利,也提前祝大家新年快乐 🧨,🐯 年吉祥。 + +

+ +

diff --git a/docs/docs/blog/webpack-5-prod-cache.en-US.md b/docs/docs/blog/webpack-5-prod-cache.en-US.md new file mode 100644 index 000000000000..2ee308d8eb5c --- /dev/null +++ b/docs/docs/blog/webpack-5-prod-cache.en-US.md @@ -0,0 +1,99 @@ +--- +toc: content +order: 7 +group: + title: Blog +--- + +# 物理构建缓存 + +在 `umi build` 构建生产环境产物时,Umi 4 默认没有配置 webpack 5 的物理缓存,这是因为 webpack 的物理缓存失效时机问题,需要依赖用户项目的实际情况,所以没有很好的通用解决方案。 + +所以,当你不明确哪些依赖会让项目物理缓存失效时,很容易产生构建缓存不失效,导致产物是旧的问题,极大影响研发效率。 + +## 缓存场景 + +当你的项目需要构建缓存时,是有原因的,我们粗略把场景分成两类:普通项目、Monorepo 中的项目。 + +### 普通项目 + +构建比较慢,如何复用上次的物理缓存,做到多次构建提速? + +#### 首选解决思路 + +此时首选的优化思路应该是:考虑使用其他更快的现代转译器,比如调整 [`srcTranspiler`](../docs/api/config#srctranspiler) 、[`cssMinifier`](../docs/api/config#cssminifier) 。 + +#### CI 中的问题 + +物理缓存一般存在于 `node_modules/.cache` ,这就意味着如果你在 CI 中构建,构建的基建必须要支持恢复上次的缓存文件,如果构建容器不支持恢复缓存,同样也无法享受好处。 + +#### 选择依据 + +所以,当你: + +1. **多次构建**:确实有多次反复构建的需求。 + +2. **能恢复缓存**:在本地构建,或在 CI 有手段能恢复上次的物理缓存文件。 + +3. **时间长**:项目构建时间比较长、开启其他转译器仍无法提速(或有强诉求无法切换转译器)。 + +满足这些条件后,你才应该考虑开启物理缓存。 + +#### 配置方法 + +```ts +// .umirc.ts + +import { join } from 'path'; +import { defineConfig } from 'umi'; +import { createHash } from 'crypto'; + +export default defineConfig({ + chainWebpack(config, { env }) { + if (env === 'production') { + config.cache({ + type: 'filesystem', + store: 'pack', + // 🟡 假如你的项目在 CI 中构建每次环境变量都不一样,请挑选或者排除 + version: createEnvironmentHash(process.env), + buildDependencies: { + config: [__filename], + tsconfig: [join(__dirname, 'tsconfig.json')], + packagejson: [join(__dirname, 'package.json')], + umirc: [join(__dirname, '.umirc.ts')], + // 🟡 其他可能会影响项目的配置文件路径,其内容变更会使缓存失效 + }, + }); + } + }, +}); + +function createEnvironmentHash(env: Record) { + const hash = createHash('md5'); + hash.update(JSON.stringify(env)); + const result = hash.digest('hex'); + return result; +} +``` + +请格外注意: + +1. 你的项目有哪些文件、依赖会影响项目,配置他们作为依赖,变更时可以使得缓存失效。 + +2. 因为 `process.env` 包括了所有的 nodejs 环境变量,这非常多,如果环境变量在 CI 中每次构建都存在差异,请挑选所需的环境变量,或者排除掉会变化的。 + + ```ts + // 如挑选可能会影响项目内容的环境变量 + createEnvironmentHash({ + NODE_ENV: process.env.NODE_ENV, + // ... + }); + ``` + +### Monorepo 中的项目 + +在 monorepo 中,如何缓存需要前置构建的其他子包,比如构建 `apps/project-umi` 需要先构建好他依赖的子包 `libs/component` ,但是下次 `libs/component` 没有代码改动,如何跳过这部分前置依赖的构建? + +此时推荐你使用 [Turborepo](https://turbo.build/repo) 来做 monorepo 构建方案,具体使用方法请参见 [官方文档](https://turbo.build/repo/docs) 和 [examples](https://github.com/vercel/turbo/tree/main/examples) 。 + +注:如果在 CI 中构建,同样需要容器支持恢复上次的 turbo 缓存,可以通过 [`--cache-dir`](https://turbo.build/repo/docs/reference/command-line-reference#--cache-dir) 选项更改缓存位置。 diff --git a/docs/docs/docs/api/api.en-US.md b/docs/docs/docs/api/api.en-US.md new file mode 100644 index 000000000000..5c7432892927 --- /dev/null +++ b/docs/docs/docs/api/api.en-US.md @@ -0,0 +1,741 @@ +--- +order: 1 +toc: content +translated_at: '2024-03-17T10:47:17.550Z' +--- + +# API + +For ease of search, the following content is sorted alphabetically. + +## umi + +### createBrowserHistory + +Creates a `BrowserHistory` that uses the browser's built-in `history` to track application navigation. It is recommended for use in modern web browsers that support the HTML5 `history` API. + +Type definition is as follows: +```ts +function createBrowserHistory(options?: { window?: Window }) => BrowserHistory; +``` + +Usage example: +```ts +// create a BrowserHistory +import { createBrowserHistory } from 'umi'; +const history = createBrowserHistory(); +// or a iframe BrowserHistory +import { createBrowserHistory } from 'umi'; +const history = createBrowserHistory({ + window: iframe.contentWindow, +}); +``` +### createHashHistory + +`createHashHistory` returns a `HashHistory` instance. The default `window` is the current document's `defaultView`. + +The main difference between `HashHistory` and `BrowserHistory` is that `HashHistory` stores the current location in the hash part of the URL, which means it does not send a request to the server when switching routes. If you are hosting your site on a server that you cannot fully control, or in a single-page Electron application, `HashHistory` is recommended. + +Usage example: +```ts +// create a HashHistory +import { createHashHistory } from 'umi'; +const history = createHashHistory(); +``` + +### createMemoryHistory + +`MemoryHistory` is not operated or read from the address bar. It is also very suitable for testing and other rendering environments. + +```ts +const history = createMemoryHistory(location) +``` + +### createSearchParams + +A utility function that wraps `new URLSearchParams(init)`, supports creating with arrays and objects + +```ts +import { createSearchParams } from 'umi'; + + +// Assuming the path http://a.com?foo=1&bar=2 +createSearchParams(location.search); +createSearchParams("foo=1&bar=2"); +createSearchParams("?foo=1&bar=2"); + +// Key-value pair object +createSearchParams({ foo: 'bar', qux: 'qoo'}).toString() +// foo=bar&qux=qoo + +// Key-value tuple array +createSearchParams([["foo", "1"], ["bar", "2"]]).toString() +// foo=1&bar=2 +``` + +[URLSearchParams documentation](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams) + +### generatePath + +Generates the actual route to be accessed using the given path with parameters and the corresponding params. + +```ts +import { generatePath } from 'umi'; + +generatePath("/users/:id", { id: "42" }); // "/users/42" +generatePath("/files/:type/*", { + type: "img", + "*": "cat.jpg", +}); // "/files/img/cat.jpg" +``` + +### Helmet + +The Helmet component provided by [react-helmet-async](https://github.com/staylor/react-helmet-async), used to dynamically configure tags in the `head` of the page, such as the `title`. + +> Note: To ensure Helmet still works during SSR, be sure to use the Helmet provided by Umi instead of installing react-helmet separately + +```tsx +import { Helmet } from 'umi'; + +export default function Page() { + return ( + + Hello World + + ); +} +``` + +### history + +Operations related to history, used to obtain current route information, execute route jumps, and listen for route changes. + +```ts +// Recommended to use useLocation in components or hooks +import { useLocation } from 'umi'; +export default function Page() { + let location = useLocation(); + return ( +
+ { location.pathname } + { location.search } + { location.hash } +
+ ); +} +``` + +To get the current route information outside of React components and Hooks. + +```ts +// location object, contains pathname, search, and hash +window.location.pathname; +window.location.search; +window.location.hash; +``` + +Imperative route navigation. + +```ts +import { history } from 'umi'; + +// Jump to a specific route +history.push('/list'); + +// Jump to a specific route with parameters +history.push('/list?a=b&c=d#anchor', state); +history.push({ + pathname: '/list', + search: '?a=b&c=d', + hash: 'anchor', + }, + { + some: 'state-data', + } +); + +// Jump to the current path and refresh state +history.push({}, state) + +// Jump back to the previous route +history.back(); +history.go(-1); +``` + +:::info{title=🚨} +Note: `history.push` and `history.replace` require using `state` as the second parameter passed to these two APIs +::: + + +Route listening. + +```ts +import { history } from 'umi'; + +const unlisten = history.listen(({ location, action }) => { + console.log(location.pathname); +}); +unlisten(); +``` + +### Link + +`` is a React component, an `` element with routing jump functionality. + +Type definition is as follows: + +```ts +declare function Link(props: { + prefetch?: boolean; + to: string | Partial<{ pathname: string; search: string; hash: string }>; + replace?: boolean; + state?: any; + reloadDocument?: boolean; +}): React.ReactElement; +``` + +Example: + +```tsx +import { Link } from 'umi'; + +function IndexPage({ user }) { + return {user.name}; +} +``` + +`` supports relative path navigation; `` does not do routing navigation and is equivalent to the jump behavior of ``. + +If `prefetch` is enabled, then when the user hovers over the component, Umi will automatically start preloading the component js files and data for the routing jump. + +### matchPath + +`matchPath` can match a given path with a known route format and return the match result. + +Type definition is as follows: + +```ts +declare function matchPath( + pattern: PathPattern | string, + pathname: string +): PathMatch | null; +interface PathMatch { + params: Params; + pathname: string; + pattern: PathPattern; +} +interface PathPattern { + path: string; + caseSensitive?: boolean; + end?: boolean; +} +``` + +Example: +```ts +import { matchPath } from 'umi'; +const match = matchPath( + { path: "/users/:id" }, + "/users/123", +); +// { +// "params": { "id": "123" }, +// "pathname": "/users/123", +// "pathnameBase": "/users/123", +// "pattern": { "path": "/users/:id" } +// } +``` +### matchRoutes + +`matchRoutes` can match a given path with multiple potential route choices and return the match result. + +Type definition is as follows: + +```ts +declare function matchRoutes( + routes: RouteObject[], + location: Partial | string, + basename?: string +): RouteMatch[] | null; +interface RouteMatch { + params: Params; + pathname: string; + route: RouteObject; +} +``` + +Example: + +```ts +import { matchRoutes } from 'umi'; +const match = matchRoutes( + [ + { + path: "/users/:id", + }, + { + path: "/users/:id/posts/:postId", + }, + ], + "/users/123/posts/456", +); +// [ +// { +// "params": { +// "id": "123", +// "postId": "456" +// }, +// "pathname": "/users/123/posts/456", +// "pathnameBase": "/users/123/posts/456", +// "route": { +// "path": "/users/:id/posts/:postId" +// } +// } +// ] +``` + +### NavLink + +`` is a special form of ``, aware of whether it is in an active routing state. Often used in navigation menus, breadcrumbs, Tabs to display the current selection status. + +Type definition is as follows: + +```ts +declare function NavLink(props: LinkProps & { + caseSensitive?: boolean; + children?: React.ReactNode | ((props: { isActive: boolean }) => React.ReactNode); + className?: string | ((props: { isActive: boolean }) => string | undefined); + end?: boolean; + style?: React.CSSProperties | ((props: { isActive: boolean }) => string | React.CSSProperties); +}): React.ReactElement; +``` + +The examples below use style, className, and children to render the active state. + +```ts +import { NavLink } from 'umi'; + +function Navs() { + return
    +
  • isActive ? { color: 'red' } : undefined}>Messages
  • +
  • isActive ? 'active' : undefined}>Tasks
  • +
  • {({ isActive }) => Blog}
  • +
; +} +``` + +### Outlet + +`` is used to render child routes within a parent route. If the parent route is an exact match, it will render the index route of the child routes (if any). + +Type definition is as follows: + +```ts +interface OutletProps { + context?: unknown; +} +declare function Outlet( + props: OutletProps +): React.ReactElement | null; +``` + +Example: + +```ts +import { Outlet } from 'umi'; + +function Dashboard() { + return ( +
+

Dashboard

+ +
+ ); +} + +function DashboardWithContext() { + return ( +
+

Dashboard

+ +
+ ); +} +``` + +The `context` of the `Outlet` component can be retrieved in the child component using the API `useOutletContext`. + +### resolvePath + +Used to resolve front-end routing jump paths on the client side. + +Type definition is as follows: + +```ts +declare function resolvePath( + to: Partial | string, + fromPathname?: string +): { + pathname: string; + search: string; + hash: string; +}; +``` + +Example: + +```ts +// Same-level relative jump, returns { pathname: '/parent/child', search: '', hash: '' } +resolvePath('child', '/parent'); +resolvePath('./child', '/parent'); +resolvePath('', '/parent/child'); +resolvePath('.', '/parent/child'); + +// Ancestor level relative jump, returns { pathname: '/parent/sibling', search: '', hash: '' } +resolvePath('../sibling', '/parent/child'); +resolvePath('../../parent/sibling', '/other/child'); + +// Absolute jump, returns { pathname: '/target', search: '', hash: '' } +resolvePath('/target', '/parent'); +resolvePath('/target', '/parent/child'); + +// Jump with search and hash, returns { pathname: '/params', search: '?a=b', hash: '#c' } +resolvePath('/params?a=b#c', '/prev'); +``` + +### terminal + +`terminal` is a tool for logging messages from the browser to the node terminal during the development stage. + +Example: +```ts +import {terminal} from 'umi'; +// The following three commands will print logs in different colors on the umi startup terminal +terminal.log('i am log level'); +terminal.warn('i am warn level'); +terminal.error('i am error level'); +``` +Note that `terminal` only takes effect when the environment variable `NODE_ENV` is not `production`; the corresponding log call functions in Umi's build output will not have any effect, so you can leave the calls to `terminal` in your code. + +### useAppData + +`useAppData` returns global application data. + +Type definition is as follows: + +```ts +declare function useAppData(): { + routes: Record; + routeComponents: Record>; + clientRoutes: ClientRoute[]; + pluginManager: any; + rootElement: string; + basename: string; + clientLoaderData: { [routeKey: string]: any }; + preloadRoute: (to: string) => void; +}; +``` +Note: This API might still be adjusted. + +### useLocation + +`useLocation` returns the current location object. + +Type definition is as follows: + +```ts +declare function useLocation(): { + pathname: string; + search: string; + state: unknown; + key: Key; +}; +``` + +One scenario is to perform some side effect operations when location changes, such as page view statistics. + +```ts +import { useLocation } from 'umi'; + +function App() { + const location = useLocation(); + React.useEffect(() => { + ga('send', 'pageview'); + }, [location]); + // ... +} +``` + +### useMatch + +`useMatch` returns match information for the given path; if no match, it will return `null` + +Type definition is as follows: + +```ts +declare function useMatch(pattern: { + path: string; + caseSensitive?: boolean; + end?: boolean; +} | string): { + params: Record; + pathname: string; + pattern: { + path: string; + caseSensitive?: boolean; + end?: boolean; + }; +}; +``` + +Example: +```tsx +import { useMatch } from 'umi'; + +// when url = '/events/12' +const match = useMatch('/events/:eventId'); +console.log(match?.pathname, match?.params.eventId); +// '/events/12 12' +``` + +### useNavigate + +`useNavigate` hook function returns a function that can control jumping; for example, it can be used after submitting a form to jump to another page. + +```ts +declare function useNavigate(): NavigateFunction; + +interface NavigateFunction { + ( + to: To, + options?: { replace?: boolean; state?: any } + ): void; + (delta: number): void; +} +``` + +Example: + +* Jump to path +```ts +import { useNavigate } from 'umi'; + +let navigate = useNavigate(); +navigate("../success", { replace: true }); +``` + +* Go back to the previous page +```ts +import { useNavigate } from 'umi'; + +let navigate = useNavigate(); +navigate(-1); +``` + +### useOutlet + +`useOutlet` returns the child route element currently matched, used internally by the ``. + +Type definition is as follows: +```ts +declare function useOutlet(): React.ReactElement | null; +``` + +Example: +```ts +import { useOutlet } from 'umi'; + +const Layout = ()=>{ + const outlet = useOutlet() + + return
+ {outlet} +
+} +``` + +### useOutletContext + +`useOutletContext` is used to return the `context` mounted on the `Outlet` component. + +Type definition is as follows: +```ts +declare function useOutletContext(): Context; +``` + +Example: +```ts +import { useOutletContext, Outlet } from 'umi'; + +const Layout = () => { + return
+ +
+} + +const SomeRouteComponentUnderLayout = () => { + const layoutContext = useOutletContext(); + + return JSON.stringify(layoutContext) // {"prop":"from Layout"} +} +``` + +### useParams + +The `useParams` hook function returns a read-only key-value pair object of dynamic route matching parameters; child routes will inherit dynamic parameters from parent routes. + +Type definition is as follows: +```ts +declare function useParams< + K extends string = string +>(): Readonly>; +``` + +Example: + +```ts +import { useParams } from 'umi'; + +// Assuming a route configuration user/:uId/repo/:rId +// Current path user/abc/repo/def +const params = useParams() +/* params +{ uId: 'abc', rId: 'def'} +*/ +``` + +### useResolvedPath + +`useResolvedPath` resolves the complete routing information for the target address based on the current path. + +Type definition is as follows: +```ts +declare function useResolvedPath(to: To): Path; +``` + +Example: + +```ts +import { useResolvedPath } from 'umi'; + +const path = useResolvedPath('docs') +/* path +{ pathname: '/a/new/page/docs', search: '', hash: '' } +*/ +``` + +### useRouteData + +`useRouteData` returns the route data of the currently matched route. + +Type definition is as follows: + +```ts +declare function useRouteData(): { + route: Route; +}; +``` +Note: This API might still be adjusted. + +Example: +```ts +import { useRouteData } from 'umi'; + +const route = useRouteData(); +/* route +{ + route: { + path: 'a/page', + id: 'a/page/index', + parentId: '@@/global-layout', + file: 'a/page/index.tsx' + } +} +*/ +``` + +### useRoutes + +`useRoutes` is a hook function to render routes, pass in route configuration and optional parameter `location` to get the rendering result; if there is no matched route, the result is `null`. + +Type definition is as follows: +```ts +declare function useRoutes( + routes: RouteObject[], + location?: Partial | string; +): React.ReactElement | null; +``` + +Example: + +```ts +import * as React from "react"; +import { useRoutes } from "umi"; + +function App() { + let element = useRoutes([ + { + path: "/", + element: , + children: [ + { + path: "messages", + element: , + }, + { path: "tasks", element: }, + ], + }, + { path: "team", element: }, + ]); + + return element; +} +``` + +### useRouteProps + +Read the props attribute of the current route in the route configuration using this hook. You can use this hook to obtain additional information in the route configuration. + +```ts +// .umirc.ts +routes: [ + { + path: '/', + custom_key: '1', + } +] +``` + +```ts +import { useRouteProps } from 'umi' + +export default function Page() { + const routeProps = useRouteProps() + + // use `routeProps.custom_key` +} +``` + +Note: Also applicable to convention-based routing. + +### useSelectedRoutes + +Used to read all the route information hit by the current path. For example, in a `layout`, it is possible to obtain information on all the subroutes hit, and also to obtain parameters configured in `routes`, which is very useful. + +Example: + +```tsx +// layouts/index.tsx + +import { useSelectedRoutes } from 'umi' + +export default function Layout() { + const routes = useSelectedRoutes() + const lastRoute = routes.at(-1) + + if (lastRoute?.pathname === '/some/path') { + return
1 :
+ } + + if ( diff --git a/docs/docs/docs/api/commands.en-US.md b/docs/docs/docs/api/commands.en-US.md new file mode 100644 index 000000000000..279d7db56cd0 --- /dev/null +++ b/docs/docs/docs/api/commands.en-US.md @@ -0,0 +1,308 @@ +--- +order: 4 +toc: content +translated_at: '2024-03-17T10:43:51.227Z' +--- + +# Command Line + +umi offers many built-in command line interfaces to start and build projects, as well as some auxiliary development commands, such as generators. + +To get a list of available commands, you can run the help command in the project directory: + +```bash +umi help +``` + +You should see logs similar to the following: + +```bash +Usage: umi [options] + +Commands: + + build build app for production + config umi config cli + dev dev server for development + help show commands help + lint lint source code using eslint and stylelint + setup setup project + deadcode check dead code + version show umi version + v show umi version + plugin inspect umi plugins + verify-commit verify the commit message, which is usually used with husky. + preview locally preview production build + run run the script commands, support for ts and zx + generate generate code snippets quickly + g generate code snippets quickly + +Run `umi help ` for more information of specific commands. +Visit https://umijs.org/ to learn more about Umi. +``` + +> For ease of lookup, the commands below are sorted alphabetically. + +## build + +Build the project for deployment in a production environment. + +```bash +$ umi build +``` + +## config + +Quickly view and modify configuration via the command line. + +Viewing configuration, you can use `list` or `get`. + +```bash +$ umi config list + - [key: polyfill] false + - [key: externals] { esbuild: true } + +$ umi config get mfsu + - [key: externals] { esbuild: true } +``` + +Modifying configuration, you can use `set` or `remove`. + +```bash +$ umi config set polyfill false +set config:polyfill on /private/tmp/sorrycc-wsYpty/.umirc.ts + +$ umi config remove polyfill +remove config:polyfill on /private/tmp/sorrycc-wsYpty/.umirc.ts +``` + +## dev + +Start a local development server for project development and debugging. + +```bash +$ umi dev + ╔═════════════════════════════════════════════════════╗ + ║ App listening at: ║ + ║ > Local: https://127.0.0.1:8001 ║ +ready - ║ > Network: https://192.168.1.1:8001 ║ + ║ ║ + ║ Now you can open browser with the above addresses👆 ║ + ╚═════════════════════════════════════════════════════╝ +event - compiled successfully in 1051 ms (416 modules) +``` + +## generate + +Used for incrementally generating files or enabling features, the command line alias is `g`. + +When used without any parameters, it will opt for an interactive generator selection. + +```bash +$ umi g +# or +$ umi generate +? Pick generator type › - Use arrow-keys. Return to submit. +❯ Create Pages -- Create a umi page by page name + Enable Prettier -- Enable Prettier +``` + +You can also specify parameters. + +```bash +# Generate route files +$ umi g page index --typescript --less +``` + +## help + +View help. + +```bash +$ umi help +Usage: umi [options] + +Commands: + + build build app for production + config umi config cli + dev dev server for development + help show commands help + setup setup project + version show umi version + plugin inspect umi plugins + generate generate code snippets quickly + +Run `umi help ` for more information of specific commands. +Visit https://umijs.org/ to learn more about Umi. +``` + +You can also specify a command to see detailed help for that command. + +```bash +$ umi help build +Usage: umi build [options] +build app for production. + +Details: + umi build + + # build without compression + COMPRESS=none umi build + + # clean and build + umi build --clean +``` + +## lint + +Used to check and correct code to match rules. + +```bash +$ umi lint +Usage: umi lint + + Support for validation of js, ts, tsx, jsx type files only: umi lint --eslint-only + + Support for validation of css, less and other style files only: umi lint --stylelint-only + + Support for cssinjs mode validation: umi lint --stylelint-only --cssinjs + + Correct the code: --fix + +``` + +## plugin + +Plugin related operations, currently only supports the `list` subcommand. + +List all plugins. + +```bash +$ umi plugin list +- @umijs/core/dist/service/servicePlugin +- @umijs/preset-umi (from preset) +- @umijs/preset-umi/dist/registerMethods (from preset) +- @umijs/preset-umi/dist/features/appData/appData (from preset) +- @umijs/preset-umi/dist/features/check/check (from preset) +- @umijs/preset-umi/dist/features/configPlugins/configPlugins (from preset) +- virtual: config-styles +- virtual: config-scripts +- virtual: config-routes +- virtual: config-plugins +... +``` + +## preview + +The `umi preview` command will launch a local static web server, running the dist folder at http://127.0.0.1:4172, for previewing the production build, supporting proxy, mock, etc. settings. + +You can use the `--port` parameter to configure the service's running port. + +```bash +$ umi preview --port 9527 +``` + +Now the `preview` command will run the server at http://127.0.0.1:9527. + +Use the `--host` parameter to specify the service's running hostname. + +The following user configurations will also take effect during `preview` + +* [https](./config#https) +* [proxy](../guides/proxy) +* [mock](../guides/mock) + +Note that the `dist` directory will change with the configuration of `outputPath`. + +## run + +The `umi run` command allows you to run TypeScript and ESM files like running js with node. You can pair it with [zx](https://github.com/google/zx) for better script command usability. + +```bash +$ umi run ./script.ts +``` + +## setup + +Initialize the project, doing operations like generating temporary files. Usually set in `scripts.postinstall` in package.json. + +```bash +{ + "scripts": { "postinstall": "umi setup" } +} +``` + +## deadcode + +Used to find files in the src directory that are not referenced, and output to the root directory. + +```bash +$ umi deadcode +- Preparing... +- begin check deadCode +- write file /examples/umi-run/DeadCodeList-{timeStamp}.txt +- check dead code end, please be careful if you want to remove them +``` + +## mfsu + +The `umi mfsu` command can be used to view MFSU dependency information, rebuild MFSU dependencies, and clear MFSU dependencies. + +```bash title="Get MFSU command help" +$ umi mfsu +``` + +```bash title="Get MFSU dependency list" +$ umi mfsu ls +warning@4.0.3 +regenerator-runtime/runtime.js@0.13.11 +react/jsx-dev-runtime@18.1.0 +react-intl@3.12.1 +react-error-overlay/lib/index.js@6.0.9 +react@18.1.0 +qiankun@2.8.4 +lodash/noop@4.17.21 +lodash/mergeWith@4.17.21 +lodash/concat@4.17.21 +... +``` + +```bash title="Rebuild MFSU dependency" +$ umi mfsu build +info - Preparing... +info - MFSU eager strategy enabled +warn - Invalidate webpack cache since mfsu cache is missing +info - [MFSU] buildDeps since cacheDependency has changed +... +info - [plugin: @umijs/preset-umi/dist/commands/mfsu/mfsu] [MFSU][eager] build success +``` + +```bash title="Clear MFSU dependency" +$ # Delete dependency information list +$ umi mfsu remove +$ # Delete dependency information list and product files +$ umi mfsu remove --all +``` + +## verifyCommit + +Verify commit message information, usually used in conjunction with [husky](https://github.com/typicode/husky). + +For example, configure the following in `.husky/commit-msg`, + +```bash +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install umi verify-commit $1 +``` + +## version + +View the `umi` version, equivalent to `umi -v`. + +```bash +$ umi version +4.0.0 +``` diff --git a/docs/docs/docs/api/config.en-US.md b/docs/docs/docs/api/config.en-US.md new file mode 100644 index 000000000000..e06f341b1e43 --- /dev/null +++ b/docs/docs/docs/api/config.en-US.md @@ -0,0 +1,591 @@ +--- +order: 2 +toc: content +translated_at: '2024-03-17T10:42:29.295Z' +--- + +# Configuration + +For the custom configurations available in umi, you can use the `.umirc.ts` file or `config/config.ts` in the project root directory. It's noteworthy that both files have the same functionality, just residing in different directories. You have to choose one of them, with `.umirc.ts` having a higher priority. + +> For more information about the directory structure, you can learn more [here](../guides/directory-structure). + +umi's configuration file is a regular node module, which is used when the umi [command line](./commands) is executed and is not included in the browser-side build. + +> For some configurations that are required for the browser-side build, and some configurations that affect the style presentation, in umi they are collectively referred to as "runtime configuration", you can see more about it in [runtime configuration](./runtime-config). + +Here is the simplest example of an umi configuration file: + +```ts +import { defineConfig } from 'umi'; + +export default defineConfig({ + outputPath: 'dist', +}); +``` + +Wrapping configurations with `defineConfig` is to get better spell check support when writing the configuration file. If you don't need it, `export default {}` works too. + +It's important to note that when using umi, you don't need to understand the purpose of each configuration. You can briefly browse through all the configurations that umi supports below, and then, when needed, come back to see how to enable and modify the configurations you need. + +> For convenience, the following configurations are sorted alphabetically. + +## alias + +- Type: `Record` +- Default value: `{}` + +Configure aliases, to map the source in import statements. + +For example: + +```js +{ + alias: { + foo: '/tmp/to/foo', + } +} +``` + +Then in the code, `import 'foo'` will actually become `import '/tmp/to/foo'`. + +Some tips: + +1. It's better to use absolute paths as the value of alias, especially when pointing to a dependency, remember to add `require.resolve`, for instance, + +```js +// ❌ +{ + alias: { + foo: 'foo', + } +} + +// ✅ +{ + alias: { + foo: require.resolve('foo'), + } +} +``` + +2. If you don't want subpaths to be mapped as well, remember to add a `$` suffix, for example + +```js +// import 'foo/bar' will be mapped to import '/tmp/to/foo/bar' +{ + alias: { + foo: '/tmp/to/foo', + } +} + +// import 'foo/bar' will still be import 'foo/bar', and won't be modified +{ + alias: { + foo$: '/tmp/to/foo', + } +} +``` + +## autoprefixer + +- Type: `object` +- Default value: `{ flexbox: 'no-2009' }` + +Used for parsing CSS and adds vendor prefixes to CSS rules using values from Can I Use. It enables autoprefixing, such as automatically adding `-webkit-` prefix. + +For more configurations, please consult [autoprefixer's options](https://github.com/postcss/autoprefixer#options). + +## analyze + +- Type: `object` +- Default value: `{}` + +Specific configuration options for the analyzer plugin when analyzing product composition through the [`ANALYZE`](../guides/env-variables#analyze) environment variable. See [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin) + +When using Vite mode, in addition to customizing the configuration of [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer), `excludeAssets`, `generateStatsFile`, `openAnalyzer`, `reportFilename`, `reportTitle` options will automatically adapt. + +## base + +- Type: `string` +- Default value: `/` + +To deploy the umi project in a non-root directory, you can use the base configuration. + +The base configuration allows you to set a routing prefix for your application. For example, with routes `/` and `/users`, after setting base to `/foo/`, you can access the previous routes through `/foo/` and `/foo/users`. + +> Note: The base configuration must be set at build time and can't be changed without rebuilding, because this value is inlined in the client-side package. + +## cacheDirectoryPath + +- Type: `string` +- Default value: `node_modules/.cache` + +By default, Umi stores some cache files generated during the build process in the `node_modules/.cache` directory, such as logger logs, webpack cache, mfsu cache, etc. You can modify Umi's cache file directory using the `cacheDirectoryPath` configuration. + +Example, + +```js +// Change the cache file path to the node_modules/.cache1 folder +cacheDirectoryPath: 'node_modules/.cache1', +``` + +## chainWebpack + +- Type: `(memo, args) => void` +- Default value: `null` + +To extend Umi's built-in webpack configuration, we provide a way to modify the webpack configuration in a chained programming style, based on webpack-chain. See the documentation of webpack-api for specific APIs. + +As shown below: + +```js +export default { + chainWebpack(memo, args) { + return memo; + }, +}; +``` + +This function has two parameters: + +- `memo` is the existing webpack configuration +- `args` contains some additional information and auxiliary objects, currently including `env` and `webpack`. `env` is the current environment, with values of `development` or `production`; `webpack` is the webpack object, from which webpack's built-in plugins can be obtained. + +Usage example: + +```js +export default { + chainWebpack(memo, { env, webpack }) { + // Set alias + memo.resolve.alias.set('foo', '/tmp/to/foo'); + + // Add extra plugins + memo.plugin('hello').use(Plugin, [...args]); + + // Delete Umi's built-in plugin + memo.plugins.delete('hmr'); + }, +}; +``` + +## clickToComponent + +- Type: `{ editor?: string }` +- Default value: `false` + +> Currently, only React projects are supported. + +When this feature is enabled, you can jump to the source code location in the editor by `Option+Click/Alt+Click` on the component. `Option+Right-click/Alt+Right-click` opens the context to view the parent component. + +About parameters. The `editor` parameter defaults to 'vscode', supporting `vscode` & `vscode-insiders`. + +Configure the behavior of clickToComponent, for more details see [click-to-component](https://github.com/ericclemmons/click-to-component). + +Example: + +```ts +// .umirc.ts +export default { + clickToComponent: {}, +}; +``` + +## clientLoader + +- Type: `{}` +- Default value: `false` + +When enabled, you can declare a data loading function `clientLoader` for each route. Extracting the data loading process required by the page into `clientLoader` allows Umi to preload the data before the page components are fully loaded, avoiding the issue of waterfall requests. For details, see [Route Data Preloading](../guides/client-loader). + +Example: + +```ts +// .umirc.ts +export default { + clientLoader: {}, +}; +``` + +Configuration enabled, use in the route component: + +```jsx +// pages/.../some_page.tsx + +import { useClientLoaderData } from 'umi'; + +export default function SomePage() { + const { data } = useClientLoaderData(); + return
{data}
; +} + +export async function clientLoader() { + const data = await fetch('/api/data'); + return data; +} +``` + +## codeSplitting + +- Type: `{ jsStrategy: 'bigVendors' | 'depPerChunk' | 'granularChunks'; jsStrategyOptions: {} }` +- Default value: `null` + +Provide code splitting strategy solutions. + +bigVendors is the big vendors scheme, which packs the files under node_modules in the async chunk together, which can avoid repetition. At the same time, the disadvantages are, 1) the size of a single file is too large, 2) there is no caching efficiency to speak of. + +depPerChunk is similar to bigVendors, but different in that dependencies are split by package name + version, solving bigVendors’ issues with size and caching efficiency. However, it also brings potential problems, possibly leading to more requests. My understanding is that for non-large projects it’s actually fine because, 1) a single page's requests will not include a very large number of dependencies, 2) based on HTTP/2, dozens of requests are not a problem. But, for large or huge projects, a more suitable solution is needed. + +granularChunks take a middle value between bigVendors and depPerChunk, while also having better utilization of caching efficiency. Unless there are special circumstances, the granularChunks strategy is recommended. + +## conventionLayout + +- Type: `boolean` +- Default value: `undefined` + +`src/layouts/index.[tsx|vue|jsx|js]` is conventionally set as layout, enabled by default. It can be disabled by setting `conventionLayout: false`. + +## conventionRoutes + +- Type: `{ base: string; exclude: RegExp[] }` +- Default value: `null` + +Modify the default convention-based route rules, only valid when using umi's convention-based routing. Convention-based routing, also known as file-based routing, means that you don’t need to write configurations manually, as the file system is the routes, and the routing configuration is analyzed through directories and files and their naming. + +When using convention-based routing, it's conventioned that all `(j|t)sx?` files under `src/pages` are routes. + +> You can see more details in [Convention-based Routing](../guides/routes#conventional-routing). + +### base + +`base` is used to set the base path for the routes by convention, by default reading from `src/pages`. If it is a site for documentation, you might need to change it to `./docs`; + +### exclude + +You can use `exclude` to filter some files that are not needed, such as you can use it to exclude files under components, models, etc. + +Example, + +```js +// Do not recognize files under components and models directories as routes +conventionRoutes: { + exclude: [/\/components\//, /\/models\//], +} +``` + +## copy + +- Type: `Array` +- Default value: `[]` + +Configure the files or folders to be copied to the output directory. + +When configuring as a string, it is copied to the product directory by default, such as: + +```ts +copy: ['foo.json', 'src/bar.json'] +``` + +Will produce the following product structure: + +``` ++ dist + - bar.json + - foo.json ++ src + - bar.json +- foo.json +``` + +You can also configure the specific copy location through objects, where the relative path starts from the project root directory: + +```ts +copy: [ + { from: 'from', to: 'dist/output' }, + { from: 'file.json', to: 'dist' } +] +``` + +This time will produce the following product structure: + +``` ++ dist + + output + - foo.json + - file.json ++ from + - foo.json +- file.json +``` + +## crossorigin + +- Type: `{ includes?: string[] }` +- Default value: `false` + +Configure the script tag's crossorigin. If declared, the local script will have a crossorigin="anonymous" attribute. + +Regarding the parameters. The parameter `includes` can allow extra non-local script tags to have this attribute. + +For example: + +``` +crossorigin: {} +``` + +Then the output HTML will have these changes, + +```diff +- + ++ + +``` + +## cssMinifier + +- Type: `string`, optional values: `esbuild`, `cssnano`, `parcelCSS`, `none` +- Default value: `esbuild` + +Configure the tool for minifying CSS during build time; `none` means not to minify. + +Example: + +```js +{ + cssMinifier: 'esbuild' +} +``` + +## cssMinifierOptions + +- Type: `object` +- Default value: `{}` + +Options for the CSS minification tool, `cssMinifier`. + +Example: + +```js +{ + cssMinifier: 'esbuild', + cssMinifierOptions: { + minifyWhitespace: true, + minifySyntax: true, + }, +} +``` + +Consult the corresponding documentation for CSS minification configuration. + +- [esbuild reference](https://esbuild.github.io/api/#minify) +- [cssnano reference](https://cssnano.co/docs/config-file/) +- [parcelCSS reference](https://github.com/parcel-bundler/parcel-css/blob/master/node/index.d.ts) + +## cssPublicPath +- Type: `string` +- Default value: `./` + +Customize the public path for external resources like images and files in CSS. Acts similar to `publicPath`. The default value is `./`. + +## cssLoader + +- Type: `object` +- Default value: `{}` + +Configure css-loader , see [css-loader > options](https://github.com/webpack-contrib/css-loader#options) + +## cssLoaderModules + +- Type: `object` +- Default value: `{}` + +Configure the behavior of CSS modules, see [css-loader > modules](https://github.com/webpack-contrib/css-loader#modules). + +For example: + +```ts +cssLoaderModules: { + // Configure to use camelCase + exportLocalsConvention: 'camelCase' +} +``` + +## deadCode + +- Type: `{ patterns?: string[]; exclude?: string[]; failOnHint?: boolean; detectUnusedFiles?: boolean; detectUnusedExport?: boolean; context?: string }` +- Default value: `false` + +Check for unused files and exports, only enabled at build phase. + +For example: + +``` +deadCode: {} +``` + +Then execute build, if any issues are found, a warning will be printed: + +``` +Warning: There are 1 unused files: + 1. /pages/index.module.less + Please be careful if you want to remove them (¬º-°)¬. +``` + +Configuration items: + + - `patterns` : Scope of code identification, such as `['src/pages/**']` + - `exclude` : Scope to exclude from detection, such as `['src/pages/utils/**']` + - `failOnHint` : Whether to stop the process if detection fails, default `false` not to stop + - `detectUnusedFiles` : Whether to detect unused files, default `true` for detection + - `detectUnusedExport` : Whether to detect unused exports, default `true` for detection + - `context` : The directory to start matching, default as the current project root directory + +## define + +- Type: `Record` +- Default value: As shown below + +``` + { + 'process.env.NODE_ENV' : process.env.NODE_ENV, + 'process.env.HMR' : process.env.HMR, + 'process.env.SOCKET_SERVER': process.env.ERROR_OVERLAY' + } +``` + +Set available variables in the code based on [define-plugin plugin](https://webpack.js.org/plugins/define-plugin/). + +:::warning{title=🚨} +1. Property values will be converted with `JSON.stringify`. +2. The replacement of key values is matched by their syntactic form, for instance, configuring `{'a.b.c': 'abcValue'}` can't replace the code in `a.b?.c`. +::: + +For example, + +``` +define: { FOO: 'bar' } +``` + +Then the code like `console.log(hello, FOO)` will be compiled to `console.log(hello, 'bar')`. + +When you use these variables in a TypeScript project, you need to declare the variable type in the typings file to support ts type hints, for example: + +If your typings file is global: + +```ts +// typings.d.ts +declare const FOO: string; +``` + +If your typings file is non-global (contains import/export): + +```ts +// typings.d.ts +import './other.d.ts'; + +declare global { + const FOO: string; +} +``` +## devtool + +- Type: `string` +- Default value: Dev defaults to `cheap-module-source-map`, no sourcemap by default for build + +Sets the sourcemap generation method. + +Common optional values include: + +- `eval`, the fastest type, but does not support old browsers +- `source-map`, the slowest but most comprehensive type + +Example, + +```js +// Disable sourcemap generation in dev stage +devtool: false; + +// Only set sourcemap for dev stage +devtool: process.env.NODE_ENV === 'development' ? 'eval' : false; +``` + +## classPropertiesLoose +- Type: `object` +- Default value: `{}` + +Set babel class-properties to enable loose mode + +## esbuildMinifyIIFE + +- Type: `boolean` +- Default value: `false` + +Fix the namespace conflict caused by global variables automatically introduced by the esbuild compressor. + +Since Umi 4 defaults to using esbuild as the compressor, the compressor will automatically inject global variables as polyfill, which may cause conflicts like async chunks global variables, conflicts between qiankun sub-apps and main apps' global variables, etc. This issue can be resolved by enabling this option or switching the [`jsMinifier`](#jsminifier-webpack) compressor. + +For more details, see [vite#7948](https://github.com/vitejs/vite/pull/7948). + +Example, +```ts +esbuildMinifyIIFE: true +``` + +## externals + +- Type: `Record | Function` +- Default value: `{}` + +Set which modules are not to be bundled, instead to be imported through ` +``` + +For more details, [see here](https://router.vuejs.org/guide/advanced/composition-api.html#accessing-the-router-and-current-route-inside-setup) + +### router-link + +For more details, [see here](https://router.vuejs.org/guide/#router-link) + +### router-view + +For more details, [see here](https://router.vuejs.org/guide/#router-view) + +## Runtime Configuration + +You can control Vue and Vue-router related configuration through exporting settings in the conventional `src/app.tsx`. + +### router + +Configure router settings + +```ts +// src/app.tsx +export const router: RouterConfig = { + // @ts-ignore + scrollBehavior(to, from) { + console.log('scrollBehavior', to, from); + }, +}; +``` + +### onMounted({app, router}) + +Callback for successful Vue app mount, where you can get instances of app and router for global component registration, route interceptors, etc. + +```ts +export function onMounted({ app, router }: any) { + console.log('onMounted', app, router); + app.provide('umi-hello', { + h: 'hello', + w: 'word', + }); +} +``` + +### rootContainer(container) + +Modify the root component rendered by vue-router. + +For example, to wrap a parent component around the outside + +```ts +import { h } from 'vue' + +export function rootContainer(container) { + return h(ThemeProvider, null, container); +} +``` + +## Examples + +For more examples, see demos: + +* [boilerplate-vue](https://github.com/umijs/umi/tree/master/examples/boilerplate-vue) +* [with-vue-pinia](https://github.com/umijs/umi/tree/master/examples/with-vue-pinia) +* [with-vue-element-plus](https://github.com/umijs/umi/tree/master/examples/with-vue-element-plus) diff --git a/docs/docs/docs/introduce/contributing.en-US.md b/docs/docs/docs/introduce/contributing.en-US.md new file mode 100644 index 000000000000..447c798cf631 --- /dev/null +++ b/docs/docs/docs/introduce/contributing.en-US.md @@ -0,0 +1,198 @@ +--- +order: 3 +toc: content +translated_at: '2024-03-17T09:56:34.842Z' +--- + +# Contributing + +❤️ Loving Umi and want to get involved? Thanks! + +## Environment Setup + +### Node.js and pnpm + +Developing Umi requires Node.js 16+ and `pnpm` v8. + +It is recommended to use [`nvm`](https://github.com/nvm-sh/nvm) to manage Node.js versions to avoid permission issues and easily switch between Node.js versions. Windows developers can use [`nvm-windows`](https://github.com/coreybutler/nvm-windows). + +Install `pnpm` via one of the methods on its [official website](https://pnpm.io/installation). + +### Clone the Project + +```bash +$ git clone git@github.com:umijs/umi.git +$ cd umi +``` + +### Install Dependencies and Build + +```bash +$ pnpm i && pnpm build +``` + +## Developing Umi + +### Start the dev Command + +A must-run command for local development of Umi, for compiling TypeScript files in the `src` directory to the `dist` directory, and incrementally compiles on file changes. + +```bash +$ pnpm dev +``` + +If you find it rather slow, you can also run the `pnpm dev` command for a specific package, for example. + +```bash +$ cd packages/umi +$ pnpm dev +``` + +### Run Example + +The `examples` directory contains various examples used for testing. Running an example is a common way to ensure functions work correctly during Umi development. Each example is equipped with a dev script, so just enter the example and run `pnpm dev`. + +```bash +$ cd examples/boilerplate +$ pnpm dev +``` + +To run in vite mode, add the `--vite` argument, + +```bash +$ pnpm dev --vite +``` + +### Testing + +Currently running tests is fast, completing in just over 10s+. It’s recommended to run tests locally before submitting a PR to reduce Round Trips. + +```bash +$ pnpm test +... +Test Suites: 1 skipped, 43 passed, 43 of 44 total +Tests: 6 skipped, 167 passed, 173 total +Snapshots: 0 total +Time: 13.658 s +Ran all test suites. +``` + +To run tests for specific files, use `pnpm jest` because `pnpm test` runs with turborepo enabled. + +For example, + +```bash +$ pnpm jest packages/plugin-docs/src/compiler.test.ts +``` + +## Contributing to Umi Documentation + +Umi's documentation is implemented by Umi@4 and the `@umijs/plugin-docs` plugin, essentially an Umi project. Execute the following commands in the root directory to start development: + +```bash +# Install Umi document dependencies +$ pnpm doc:deps +# Start Umi document development +# Compilation takes longer on first launch, please be patient +$ pnpm doc:dev +``` + +Open the specified port number to see real-time updates of documentation and results of the `@umijs/plugin-docs` plugin development. + +### Writing Umi Documentation + +Umi documentation is written in MDX format. MDX is an extension of the Markdown format allowing you to insert JSX components while writing Umi documentation. + +:::success{title=🏆︎} +When writing **Documentation**, you can find usable components in `packages/plugin-docs/client/theme-doc/components`. When writing **Blog posts**, usable components can be found in `packages/plugin-docs/client/theme-blog/components`. +::: + +Umi documentation code highlighting is based on [`Rehype Pretty Code`](https://github.com/atomiks/rehype-pretty-code), for full capabilities and instructions, please visit its [official documentation](https://rehype-pretty-code.netlify.app). + +Execute the following command in the root directory to format existing Umi documentation: + +```bash +$ pnpm format:docs +``` + +After formatting the documentation, it is recommended to **only submit the Umi documents you have written or modified**. Different document contributors have varying writing styles and formatting may not always retain the original intended style. + +### Participating in Umi Documentation Plugin Development + +Open a new terminal and execute the following commands: + +```bash +$ cd packages/plugin-docs +$ pnpm dev:css +``` + +Now, when you modify the `tailwind.css` file or TailwindCSS style classes during development, a `tailwind.out.css` stylesheet file will be automatically compiled and generated. + +Umi listens for changes in the `docs` and `packages/plugin-docs/client` directories, but does not listen to changes in `packages/plugin-docs/src`. + +:::info{title=💡} +If you need to compile files in `packages/plugin-docs/src`, please move to the `packages/plugin-docs` directory and execute the `pnpm build` command, then restart development. +::: + +Executing the following command in the root directory to format the code of Umi documentation plugin: + +```bash +$ pnpm format:plugin-docs +``` + +Execute the following command in the root directory to build Umi documentation: + +```bash +$ pnpm doc:build +``` + +## Adding a New Package + +Adding a new package has a script encapsulated, no need to manually copy `package.json`, etc.: + +```bash +# Create package directory +$ mkdir packages/foo +# Initialize package development +$ pnpm bootstrap +``` + +## Updating Dependencies + +> It is not recommended for non-Core Maintainers to update a large number of dependencies because it involves pre-packaging of dependencies and there are many points to note. + +Run `pnpm dep:update` to update dependencies. + +```bash +$ pnpm dep:update +``` + +Since Umi pre-packages some dependencies, after updating dependencies, it's necessary to check if the updated dependency is in devDependencies and whether it has been pre-packaged. If so, execute `pnpm build:deps` in the corresponding package and specify the dependency to update pre-packaged dependency files. + +```bash +$ pnpm build:deps --dep webpack-manifest-plugin +``` + +## Release + +Only Core Maintainers can carry out releases. + +```bash +$ pnpm release +``` + +## Rolling Back through dist-tag + +For example, to roll back to 4.0.81. + +```bash +$ pnpm -rc --filter "./packages/**" exec pnpm dist-tag add \$PNPM_PACKAGE_NAME@4.0.81 latest +``` + +## Joining the Contributor Group + +For those who have submitted Bugfix or Feature type PRs and are interested in participating in maintaining Umi, you can first scan the QR code below with DingTalk (mention your github id), and then I will add you to the group. + + + +If you don't know what you can contribute, you can search TODO or FIXME in the source code. diff --git a/docs/docs/docs/introduce/faq.en-US.md b/docs/docs/docs/introduce/faq.en-US.md new file mode 100644 index 000000000000..67ddc5e83423 --- /dev/null +++ b/docs/docs/docs/introduce/faq.en-US.md @@ -0,0 +1,207 @@ +--- +order: 5 +toc: content +translated_at: '2024-03-17T09:54:04.320Z' +--- + +# FAQ + +## Can I disable dynamicImport? + +Yes, but it is not recommended to do so. + +1. Install dependencies + +```bash + pnpm i babel-plugin-dynamic-import-node -D +``` + +2. Add `extraBabelPlugins` to the configuration, but only enable it for production + +```ts +// .umirc.ts +export default { + extraBabelPlugins: process.env.NODE_ENV === 'production' + ? ['babel-plugin-dynamic-import-node'] + : [] +} +``` + +## How to configure loading for dynamicImport when it's not present? + +Define `src/loading.tsx`: + +See [Directory Structure > loading.tsx](../guides/directory-structure#loadingtsxjsx) + +## Can I use React 17? + +Since Umi v4 has upgraded the default version of React to v18, be aware of compatibility between dependency libraries and React 18 when using Umi4. If you still need to use React 17, run the following commands and restart. + +```bash + pnpm add react@^17 react-dom@^17 +``` + +## Constantly restarting and refreshing the page after proxying static resources locally + + + +Solution: Configure `SOCKET_SERVER=127.0.0.1:${port}` to start the project + +```bash + SOCKET_SERVER=http://127.0.0.1:8000 pnpm dev +``` + +## Error evaluating function `round`: argument must be a number + + + +Solution: In the new version of less, `/` is recognized as property shorthand by default. To restore the old behavior (where `/` was used as a calculation symbol by default), configure `lessLoader: { math: 'always' }`. + +## The layout configuration option in routes does not take effect + +The layout configuration has been moved to `app.ts`, see [Runtime Config > layout](https://umijs.org/docs/api/runtime-config#layout) + +## Where did document.ejs go and how do I customize the HTML template + +In addition to the injection through the configuration of external [script](https://umijs.org/docs/api/config#scripts), [css](https://umijs.org/docs/api/config#styles), you can also use the project-level plugin to more flexibly modify HTML products, see: [issuecomment-1151088426](https://github.com/umijs/umi-next/issues/868#issuecomment-1151088426) + +## Why are external js files configured in scripts by default inserted after umi.js + +React only starts running after the page has fully loaded, so inserting after `umi.js` will not affect the project. + +If you need to insert it before `umi.js`, see [issuecomment-1176960539](https://github.com/umijs/umi/issues/8442#issuecomment-1176960539) + +## How do I code split with Umi4? + +Umi 4 splits code by page by default. If you feel the need for further optimization, you can use sub-package strategies or manual splitting, see: [Code Splitting Guide](../../blog/code-splitting) + +If you have the requirement to pack all js products into a single `umi.js` file, please disable [dynamicImport](#can-i-disable-dynamicimport). + +## Where did _layout.tsx go and how do I nest routes? + +Umi 4 uses react-router v6, and nested route content is displayed through ``. See: [issuecomment-1206194329](https://github.com/umijs/umi/issues/8850#issuecomment-1206194329) + +## How to use GraphQL + +For configuring `graph-ql` loader, see: [discussions/8218](https://github.com/umijs/umi/discussions/8218) + +## How to use WebAssembly + +Configure as follows: + +```ts +// .umirc.ts + +export default { + chainWebpack(config) { + config.set('experiments', { + ...config.get('experiments'), + asyncWebAssembly: true + }) + + const REG = /\.wasm$/ + + config.module.rule('asset').exclude.add(REG).end(); + + config.module + .rule('wasm') + .test(REG) + .exclude.add(/node_modules/) + .end() + .type('webassembly/async') + .end() + }, +} +``` + +See an actual example: [discussions/8541](https://github.com/umijs/umi/discussions/8541) + +## How to customize loaders + +Depending on the scenario, you may need to exclude the file type you need to load from the static asset rules first and then add your own loader or modify it. Refer to the following examples: + + - [discussions/8218](https://github.com/umijs/umi/discussions/8218) + + - [discussions/8452](https://github.com/umijs/umi/discussions/8452) + +## How to use css modules in third-party packages + +1. Directly publish the source code of third-party packages' `jsx` / `ts` / `tsx` to npm, no need to translate to `js`. Umi 4 supports direct use. + +2. If the third-party package output is `js`, you need to include it for additional processing by babel to support css modules: + +```ts +// .umirc.ts +export default { + extraBabelIncludes: ['your-pkg-name'] +} +``` + +## How to solve the lack of hot updates in npm linked packages + +Umi 4 has `mfsu` enabled by default and ignores changes in `node_modules` by default. Exclude the package from `mfsu` as follows: + +```ts +// .umirc.ts + +export default { + mfsu: { + exclude: ['package-name'] + }, +} +``` + +## What is the priority order of config files for different environments? + +For the detailed loading priority, see [UMI_ENV](../../docs/guides/env-variables#umi_env), and the same applies to `config/config.ts` or `.umirc.ts`. + +## Problems with IE compatibility + +Umi 4 is not IE compatible by default in the context of modern browsers. + +If you need to adjust the build for compatibility targets, for older browsers, or for IE, please refer to [Legacy Browser Compatibility](../../blog/legacy-browser). + +## SSR Issues + +SSR is still an experimental feature and not recommended for production environments. If any issues are encountered, they should be reported on [issues](https://github.com/umijs/umi/issues). + +## Vue / Vite Issues + +Umi 4 now supports Vite mode and Vue. There may be edge cases, so report any issues on [issue](https://github.com/umijs/umi/issues). + +## Why does the pathname obtained from `history` differ from the one obtained from `useLocation`? + +This situation occurs when the project is configured with `base`. `history.location.pathname` reflects the browser's pathname, which includes the `base`. Route-related hooks, however, return the **frontend route** definition's pathname, which does not include the `base`. [Reference](../guides/routes#location-information). + +## How to change the compression encoding format of the output + +By default, the js/css compressor `esbuild` uses `ascii` encoding for compression, which may lead to the encoding of Chinese characters and increase the size of the output. + +Adjust the encoding to `utf8` to prevent character conversion: + +```ts +// .umirc.ts +export default { + jsMinifierOptions: { charset: 'utf8' }, + cssMinifierOptions: { charset: 'utf8' } +} +``` + +Or switch the compressor to resolve this: + +```ts +// .umirc.ts +export default { + jsMinifier: 'terser', + cssMinifier: 'cssnano' +} +``` + +## How to configure devServer options + +Umi 4 no longer supports configuring `devServer`, but you can find alternatives as follows: + +1. Use the [`proxy`](../api/config#proxy) option to set up a proxy, and modify request header information through `onProxyReq`, see [#10760](https://github.com/umijs/umi/issues/10760#issuecomment-1471158059). + +2. Write a [project-level plugin](../guides/use-plugins#project-level-plugins) to insert an express middleware to modify requests, see [#10060](https://github.com/umijs/umi/issues/10060#issuecomment-1471519707). + diff --git a/docs/docs/docs/introduce/introduce.en-US.md b/docs/docs/docs/introduce/introduce.en-US.md new file mode 100644 index 000000000000..37caeeb5b581 --- /dev/null +++ b/docs/docs/docs/introduce/introduce.en-US.md @@ -0,0 +1,57 @@ +--- +order: 1 +toc: content +translated_at: '2024-03-17T09:52:32.853Z' +--- + +# Introduction to Umi + +
+ + +## What is Umi? + +Umi, pronounced as "Wu Mi" in Chinese, is an extensible enterprise-level front-end application framework. Umi is based on routing, supporting both configuration-based routing and convention-based routing, ensuring the completeness of routing features and further extending functionalities based on this. Accompanied by a plugin system with a complete lifecycle, it covers every lifecycle from the source code to the build product, supporting various functional extensions and business needs. + +Umi is the underlying front-end framework of Ant Group, serving directly or indirectly over 10,000 applications, including Java, Node, H5 Wireless, Hybrid applications, pure front-end assets applications, CMS applications, Electron applications, Serverless applications, and more. It has served our internal users well and many external users, including Taobao system, Feizhu, Alibaba Cloud, ByteDance, Tencent, Koubei, Meituan, etc. In ByteDance's [research report](https://zhuanlan.zhihu.com/p/403206195) in 2021, Umi was the choice of 25.33% of developers. + +Umi has many interesting features, such as: + +1. **Enterprise-level**, considering more in terms of security, stability, best practices, and constraint ability
+2. **Plugin-based**, everything can be modified, and Umi itself is also composed of plugins
+3. **MFSU**, a Webpack packaging solution faster than Vite
+4. Complete routing based on React Router 6
+5. The fastest request by default
+6. SSR & SSG
+7. Stable white-box performance with ESLint and Jest
+8. Framework-level integration of React 18
+9. Best practices for Monorepo
+... + +## When Not to Use Umi? + +If your project, + +1. Needs to support IE 8 or lower browsers
+2. Needs to support React versions below 16.8.0
+3. Needs to run in environments below Node 14
+4. Has strong webpack customization needs and subjective willingness
+5. Needs to choose different routing solutions
+... + +Umi might not be suitable for you. + + +## Why Not? + +### create-react-app + +create-react-app is a scaffolding tool and not the same type as meta frameworks like Umi, next.js, remix, ice, modern.js, etc. Scaffolding can quickly start a project, which is sufficient for individual projects, but not enough for teams. Because using scaffolding is like spilt water; once it starts, it cannot iterate. Meanwhile, the encapsulation and abstraction that scaffolding can do are very limited. + +### next.js + +If SSR is needed, next.js is a very good choice (of course, Umi also supports SSR); but if only CSR is needed, Umi would be a better choice. By comparison, Umi has better extensibility; and Umi offers many more down-to-earth features, such as configuration-based routing, patch scheme, integration of antd, micro frontends, internationalization, permissions, etc.; at the same time, Umi is more stable, because it locks all dependencies that can be locked and proactively updates regularly. A certain subversion of Umi will not fail to run due to reinstallation of dependencies. + +### remix + +Remix is a framework I really like, and Umi 4 has copied (learned) a lot from it. But Remix is a Server framework, its built-in loaders and actions run on the server side, so it requires certain deployment environments. Umi applies loaders, actions, and the request mechanism of Remix to both the client and server side, not only making server requests fast but also achieving theoretically fastest values for pure CSR projects. At the same time, Remix is based on esbuild for packaging, which may not be suitable for projects with compatibility requirements or especially large dependencies. diff --git a/docs/docs/docs/introduce/philosophy.en-US.md b/docs/docs/docs/introduce/philosophy.en-US.md new file mode 100644 index 000000000000..fd2f4801fb83 --- /dev/null +++ b/docs/docs/docs/introduce/philosophy.en-US.md @@ -0,0 +1,66 @@ +--- +order: 2 +toc: content +translated_at: '2024-03-17T09:51:30.781Z' +--- + +# Design Philosophy + +Umi has gone from 1 to 4, experimenting with many things, both successes and failures. We have gradually accumulated these successful experiences, guiding us on how to build a robust enterprise-level framework. + +The design philosophy includes: + +1. Technological Convergence +2. Plugins and Plugin Collections +3. Best Practices +4. Enterprise-Level +5. import all from umi +6. Compile-Time Framework +7. Dependency Prepacking +8. Fast by Default +9. Constraints and Openness + +Below, we introduce the first five points, while the last four points (6-9) had a detailed share at the SEE Conf on January 8, 2022, see [SEE Conf: Umi 4 Design Philosophy Manuscript](https://mp.weixin.qq.com/s?__biz=MjM5NDgyODI4MQ%3D%3D&mid=2247484533&idx=1&sn=9b15a67b88ebc95476fce1798eb49146). + +## Technological Convergence + +
+ +
+ +Technological convergence is particularly important for teams, encompassing two meanings: 1) convergence of technology stacks and 2) convergence of dependencies. Convergence of technology stacks refers to the plethora of technology stacks available in the community, with numerous choices for each technical direction, such as data flows which alone could number over 100, and how developers should make their choice; After converging the technology stack, it's also necessary to converge dependencies, as the projects of developers within a team should not have many fragmented dependencies, each of which comes with an upgrading cost. + +We hope that once developers depend on Umi, they no longer need to worry about dependencies like babel, webpack, postcss, react, react-router, etc., and by depending on @umijs/max, they need not be concerned with dependencies and technology stacks of developing mid-end projects. + +## Plugins and Plugin Collections + +
+ +
+ +Umi satisfies different scenarios and business needs through the mechanism of plugins and plugin collections. A plugin is to extend a function, whereas a plugin collection is to extend a category of business. For example, to support vue, we could have `@umijs/preset-vue`, including build and runtime related to vue; to support the H5 application type, there could be `@umijs/preset-h5`, aggregating H5 related functionalities together. + +As an analogy, plugin collections are similar to babel's presets and eslint's configs. + +## Best Practices + +Best practice is what we believe to be the best way to do something at the moment. The subject is us, so it could be relatively subjective. For instance, in aspects like routing, patch solutions, data flow, requests, permissions, internationalization, micro-frontends, icons usage, editor usage, charts, forms, etc., Umi will offer our best practices. Most of these practices come from internal practices and discussions at Ant Group, with some from the community. They are subjective and time-sensitive, so there might be relatively frequent iterations. + +The need for best practices arises, 1) because there are too many solutions in the community to choose from, and 2) many people lack the energy, experience, or even willingness to make choices. Especially for non-professional front-end developers, having choices is better than having none, regardless of what those choices might be. + +## Enterprise-Level + +The npm community is "deteriorating" with political, malicious, advertisement, and job-seeking packages frequently appearing. Thus, how to ensure your project won’t “crash overnight” is an unavoidable issue for frameworks providing services to enterprises. + +Umi achieves this by locking down versions, prepacking dependencies, hacking eslint to lock eslint's dependencies, and using configuration to lock down babel patch dependencies, so that Umi won't crash after you reinstall node_modules, aiming to be "Still Usable in Ten Years". + +## import all from umi + +Many may be hearing about this for the first time. import all from umi means all imports come from `umi`. For instance, dva is not `import { connect } from 'dva'`, but `import { connect } from 'umi'`, exported from Umi. The methods exported not only come from Umi itself but also from Umi plugins. + +```ts +// A large number of plugins provide additional exports for umi +import { connect, useModel, useIntl, useRequest, MicroApp, ... } from 'umi'; +``` + +The advantage is that Umi manages a large number of dependencies so users don’t need to manually install them; at the same time, developers will also have fewer import statements in their code. diff --git a/docs/docs/docs/introduce/upgrade-to-umi-4.en-US.md b/docs/docs/docs/introduce/upgrade-to-umi-4.en-US.md new file mode 100644 index 000000000000..21a6c645535d --- /dev/null +++ b/docs/docs/docs/introduce/upgrade-to-umi-4.en-US.md @@ -0,0 +1,305 @@ +--- +order: 4 +toc: content +translated_at: '2024-03-17T09:50:41.558Z' +--- + +# Upgrade to Umi 4 + +## Upgrade Steps + +Upgrading to Umi 4 can be completed in just a few easy steps, which can simply be described as - "Reinstall dependencies, modify configuration": + +1. **Dependency Handling** +2. **Start Command** +3. **Upgrading Non-Official Plugins** +4. **Configuration Layer Migration** +5. **Code Layer Modifications** + +### Dependency Handling + +Your project's `package.json` needs to upgrade Umi and replace the corresponding Umi plugins. + +If `umi@3` was developed using a combination of `umi` + `@umijs/preset-react`, then you can directly upgrade using the new `max`. + +```diff +{ + "devDependencies": { ++ "@umijs/max": "^4.0.0", +- "umi": "^3.0.0", +- "@umijs/preset-react": "^1.2.2" + } +} +``` + +Delete `node_module`, then execute `npm install` to reinstall dependencies. + +### Start Command + +If using `@umijs/max`, you can replace `umi` with the `max` command, such as `max dev`, `max build`, etc. + +`umi@4` moved some pre-project operations to the `setup` command, such as `umi g tmp` commands in umi@3, which need to be replaced with `umi setup` + +`package.json` + +```diff +{ + "scripts": { +- "build": "umi build", ++ "build": "max build", +- "postinstall": "umi g tmp", ++ "postinstall": "max setup", +- "start": "umi dev", ++ "start": "max dev", + } +} +``` + +### Upgrading Non-Official Plugins + +For some non-Umi official Umi plugins used in the project, please contact the relevant authors to update them in a timely manner according to [plugin api changes](../api/plugin-api). + +When migrating the project, you can first turn off references to the corresponding plugin packages, such as temporarily commenting out the `plugins` in the configuration, and removing all dependencies starting with `umi-plugin-`, `@umijs/plugin-`, and `@umijs/preset-` in package.json. + +### Configuration Layer Migration + +**Configurations provided by max** are as follows in `config/config.ts`: + +> It’s worth noting that some rules that were conventionally enabled by plugins need to be explicitly configured in `umi@4`, as we want less 'black box' behavior in `umi@4`. + +```typescript +import { defineConfig, utils } from 'umi'; + +export default defineConfig({ + model: {}, + antd: {}, + request: {}, + initialState: {}, + mock: { + include: ['src/pages/**/_mock.ts'], + }, + dva: {}, + layout: { + // https://umijs.org/docs/max/layout-menu#configuration-at-build-time + title: 'UmiJS', + locale: true, + }, + // https://umijs.org/zh-CN/plugins/plugin-locale + locale: { + // default zh-CN + default: 'zh-CN', + antd: true, + // default true, when it is true, will use `navigator.language` overwrite default + baseNavigator: true, + }, +}); +``` + +**Configurations with differences** are as follows in `config/config.ts`: + +```typescript +import { defineConfig, utils } from 'umi'; + +export default defineConfig({ +- fastRefresh: {}, ++ fastRefresh: true, + dva: { + // The parameter hmr is no longer supported +- hmr: true, + }, +// Default webpack5 +- webpack5: {}, +}) +``` + +### Code Layer Modifications + +Umi 4 upgrades `react-router@5` to `react-router@6`, so there are some differences in the use of related api. + +Props are empty objects by default, the following properties cannot be taken directly from props ![image](https://img.alicdn.com/imgextra/i4/O1CN01H9ScQv21ymaLkwZ8p_!!6000000007054-2-tps-1210-374.png) + +#### children + +```typescript +import { Outlet } from 'umi'; +; +``` + +Mainly needs modifications in the global layout + +Such as `layouts/index.tsx`: + +```diff +import React from 'react'; ++ import { Outlet } from 'umi'; + +export default function Layout(props) { + return ( +
+- { props.children } ++ +
+ ); +} +``` + +Change the way that route components rendered with `React.cloneElement`, for example: + +```diff +import React from 'react'; ++ import { Outlet } from 'umi'; + +export default function RouteComponent(props) { + return ( +
+- { React.cloneElement(props.children, { someProp: 'p1' }) } ++ +
+ ); +} +``` + +Change components to get values from `useOutletContext` + +```diff +import React from 'react'; ++ import { useOutletContext } from 'umi'; + +- export function Comp(props){ ++ export function Comp() { ++ const props = useOutletContext(); + + return props.someProp; +} +``` + +#### history + +```diff ++ import { history } from 'umi'; +export default function Page(props) { + return ( +
{ +- props.history.push('list'); ++ history.push('list'); + }}> +
+ ); +} +``` + +#### location + +> It’s recommended to use useLocation in components or hooks, and window.location elsewhere. + +```diff +export default function Page(props) { ++ const { location } = window; + return ( +
+- { props.location } ++ { location } +
+ ); +} +``` + +Or + +```diff ++ import { useLocation } from 'umi'; +export default function Page(props) { ++ let location = useLocation(); + return ( +
+- { props.location } ++ { location } +
+ ); +} +``` + +#### match + +```diff ++ import { useMatch } from 'umi'; +export default function Page(props) { ++ const match = useMatch({ path: 'list/search/:type' }); + return ( +
+- { props.match } ++ { match } +
+ ); +} +``` + +In the use of class component components: + +```diff +import { matchPath } from 'umi'; +class Page extends Component { ++ match = matchPath({ path: 'list/search/:type' }, window.location.pathname); + state = {} + render() { + return ( +
+- {this.props.match.type} ++ {this.match.type} +
+ ) + } +} +``` +For more `Umi` related [api](https://umijs.org/docs/api/api) + +Pay attention to the difference in match data: + +``` +// match v5 +isExact: true +params: {} +path: "/users/abc" +url: "/users/abc" + +// match v6 +params:{ } +pathname: "/list/search/articles" +pathnameBase: "/list/search/articles" +pattern: {path: 'list/search/:type'} +``` + +For more changes and API updates, please refer to [react-router@6](https://reactrouter.com/docs/en/v6/api#uselocation) + +After completing the above operations, execute `max dev`, and visit [http://localhost:8000](http://localhost:8000), please verify all functions meet expectations. + +If your project cannot start normally, you may need to do the following: + +## Configuration Changes + +TODO + +## FAQ + +### Can't find query in location? + +Query in location is no longer supported, later it’s recommended to use [search](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) + +```diff +- const { query } = history.location; ++ import { parse } from 'query-string'; ++ const query = parse(history.location.search); +``` + +### Can't find *.d file, or its reference + +In `umi@3`, importing would automatically find the corresponding `.d.ts` file, such as: + +`import { ButtonType } from './button';` + +If there is a `.button.d.ts` file, it will correctly execute in `umi@3`, but it will cause an error in umi@4, you may need more standardized type references. + +```diff +- import { ButtonType } from './button'; ++ import type { ButtonType } from './button.d'; +``` diff --git a/docs/docs/docs/max/access.en-US.md b/docs/docs/docs/max/access.en-US.md new file mode 100644 index 000000000000..3b5a950219e1 --- /dev/null +++ b/docs/docs/docs/max/access.en-US.md @@ -0,0 +1,168 @@ +--- +order: 7 +toc: content +translated_at: '2024-03-17T09:49:43.250Z' +--- + +# Permissions + +## How to Enable + +Configuring activation. Requires `src/access.ts` to provide permission configuration. + +```ts +export default { + access: {}, + // access plugin depends on the initial State so it needs to be enabled at the same time + initialState: {}, +}; +``` + +## Introduction + +We have agreed that `src/access.ts` is our permission definition file, which needs to export a function by default. The method will be executed when the project is initialized. This method needs to return an object, where each value of the object corresponds to a defined permission. As shown below: + +```js +// src/access.ts +export default function (initialState) { + const { userId, role } = initialState; + + return { + canReadFoo: true, + canUpdateFoo: role === 'admin', + canDeleteFoo: (foo) => { + return foo.ownerId === userId; + }, + }; +} +``` + +Where `initialState` is the data provided by the initialization state plugin `initial-state`, you can use this data to initialize your user permissions. + +## Configuration + +### Extended Routing Configuration + +With the [layout](./layout-menu) plugin, you can easily implement permission control for some pages. As shown below, users can access the page only if they have the canReadPageA permission (defined in `src/access.ts`). Otherwise, it will render the default permission error page built into the Layout plugin by default. + +```ts +export const routes = [ + { + path: '/pageA', + component: 'PageA', + access: 'canReadPageA', // Some key of the permission definition return value + }, +]; +``` + +### Custom Permission Page Configuration + +As mentioned above, to configure a custom permission page, you need to define it in `src/app.tsx`. + +```tsx +export const layout: RunTimeLayoutConfig = () => { + return { + // Customize the 403 page + unAccessible:
'unAccessible'
, + // Customize the 404 page + noFound:
'noFound'
, + }; +}; +``` + +#### access + +- Type: `string` + +The corresponding permission name. + +## API + +### useAccess + +We provide a hook for obtaining permission-related information in components, as shown below: + +```js +import React from 'react'; +import { useAccess } from 'umi'; + +const PageA = (props) => { + const { foo } = props; + the access = useAccess(); + + if (access.canReadFoo) { + // If can read Foo, then... + } + + return <>TODO; +}; + +export default PageA; +``` + +Combined with the `Access` component, it's easy to control the permissions of elements within pages. + +### Access + +You can use the `useAccess` hook and the `` component provided by the plugin to control permissions in your application. The `Access` component supports the following properties: + +#### accessible + +- Type: `boolean` + +Whether permission is granted, usually obtained through `useAccess` and passed in. + +#### fallback + +- Type: `React.ReactNode` + +The display when there is no permission, by default no content is displayed without permission. + +### children + +- Type: `React.ReactNode` + +What is displayed when permission is granted. + +A complete example is as follows: + +```js +import React from 'react'; +import { useAccess, Access } from 'umi'; + +const PageA = (props) => { + const { foo } = props; + const access = useAccess(); // members of access: canReadFoo, canUpdateFoo, canDeleteFoo + + if (access.canReadFoo) { + // If can read Foo, then... + } + + return ( +
+ Can not read foo content.
} + > + Foo content. +
+ Can not update foo.} + > + Update foo. + + Can not delete foo.} + > + Delete foo. + + + ); +}; +``` + +- The return value `access` of `useAccess()` is the set of permissions defined in `src/access.ts`, which can be used to control the flow of code execution within components. + +- The `` component has two properties: `accessible` and `fallback`. When `accessible` is `true`, it will render the child component; when `accessible` is `false`, it will render the `ReactNode` corresponding to the `fallback` property. diff --git a/docs/docs/docs/max/analytics.en-US.md b/docs/docs/docs/max/analytics.en-US.md new file mode 100644 index 000000000000..748c02bccbc7 --- /dev/null +++ b/docs/docs/docs/max/analytics.en-US.md @@ -0,0 +1,31 @@ +--- +order: 14 +toc: content +translated_at: '2024-03-17T09:48:57.403Z' +--- + +# Site Statistics + +`@umijs/max` has built-in site statistics functionality, currently supporting [Google Analytics](https://analytics.google.com/analytics/web/) and [Baidu Analytics](https://tongji.baidu.com/web/welcome/login) + +## How to Enable + +Configure it to be enabled and enter the respective analytics service key as required. + +Example: + +```ts +{ + analytics: { + ga_v2: 'G-abcdefg', // google analytics key (GA 4) + baidu: 'baidu_tongji_key', + + // If you are using the old version GA v1, please use `ga` to configure + ga: 'ga_old_key' + } +} +``` + +### Environment Variables + +The key for [Google Analytics 4](https://support.google.com/analytics/answer/10089681) can also be configured through the environment variable `GA_V2_KEY`, and the old version uses `GA_KEY`. diff --git a/docs/docs/docs/max/antd.en-US.md b/docs/docs/docs/max/antd.en-US.md new file mode 100644 index 000000000000..f05848c4c9a1 --- /dev/null +++ b/docs/docs/docs/max/antd.en-US.md @@ -0,0 +1,232 @@ +--- +order: 3 +toc: content +translated_at: '2024-03-17T09:48:51.045Z' +--- + +# antd + +Integration of the antd component library. + +## How to Enable + +Configure to enable, example: + +```ts +// config/config.ts +export default { + antd: { + // configProvider + configProvider: {}, + // themes + dark: true, + compact: true, + // babel-plugin-import + import: true, + // less or css, default less + style: 'less', + // shortcut of `configProvider.theme` + // used to configure theme token, antd v5 only + theme: {}, + // antd valid for version 5.1.0 or higher, default: undefined + appConfig: {}, + // Transform DayJS to MomentJS + momentPicker: true, + // Add StyleProvider for legacy browsers + styleProvider: { + hashPriority: 'high', + legacyTransformer: true, + }, + }, +}; +``` + +## Introduction + +Includes the following features: + +1. Built-in [antd](https://ant.design/), the current built-in version is `^4.0.0` +2. On-demand compilation based on [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) +3. When using antd@4, you can switch to the dark theme with one click, see the image below + +![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*mYU9R4YFxscAAAAAAAAAAABkARQnAQ) + +## Configuration + +### Build-time Configuration + +Note: Build-time configuration will go through JSON conversion, so here you can only set configurations that conform to the JSON format. For function configurations like `algorithm`, set them at [Runtime Configuration](#runtime-configuration). + +#### dark + +Enables dark theme. + +- Type: `boolean` +- Default: `false` + +#### compact + +Enables compact theme. + +- Type: `boolean` +- Default: `false` + +For example: + +```ts +export default { + antd: { + dark: true, + compact: true, + }, +}; +``` + +Dark theme activation is only supported with antd version 4. Compact theme is supported with `antd@>4.1.0`. + +#### import + +- Type: `boolean` + +Configure `antd`'s `babel-plugin-import` for on-demand loading. + +#### style + +- Type: `"less" | "css"` +- Default: `less` + +Configure the use of `antd` styles, default is `less`. + +#### configProvider + +- Type: `object` + +Configure `antd`'s `configProvider`. + +#### theme + +- Type: `object` + +Configure theme token for `antd@5`, equivalent to setting `configProvider.theme`, and this configuration item has higher priority. + +**Note: This configuration is only available for antd v5** + +#### appConfig + +- Type: `object` + +Configure the App wrapper component for `antd`, please note that for `antd@5.1.0 ~ 5.2.3`, it can only be enabled through `appConfig: {}`, and only `antd >=5.3.0` supports more App configuration projects. + +**Note: This configuration is only available for antd v5.1.0 and above** + +#### momentPicker + +- Type: `boolean` + +Configure whether `antd`'s `DatePicker`, `TimePicker`, `Calendar` components use `moment` as the date processing library, default is `false`. + +**Note: This configuration is only available for antd v5 and above** + +#### styleProvider + +- Type: `object` + +Configure `antd`'s `StyleProvider` component, which is used to support legacy browsers, such as IE11. When your project is configured with `legacy` or `targets` that include `ie`, it will automatically be downgraded, no manual configuration required. + +**Note:** + +1. This configuration is only available for antd v5 and above. + +2. The downgrade of CSS relies on [`@ant-design/cssinjs`](https://ant.design/docs/react/compatible-style-cn). If you have explicitly installed `antd`, please also install and ensure that your `@ant-design/cssinjs` version corresponds correctly with `antd`. + +### Runtime Configuration + +In the runtime configuration of `app.ts(x)`, you can modify the values of antd's `ConfigProvider`. Before using this feature, **make sure you have enabled the `antd.configProvider` option**, otherwise changes to `ConfigProvider` will not take effect: + +```ts +// .umirc.ts + + antd: { + configProvider: {} + } +``` + +For example, to configure antd 5's theme preset algorithm and the maximum number of `message` notifications: + +```ts +// app.ts +import { RuntimeAntdConfig } from 'umi'; +import { theme } from 'antd'; + +export const antd: RuntimeAntdConfig = (memo) => { + memo.theme ??= {}; + memo.theme.algorithm = theme.darkAlgorithm; // Configure the preset dark algorithm for antd5 + + memo.appConfig = { + message: { + // Configure the maximum number of messages displayed, when exceeding the limit, the earliest messages will be automatically closed + maxCount: 3, + } + } + + return memo; +}; +``` + +### Dynamically Switch Global Configuration + +**Note: This feature is only available for antd v5** + +Use `useAntdConfig` / `useAntdConfigSetter` methods to dynamically get and modify antd's `ConfigProvider` configuration, which can typically be used to dynamically modify themes. + +Note: This feature depends on `ConfigProvider`, please also enable `configProvider: {}`. + +```tsx +import { Layout, Space, Button, version, theme, MappingAlgorithm } from 'antd'; +import { useAntdConfig, useAntdConfigSetter } from 'umi'; +const { darkAlgorithm, defaultAlgorithm } = theme; + +export default function Page() { + const setAntdConfig = useAntdConfigSetter(); + const antdConfig = useAntdConfig(); + return ( + +

with antd@{version}

+ + isDarkTheme + { + // This configuration will deep merge with the original configuration + setAntdConfig({ + theme: { + algorithm: [ + data ? darkAlgorithm : defaultAlgorithm, + ], + }, + }); + // or + setAntdConfig((config) => { + const algorithm = config.theme!.algorithm as MappingAlgorithm[]; + if (algorithm.includes(darkAlgorithm)) { + config.theme!.algorithm = [defaultAlgorithm] + } else { + config.theme!.algorithm = [darkAlgorithm]; + } + return config; + }); + }} + > + +
+ ); +} +``` + +Using `setAntdConfig`, you can dynamically modify any property supported by [antd@5 ConfigProvider](https://ant.design/components/config-provider-cn). + +## FAQ + +### How to use other versions of antd? + +Install the version of antd you need in your project. diff --git a/docs/docs/docs/max/charts.en-US.md b/docs/docs/docs/max/charts.en-US.md new file mode 100644 index 000000000000..0a19465bf39b --- /dev/null +++ b/docs/docs/docs/max/charts.en-US.md @@ -0,0 +1,535 @@ +--- +order: 4 +toc: content +translated_at: '2024-03-17T09:39:12.613Z' +--- + +# Charts + +The Umi team recommends using [Ant Design Charts](https://charts.ant.design/) or [Pro Components](https://procomponents.ant.design/) in line with Ant Design to add visualization charts 📈 to your project. + +This tutorial will provide you with some common use cases. + +## Ant Design Charts + +Ant Design Charts is a React implementation of the [AntV](https://antv.vision/en) project, developed by the data visualization team of Ant Group. + +You can install the complete Ant Design Charts package: + +```bash +pnpm install @ant-design/charts +``` + +You can also just include the sub-packages you use, for example: + +```bash +# Install statistical chart package +pnpm install @ant-design/plots +``` + +In the example usage below, we will keep imports to a minimum. + +You can also directly read the complete [Getting Started documentation](https://charts.ant.design/en/docs/manual/getting-started) and [Chart Examples](https://charts.ant.design/en/examples/gallery) of Ant Design Charts. + +### Line Chart + +Now, we need to create a line chart to display [these data](https://gw.alipayobjects.com/os/bmw-prod/1d565782-dde4-4bb6-8946-ea6a38ccf184.json). + +First, include the statistical chart package: + +```bash +pnpm install @ant-design/plots +``` + +Write code to fetch the data (omitted below): + +```tsx +import { useState, useEffect } from 'react'; + +const DemoLine = () => { + const [data, setData] = useState([]); + + useEffect(() => { + asyncFetch(); + }, []); + + const asyncFetch = () => { + fetch('https://gw.alipayobjects.com/os/bmw-prod/1d565782-dde4-4bb6-8946-ea6a38ccf184.json') + .then((response) => response.json()) + .then((json) => setData(json)) + .catch((error) => { + console.log('fetch data failed', error); + }); + }; +}; +``` + +Thus, we have obtained the data and saved the JSON object's content to `data`. Each data object is like: + +```json +{ + "Date": "2010-01", + "scales": 1998, +} +``` + +Display the data on the line chart: + +```tsx +import React from 'react'; +import { Line } from '@ant-design/plots'; + +const DemoLine: React.FC = () => { + // fetch data + + const config = { + data, + padding: 'auto', + xField: 'Date', + yField: 'scales', + xAxis: { + // type: 'timeCat', + tickCount: 5, + }, + smooth: true, + }; + + return ; +}; +``` + +In this, the `Date` attribute in `data` will serve as the X-axis horizontal axis of the line chart, and the `scales` attribute will serve as the Y-axis vertical axis for drawing. + +The complete line chart code and effect can be viewed on [this page](https://charts.ant.design/en/examples/line/basic#spline). + +### Column Chart + +Now, we need to display the page load time through a column chart. + +First, include the statistical chart package: + +```bash +pnpm install @ant-design/plots +``` + +Suppose we have the following `data`: + +```ts +const data = [ + { + type: '0-1 seconds', + value: 0.55, + }, + { + type: '1-3 seconds', + value: 0.21, + }, + { + type: '3-5 seconds', + value: 0.13, + }, + { + type: '5+ seconds', + value: 0.11, + }, +]; +``` + +Especially, for the case of `5+ seconds`, we want to mark it with a bright color. Then we can write the column chart code as follows: + +```tsx +import React from 'react'; +import { Column } from '@ant-design/plots'; + +const DemoColumn: React.FC = () => { + // fetch data + + const paletteSemanticRed = '#F4664A'; + const brandColor = '#5B8FF9'; + const config = { + data, + xField: 'type', + yField: 'value', + seriesField: '', + color: ({ type }) => { + if (type === '5+ seconds') { + return paletteSemanticRed; + } + + return brandColor; + }, + legend: false, + xAxis: { + label: { + autoHide: true, + autoRotate: false, + }, + }, + }; + + return ; +}; +``` + +The complete column chart code and effect can be viewed on [this page](https://charts.ant.design/en/examples/column/basic#color). + +### Word Cloud + +Now, we need to display the names of some countries in the world in a word cloud manner. The more populous the country, the larger the font size of its name in the word cloud. + +First, include the statistical chart package: + +```bash +pnpm install @ant-design/plots +``` + +Get the [`data`](https://gw.alipayobjects.com/os/antfincdn/jPKbal7r9r/mock.json) containing country population figures. It looks like: + +```json +{ + "country": "China", + "value": 1383220000, + "category": "asia", +} +``` + +Render the data to get the word cloud: + +```tsx +import React from 'react'; +import { WordCloud } from '@ant-design/plots'; + +const DemoWordCloud: React.FC = () => { + // fetch data + + const config = { + data, + wordField: 'country', + weightField: 'value', + color: '#122c6a', + interactions: [ + { + type: 'element-active', + }, + ], + state: { + active: { + style: { + lineWidth: 2, + }, + }, + }, + }; + + return ; +}; +``` + +The complete word cloud code and effect can be viewed on [this page](https://charts.ant.design/en/examples/more-plots/word-cloud#basic). + +### Dot Map + +Now, we need to display the distribution of cities and districts in our country on the map in the form of dots. + +First, include the map package: + +```bash +pnpm install @ant-design/maps +``` + +Get the [`data`](https://gw.alipayobjects.com/os/antfincdn/g5hIthhKlr/quanguoshixianweizhi.json) containing all district data. It looks like: + +```json +{ + "list": [ + { + "lnglat": [ + 116.258446, + 37.686622 + ], + "name": "Jing County", + "style": 2 + }, + // ... + ] +} +``` + +Where, `style` of `0` stands for municipal city, `1` for county-level city, and `2` for district. + +Render the data to get the dot map: + +```tsx +import React from 'react'; +import { DotMap } from '@ant-design/maps'; + +const DemoDotMap: React.FC = () => { + // fetch data + + const config = { + map: { + type: 'mapbox', + style: 'dark', + zoom: 3, + center: [107.4976, 32.1697], + pitch: 0, + }, + source: { + data, + parser: { + type: 'json', + coordinates: 'lnglat', + }, + }, + size: 4, + color: { + field: 'style', + value: ({ style }) => { + if (style == 0) { + return '#14B4C9'; + } else if (style == 1) { + return '#3771D9'; + } else { + return '#B8EFE2'; + } + }, + }, + legend: { + type: 'category', + position: 'bottomleft', + items: [ + { + color: '#14B4C9', + value: 'Municipal city', + }, + { + color: '#3771D9', + value: 'County-level city', + }, + { + color: '#B8EFE2', + value: 'District', + }, + ], + }, + }; + + return ; +}; +``` + +The complete dot map code and effect can be viewed on [this page](https://charts.ant.design/en/examples/map-dot/map-scatter#distribution-cities). + +## Pro Components + +Pro Components are aimed at mid-to-background applications, offering a higher degree of abstraction on top of Ant Design, providing a higher-level design specification to help developers quickly build high-quality pages. + +You should import the sub-packages you use as needed: + +```bash +# Import advanced table +pnpm install @ant-design/pro-table + +# Import advanced list +pnpm install @ant-design/pro-list +``` + +You can also directly read Pro Components' complete [Getting Started documentation](https://procomponents.ant.design/docs/getting-started), [Table Examples](https://procomponents.ant.design/components/table) and [List Examples](https://procomponents.ant.design/components/list). + +The examples below will assume you have already imported the sub-packages you will use. + +### Pro Table Advanced Table + +Now, you need to quickly build a table containing members and related information. + +Member information is as follows: + +```ts +const realNames = ['Ma Baba', 'Zhang Sanfeng', 'Fei Peng', 'Xu Changqing']; +const nickNames = ['Baba', 'Junbao', 'Jingtian', 'Mr. Xu']; +const emails = ['baba@antfin.com', 'junbao@antfin.com', 'jingtian@antfin.com', 'xvzhangmen@antfin.com']; +const phones = ['18800001234', '13900002345', '17200003456', '17800004567']; +``` + +Define a `Member` type: + +```ts +export type Member = { + id: number; + realName: string; + nickName: string; + email: string; + phone: string; +}; +``` + +Process member information to build a `Member` array: + +```ts +const memberList: Member[] = []; + +for (let i = 0; i < realNames.length; i++) { + memberList.push({ + id: `${102047 + i}`, + realName: realNames[i], + nickName: nickNames[i], + email: emails[i], + phone: phones[i], + }); +} +``` + +Pass the array to Pro Table to quickly build the table: + +```tsx +import React from 'react'; +import type { ProColumns } from '@ant-design/pro-table'; +import ProTable from '@ant-design/pro-table'; + +// resolve member info list + +const MemberList: React.FC = () => { + const columns: ProColumns[] = [ + { + dataIndex: 'realName', + title: 'Name', + }, + { + dataIndex: 'nickName', + title: 'Nickname', + }, + { + dataIndex: 'email', + title: 'Account', + }, + { + dataIndex: 'phone', + title: 'Phone number', + }, + { + title: 'Action', + dataIndex: 'x', + valueType: 'option', + render: (_, record) => { + return [
Edit, Remove]; + }, + }, + ]; + + return ( + + columns={columns} + request={(params, sorter, filter) => { + console.log(params, sorter, filter); + return Promise.resolve({ + data: memberList, + success: true, + }); + }} + rowKey="id" + pagination={{ + showQuickJumper: true, + }} + toolBarRender={false} + search={false} + /> + ); +} +``` + +The complete table code and effect can be viewed on [this page](https://procomponents.ant.design/components/table). + +### Pro List Advanced List + +Now, you need to quickly build a list containing test information. + +Test information is as follows: + +```ts +export type Test = { + id: number; + name: string; + image: string; + desc: string; +}; + +const testList: Test[] = [ + { + id: 9903, + name: 'Yuque\'s Sky', + image: + 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg', + desc: 'Covered all test cases for the login module', + }, + { + id: 9904, + name: 'Ant Design', + image: + 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg', + desc: 'Covered all test cases, all scenarios have been verified under the Node 17 testing environment', + }, + { + id: 9905, + name: 'Ant Group Experience Technology', + image: + 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg', + desc: 'Covered all testing requirements, all scenarios have been verified under the Ubuntu 14.04 testing environment', + }, + { + id: 9906, + name: 'TechUI', + image: + 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg', + desc: 'Covered all testing requirements, all scenarios have been verified under MacOS testing environment', + }, +]; +``` + +Pass the test information to Pro List to quickly build the list: + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import ProList from '@ant-design/pro-list'; + +// resolve test info list + +const MemberList: React.FC = () => { + return ( + + toolBarRender={() => { + return [ + , + ]; + }} + rowKey="id" + headerTitle="Test Results" + dataSource={testList} + showActions="hover" + showExtra="hover" + metas={{ + title: { + dataIndex: 'name', + }, + avatar: { + dataIndex: 'image', + }, + description: { + dataIndex: 'desc', + }, + actions: { + render: (text, row) => [ + Link, + Alert, + View, + ], + }, + }} + /> + ); +} +``` + +The complete list code and effect can be viewed on [this page](https://procomponents.ant.design/components/list). diff --git a/docs/docs/docs/max/data-flow.en-US.md b/docs/docs/docs/max/data-flow.en-US.md new file mode 100644 index 000000000000..a7a6e73f9b9a --- /dev/null +++ b/docs/docs/docs/max/data-flow.en-US.md @@ -0,0 +1,232 @@ +--- +order: 5 +toc: content +translated_at: '2024-03-17T09:24:49.645Z' +--- + +```markdown +# Data Stream + +`@umi/max` has a built-in **data stream management** [plugin](https://github.com/umijs/umi/blob/master/packages/plugins/src/model.ts), which is a lightweight data management solution based on the `hooks` paradigm. It allows managing global shared data in Umi projects. + +## Getting Started + +### Creating a Model + +The data stream management plugin adopts a conventional directory structure. We agree that you can include Model files in the `src/models`, `src/pages/xxxx/models/` directory, and `src/pages/xxxx/model.{js,jsx,ts,tsx}` files. +Model files can use `.(tsx|ts|jsx|js)` four suffix formats, the **namespace** generation rule is as follows. + +| Path | Namespace | Explanation | +| :--- |:--- | :--- | +| `src/models/count.ts` | `count` | `src/models` directory does not support nested directory definition model | +| `src/pages/pageA/model.ts` | `pageA.model` | | +| `src/pages/pageB/models/product.ts` | `pageB.product` | | +| `src/pages/pageB/models/fruit/apple.ts` | `pageB.fruit.apple` | `pages/xxx/models` under model definition supports nested definition | + +A Model is essentially a custom `hooks` that doesn't involve any "black magic" that users need to worry about. + +When you need to get the global data in the Model, you can call its namespace. For example, for the Model file `userModel.ts`, its namespace is `userModel`. + +Write a function with a default export: + +```ts +// src/models/userModel.ts +export default function Page() { + const user = { + username: 'umi', + }; + + return { user }; +}; +``` + +This is a Model. The plugin's job is to turn the state or data within it into **global data**, so when different components use this Model, they get the same set of state or data. + +:::info{title=💡} +The Model file needs to default export a function, which defines a `hook`. Files that do not comply with this specification will be filtered out and cannot be called by namespace. +::: + +The Model can use other `hooks`, taking a counter as an example: + +```ts +// src/models/counterModel.ts +import { useState, useCallback } from 'react'; + +export default function Page() { + const [counter, setCounter] = useState(0); + + const increment = useCallback(() => setCounter((c) => c + 1), []); + const decrement = useCallback(() => setCounter((c) => c - 1), []); + + return { counter, increment, decrement }; +}; +``` + +In project practice, we usually need to request backend interfaces to obtain the required data. Now let's extend the previous example of getting user information: + +```ts +// src/models/userModel.ts +import { useState } from 'react'; +import { getUser } from '@/services/user'; + +export default function Page() { + const [user, setUser] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + getUser().then((res) => { + setUser(res); + setLoading(false); + }); + }, []); + + return { + user, + loading, + }; +}; +``` + +If you use [ahooks](https://ahooks.js.org) in your project, you can organize your code like this: + +```ts +// src/models/userModel.ts +import { useRequest } from 'ahooks'; +import { getUser } from '@/services/user'; + +export default function Page() { + const { data: user, loading: loading } = useRequest(async () => { + const res = await getUser(); + if (res) { + return res; + } + return {}; + }); + + return { + user, + loading, + }; +}; +``` + +### Using Model + +Now, if you want to use a global Model in a specific component. Taking user information as an example, just call the `useModel` hook function: + +```tsx +// src/components/Username/index.tsx +import { useModel } from 'umi'; + +export default function Page() { + const { user, loading } = useModel('userModel'); + + return ( + {loading ? <>:
{user.username}
} + ); +} +``` + +In this case, the `useModel()` method's parameter is the Model's **namespace**. + +:::info{title=💡} +If you use VSCode as the IDE for developing Umi projects, it is recommended to use in conjunction with the [@umijs/plugin-model](https://marketplace.visualstudio.com/items?itemName=litiany4.umijs-plugin-model) plugin. It allows you to quickly jump to the file that defines the Model: + +![vscode - @umijs/plugin-model plugin demonstration](https://gw.alipayobjects.com/zos/antfincdn/WcVbbF6KG2/1577073518336-afe6f03d-f817-491a-848a-5feeb4ecd72b.gif) +::: + +## Performance Optimization + +The `useModel()` method can accept an optional second parameter. When the component only needs to use part of the Model's parameters and is not interested in the changes of other parameters, a function can be passed in for filtering. Taking the operation buttons of the counter as an example: + +```tsx +// src/components/CounterActions/index.tsx +import { useModel } from 'umi'; + +export default function Page() { + const { add, minus } = useModel('counterModel', (model) => ({ + add: model.increment, + minus: model.decrement, + })); + + return ( +
+ + +
+ ); +}; +``` + +The above component is not concerned with the `counter` value in the counter Model, only needing to use the `increment()` and `decrement()` methods provided by the Model. Thus, we passed in a function as the second parameter of the `useModel()` method, whose return value will serve as the `useModel()` method's return value. + +This way, we filter out the frequently changing `counter` value, avoiding performance loss caused by repeated rendering of the component. + +## Global Initial State + +`@umi/max` has a built-in **global initial state management** [plugin](https://github.com/umijs/umi/blob/master/packages/plugins/src/initial-state.ts), allowing you to quickly build and obtain the global initial state of Umi projects within components. + +The global initial state is a special Model. + +The global initial state is created at the very beginning of the entire Umi project. Write the `getInitialState()` export method in `src/app.ts`, whose return value will become the global initial state. For example: + +```ts +// src/app.ts +import { fetchInitialData } from '@/services/initial'; + +export async function getInitialState() { + const initialData = await fetchInitialData(); + return initialData; +} +``` + +Now, various plugins and components you define can directly obtain this global initial state through `useModel('@@initialState')`, as shown below: + +```tsx +import { useModel } from 'umi'; + +export default function Page() { + const { initialState, loading, error, refresh, setInitialState } = + useModel('@@initialState'); + return <>{initialState}; +}; +``` + +| Object Property | Type | Introduction | +| --- | --- | --- | +| `initialState` | `any` | The return value of the exported `getInitialState()` method | +| `loading` | `boolean` | Whether the `getInitialState()` or `refresh()` method is in progress. Before the initial state is first obtained, the rendering of other parts of the page will be **blocked** | +| `error` | `Error` | If the exported `getInitialState()` method throws an error, the error message | +| `refresh` | `() => void` | Re-execute the `getInitialState` method and obtain a new global initial state | +| `setInitialState` | `(state: any) => void` | Manually set the value of `initialState`, after manual setting, `loading` will be set to `false` | + +## Qiankun Parent-Child Application Communication + +`@umi/max` has a built-in **Qiankun Microfrontend** [plugin](https://github.com/umijs/umi/blob/master/packages/plugins/src/qiankun.ts), when using the data stream plugin, it allows micro-applications to obtain the parent application's data Model passed to the child application through the `useModel('@@qiankunStateFromMaster')` method, thereby achieving communication between parent and child applications. + +For specific usage methods, please refer to the section on parent-child application communication in microfrontend. + +## API + +### `useModel` + +`useModel()` is a hook function that provides the capability to use Model. It accepts two parameters: + +| Parameter | Type | Introduction | +| --- | --- | --- | +| `namespace` | `String` | The namespace of the Model file | +| `updater` | `(model: any) => any` | An optional parameter. Pass in a function, whose return value is the Model state or data currently needed in the component, and serves as the return value of the `useModel()` method. It is of significant importance for optimizing component performance. | + +```tsx +// src/components/AdminInfo/index.tsx +import { useModel } from 'umi'; + +export default function Page() { + const { user, fetchUser } = useModel('adminModel', (model) => ({ + user: model.admin, + fetchUser: model.fetchAdmin, + })); + + return <>hello; +}; +``` diff --git a/docs/docs/docs/max/dva.en-US.md b/docs/docs/docs/max/dva.en-US.md new file mode 100644 index 000000000000..1504bc32848b --- /dev/null +++ b/docs/docs/docs/max/dva.en-US.md @@ -0,0 +1,343 @@ +--- +order: 13 +toc: content +translated_at: '2024-03-17T09:17:48.666Z' +--- + +# dva + +## Why We Need State Management + +React components are essentially built through JSX and styles according to the state to form the final UI. It is the change in state that actually dynamizes the page. For simple frontend applications, the application data management needs can be met by the component's own state plus the parent component passing state via props. However, when the application scales to a certain degree, it will lead to very complex states maintained within components. Coupled with the state transfer between components, data management can easily become chaotic. A minor modification can lead to unpredictable side effects. + +Therefore, we need pure UI components that, apart from rendering logic, do not mix other logics (such as network requests). This requires us to find a way to extract business logic unrelated to rendering, forming an independent layer (which is managed by the model in the `src/models` folder in Umi) to manage. All components are downgraded to `stateless components` that solely depend on props for rendering. In this way, the UI layer no longer needs to worry about rendering unrelated logics and focuses on UI rendering. (Note: The components mentioned here mainly refer to the page components under page, whereas the components under component should be relatively universal components that should also rely solely on props for rendering, and they should not have a model. Data should be passed to them via props in the page components). + +## Simple Data Sharing + +For simple applications that do not require complex data flows, only some simple data sharing is needed. We recommend using [Middle Platform Best Practices Simple Data Flow](./data-flow). + +## How Umi Manages State + +As shown in the figure below, Umi has built in [Dva](https://dvajs.com) to provide a set of state management solutions: + +![undefined](https://gw.alipayobjects.com/zos/skylark/48f9ff5f-ab11-4896-9fb6-65cdd83340de/2018/png/dcb7073b-fc0c-4e2c-aa39-93ac249d715c.png) + +Data is uniformly managed in the models of `src/models`, components strive not to maintain data as much as possible, but to associate with the data in the model through connect. When there is an operation on the page, an action is triggered to request the backend interface and modify the data in the model, separating the business logic into a cyclic closed loop, allowing the data to flow unidirectionally within it, making the application easier to maintain. This idea originally came from Facebook's [flux](http://facebook.github.io/flux/). Let's take a closer look at how to implement such logic in Umi. + +### Configuring dva + +First, you need to configure `dva: {}` to activate the dva plugin built into Umi. + +### Adding a model + +Umi will automatically mount the model definition in `src/models` by default, you just need to create a new file in the model folder to add a model to manage component states. + +After 2.0, to better support the demand loading of mobile H5 projects and the organization of models in large projects, we will also default mount the model under a certain page folder, you can refer to [Directory Structure Explanation](../guides/directory-structure) for specific structure. However, it is worth noting that the namespace of the model is global, and you still need to ensure your namespace is unique (the default is the file name). For most projects, we recommend managing them uniformly in the model, without using this function. + +The model writing refers to the following example: + +```js +import { queryUsers, queryUser } from '../../services/user'; + +export default { + state: { + user: {}, + }, + + effects: { + *queryUser({ payload }, { call, put }) { + const { data } = yield call(queryUser, payload); + yield put({ type: 'queryUserSuccess', payload: data }); + }, + }, + + reducers: { + queryUserSuccess(state, { payload }) { + return { + ...state, + user: payload, + }; + }, + }, + + test(state) { + console.log('test'); + return state; + }, +}; +``` + +### Connecting the Component and Model + +After creating the model, you can conveniently connect the model and component together in the component through the ES6 [Decorator](http://es6.ruanyifeng.com/#docs/decorator). Then you can access the data in the model through `this.props.[modelName]` in the component. (In the corresponding model, the default namespace is the file name) + +Component example is as follows: + +```javascript +import React, { Component } from 'react'; +import { connect } from 'umi'; + +@connect(({ user }) => ({ + user, +})) +class UserInfo extends Component { + constructor(props) { + super(props); + } + render() { + return
{this.props.user.name}
; + } +} + +export default UserInfo; +``` + +### Dispatching Events in Components + +The connect method also adds `dispatch` to `this.props`, and you can call it to trigger the effects or reducer in the model to modify the data in the model when the user triggers a certain event. As shown below: + +```javascript +import React, { Component } from 'react'; +import { connect } from 'umi'; + +@connect(({ user }) => ({ + user, +})) +class UserInfo extends Component { + constructor(props) { + super(props); + } + render() { + return ( +
{ + this.props.dispatch({ + type: 'user/test', + }); + }} + > + {this.props.user.name} +
+ ); + } +} + +export default UserInfo; +``` + +### Modifying Data + +After dispatching an action, it will find an effect or reducer defined in the model according to the type in the action. + +If it's an effect, it can request back-end data, and then trigger a reducer to modify the data. After the data is modified by the reducer, the component will update according to the latest data, thus completing a data flow. + +## Documentation + +### Model Definition + +A model can define the following parts: + +- namespace # The namespace of the model, uniquely identifying a model, can be omitted if it is the same as the file name +- state # Data in the model +- effects # Asynchronous actions, used to send asynchronous requests +- reducers # Synchronous actions, used to modify state + +### Connect + +`Connect` is used to associate a model and a component, adding related data and `dispatch` to the component's `props`. As shown below: + +```jsx +import React, { Component } from 'react'; +import { connect } from 'umi'; + +const mapModelToProps = allModels => { + return { + test: 'hello world', + // props you want connect to Component + }; +}; + +@connect(mapModelToProps) +class UserInfo extends Component { + render() { + return
{this.props.test}
; + } +} + +export default UserInfo; +``` + +It is recommended to call connect through annotation, which is equivalent to `export default connect(mapModelToProps)(UserInfo);`. Connect accepts a parameter, a method, where you receive all the model information and need to return the object to be added to props. In the example above, you can get the string 'hello world' through `this.props.test`. + +### Dispatch + +At the same time as using `connect` to associate the component and the model, the framework will also add a `this.props.dispatch` method. With this method, you can trigger an action to the model. As shown below: + +```jsx +render () { + return
{ + this.props.dispacth({ + type: 'modelnamespace/actionname', + sometestdata: 'xxx', + othertestata: {}, + }).then(() => { + // it will return a promise + // action success + }); + }}>test
+} +``` + +The actions triggered by `this.props.dispatch` are divided into two categories: effect and reducer. Below are more details on them. + +### Reducer + +Reducer is a function that handles the logic of modifying data (synchronously, cannot request back-end). It accepts state and action, returning the old or new state. That is: `(state, action) => state`. + +#### Add, Delete, Modify + +Taking todos as an example. + +```javascript +exports default { + namespace: 'todos', + state: [], + reducers: { + add(state, { payload: todo }) { + return state.concat(todo); + }, + remove(state, { payload: id }) { + return state.filter(todo => todo.id !== id); + }, + update(state, { payload: updatedTodo }) { + return state.map(todo => { + if (todo.id === updatedTodo.id) { + return { ...todo, ...updatedTodo }; + } else { + return todo; + } + }); + }, + }, +}; +``` + +#### Add, Delete, Modify Nested Data + +It is recommended to keep at most one layer of nesting to maintain state flatness. Deep nesting makes reducer hard to write and maintain. + +```javascript +app.model({ + namespace: 'app', + state: { + todos: [], + loading: false, + }, + reducers: { + add(state, { payload: todo }) { + const todos = state.todos.concat(todo); + return { ...state, todos }; + }, + }, +}); +``` + +Below is an example of deep nesting, which should be avoided as much as possible. + +```javascript +app.model({ + namespace: 'app', + state: { + a: { + b: { + todos: [], + loading: false, + }, + }, + }, + reducers: { + add(state, { payload: todo }) { + const todos = state.a.b.todos.concat(todo); + const b = { ...state.a.b, todos }; + const a = { ...state.a, b }; + return { ...state, a }; + }, + }, +}); +``` + +### Effect + +Effects are defined in the model. It is also a type of action, mainly used for asynchronous communication with the back-end. After requesting and receiving the necessary data from the back-end through effects, you can again send a reducer through the put method to modify the data. + +Effect supports asynchronous request through ES6 [Generator function](http://es6.ruanyifeng.com/#docs/generator), see the example below: + +```javascript +export default { + namespace: 'todos', + effects: { + *addRemote({ payload: todo }, { put, call }) { + yield call(addTodo, todo); + yield put({ type: 'add', payload: todo }); + }, + }, +}; +``` + +Actions defined in effects must be Generator functions defined through `*`, and asynchronous logic is triggered through the keyword `yield` in the function. + +#### Effects + +##### put + +Used to trigger an action. + +```javascript +yield put({ type: 'todos/add', payload: 'Learn Dva' }); +``` + +##### call + +Used to call asynchronous logic, supporting promise. + +```javascript +const result = yield call(fetch, '/todos'); +``` + +##### select + +Used to get data from state. + +```javascript +const todos = yield select(state => state.todos); +``` + +### loading + +The framework will by default add a model with the namespace loading, containing information related to loading of asynchronous effects in this model, its state format is as follows: + +```js +{ + global: Boolean, // Whether there is really asynchronous request being sent + models: { + [modelnamespace]: Boolean, // The loading situation for each specific model + }, + effects: { + [modelnamespace/effectname]: Boolean, // The loading situation for each specific effect + }, +} +``` + +You can use this model to implement loading animations in components. + +## Debugging + +### redux + +Since the underlying layer of dva is based on redux, you can install redux's [development tools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=zh-CN) to view the data and change records in the model. + +![reduxdevtool](https://lh3.googleusercontent.com/wfhSnnYEQc3TCXbRTpTloa-XZesgDt0xAogzGoLF1BUCU04aYhdwAjueJYTtDxfRiqjUfC539g=w640-h400-e365) + +## References + +- [dva official website](https://dvajs.com/) diff --git a/docs/docs/docs/max/i18n.en-US.md b/docs/docs/docs/max/i18n.en-US.md new file mode 100644 index 000000000000..979082ac6a6c --- /dev/null +++ b/docs/docs/docs/max/i18n.en-US.md @@ -0,0 +1,529 @@ +--- +order: 8 +toc: content +translated_at: '2024-03-17T09:15:38.373Z' +--- + +# Internationalization + +`@umi/max` comes with the [Internationalization plugin](https://github.com/umijs/umi/blob/master/packages/plugins/src/locale.ts), which easily integrates internationalization features into your Umi application. + +## Getting Started + +The internationalization plugin adopts a convention-based directory structure, where we conventionally import multilingual files in the `src/locales` directory. + +The naming of multilingual files should follow this specification: `.(js|json|ts)`. Here, `` is a delimiter, which defaults to `-`, and can be configured through the `baseSeparator` option. + +For example, if you need to introduce support for Simplified Chinese and English in your project, you can create `zh-CN.ts` and `en-US.ts` files in the `src/locales` directory: + +```diff +src + + locales + + zh-CN.ts + + en-US.ts + pages +``` + +Configure the internationalization plugin in `.umirc.ts`: + +```ts +export default { + locale: { + // Default to using src/locales/zh-CN.ts as a multilingual file + default: 'zh-CN', + baseSeparator: '-', + }, +}; +``` + +For more introductions to configuration, see the [Configuring Plugins](#configuring-plugins) section. + +Now, add your first multilingual content: + +```ts +// src/locales/zh-CN.ts +export default { + welcome: '欢迎光临 Umi 的世界!', +}; +``` + +```ts +// src/locales/en-US.ts +export default { + welcome: "Welcome to Umi's world!", +}; +``` + +You can also use `.json` files to store multilingual content: + +```json +// src/locales/zh-CN.json +{ + "welcome": "欢迎光临 Umi 的世界!", +} + +// src/locales/en-US.json +{ + "welcome": "Welcome to Umi's world!", +} +``` + +Everything is ready, now you can use multilingual content in Umi. Leave it to our `` component, just pass in the previous `welcome` as the value of the parameter `id`: + +```tsx +import { FormattedMessage } from 'umi'; + +export default function Page() { + return ( +
+ +
+ ); +}; +``` + +The rendered result is as follows: + +```html + +
欢迎光临 Umi 的世界!
+ + +
Welcome to Umi's world!
+``` + +## Using in Component Parameters + +In some cases, you need to pass multilingual content as a parameter to a certain component. This can be achieved through the `intl` object: + +```tsx +import { Alert } from 'antd'; +import { useIntl } from 'umi'; + +export default function Page() { + const intl = useIntl(); + const msg = intl.formatMessage({ + id: 'welcome', + }); + + return ; +}; +``` + +At the bottom layer, the internationalization plugin is encapsulated based on [`react-intl`](https://github.com/formatjs/formatjs/tree/main/packages/react-intl) and supports all its interfaces. For details, see [this document](https://github.com/formatjs/formatjs/blob/main/website/docs/react-intl/api.md). + +In the code above, we used the `useIntl()` interface provided by `react-intl` to initialize the `intl` object and called the [`formatMessage()`](https://github.com/formatjs/formatjs/blob/main/website/docs/react-intl/api.md#formatmessage) method of this object to format the string. + +## Formatting Strings + +You may want to dynamically interpolate in multilingual translations, so you can write multilingual content like this: + +```ts +// src/locales/zh-CN.ts +export default { + user: { + welcome: '{name},今天也是美好的一天!', + }, +}; +``` + +```ts +// src/locales/en-US.ts +export default { + user: { + welcome: '{name}, what a nice day!', + }, +}; +``` + +Above, we wrote special syntax `{name}`, which allows us to dynamically assign values at runtime: + +```tsx +import { FormattedMessage } from 'umi'; + +export default function Page() { + return ( +

+ +

+ ); +}; +``` + +If you wish to achieve this through the `intl` object, you can do so by assigning it like this: + +```tsx +import { useIntl } from 'umi'; + +export default function Page() { + const intl = useIntl(); + the msg = intl.formatMessage( + { + id: 'user.welcome', + }, + { + name: '张三', + }, + ); + + return

{msg}

; +}; +``` + +Note that the key-value pair object used for assignment should be passed as the second parameter of the `formatMessage()` method. + +The rendered result is as follows: + +```html + +

张三,今天也是美好的一天!

+ + +

张三, what a nice day!

+``` + +## Switching Languages + +The preset `` component can help you quickly add the feature of switching languages to your project, just write it like this: + +```tsx +import { SelectLang } from 'umi'; + +export default function Page() { + return ; +}; +``` + +In many cases, you may need to write your own language-switching component. This is where the `setLocale()` interface comes in handy: + +```ts +import { setLocale } from 'umi'; + +// Refresh the page when switching +setLocale('en-US'); +``` + +When switching languages using this method, the current page will be refreshed by default. You can set its second parameter to `false` to switch languages without refreshing: + +```ts +// Do not refresh the page when switching +setLocale('en-US', false); +``` + +If you need to switch to the default language, just call this method without passing any parameters: + +```ts +// If your default language is zh-CN +// Then the following call has the same effect as setLocale('zh-CN') +setLocale(); +``` + +## Multilingual Default Values + +For the sake of page consistency, if Umi does not find the content corresponding to the `id` in the current multilingual file, it will directly render the `id` as the content on the page. + +For example, if the following multilingual files are written: + +```ts +// src/locales/zh-CN.ts +export default { + table: { + submit: '提交表单', + }, +}; +``` + +```ts +// src/locales/en-US.ts +export default { + // table: { + // submit: 'SUBMIT TABLE', + // }, +}; +``` + +With the following component: + +```tsx +import { Button } from 'antd'; +import { FormattedMessage } from 'umi'; + +export default function Page() { + return ( + + ); +}; +``` + +The rendered result is: + +```html + + + + + +``` + +Especially, if you need to give a default value without completing internationalization adaptation, you can use the `defaultMessage` parameter: + +```tsx +import { Button } from 'antd'; +import { FormattedMessage } from 'umi'; + +export default function Page() { + return ( + + ); +}; +``` + +When using the `formatMessage()` method, you can do the same: + +```tsx +import { Button } from 'antd'; +import { useIntl } from 'umi'; + +export default function Page() { + const intl = useIntl(); + the msg = intl.formatMessage({ + id: 'table.submit', + defaultMessage: 'SUBMIT TABLE', + }); + + return ; +}; +``` + +Using `defaultMessage` to configure default values is not recommended as this will result in writing a large amount of repetitive internationalization content. The best case is that during your internationalization adaptation, ensure that each multilingual file contains all the keys used. + +## Introduction to Common Interfaces + +### `addLocale` Dynamically Add Language Support + +Without creating and writing separate multilingual files, you can dynamically add language support at runtime using the `addLocale()` interface. It accepts three parameters: + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `name` | `String` | Key of the multilingual | +| `message` | `Object` | Multilingual content object | +| `options` | `Object` | `momentLocale` and `antd` configurations | + +For example, if you want to dynamically introduce Traditional Chinese multilingual support, you can write code as follows: + +```ts +import { addLocale } from 'umi'; +import zhTW from 'antd/es/locale/zh_TW'; + +addLocale( + 'zh-TW', + { + welcome: '歡迎光臨 Umi 的世界!', + }, + { + momentLocale: 'zh-tw', + antd: zhTW, + }, +); +``` + +### `getAllLocales` Get Multilingual List + +You can get an array of all current multilingual options through the `getAllLocales()` interface, including multilingual options added through the `addLocale()` method. This interface defaults to looking for files in the `src/locales` directory that are like `zh-CN.(js|json|ts)` and returns the Key of the multilingual. + +```ts +import { getAllLocales } from 'umi'; + +getAllLocales(); +// [en-US, zh-CN, ...] +``` + +### `getLocale` Get the Currently Selected Language + +You can get the currently selected language through the `getLocale()` interface: + +```ts +import { getLocale } from 'umi'; + +getLocale(); +// zh-CN +``` + +### `useIntl` Get `intl` Object + +`useIntl()` is likely to be the most commonly used interface in your development. Through it, you can obtain the `intl` object, and further execute methods such as `formatMessage()` to achieve your diverse needs: + +```json +// src/locales/en-US.json +{ + "welcome": "Hi, {name}." +} +``` + +```ts +import { useIntl } from 'umi'; + +const intl = useIntl(); +const msg = intl.formatMessage( + { + id: 'welcome', + }, + { + name: 'Jackson', + }, +); +console.log(msg); +// Hi, Jackson. +``` + +For more uses of the `intl` object, please refer to the [interface documentation of `react-intl`](https://github.com/formatjs/formatjs/blob/main/website/docs/react-intl/api.md). + +### `setLocale` Set Language + +You can dynamically set the current language using the `setLocale()` interface through programming. It has two parameters: + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lang` | `String` | The language to switch to | +| `realReload` | `Boolean` | Whether to refresh the page when switching, default is `true` to refresh | + +```ts +import { setLocale } from 'umi'; + +// Refresh the page when switching +setLocale('en-US'); + +// Do not refresh the page when switching +setLocale('en-US', false); +``` + +## Configuring Plugins + +You can configure the internationalization plugin in `.umirc.ts`. The default values are as follows: + +```ts +export default { + locale: { + antd: false, // If `antd` is included in project dependencies, then default to true + baseNavigator: true, + baseSeparator: '-', + default: 'zh-CN', + title: false, + useLocalStorage: true, + }, +}; +``` + +The detailed introduction to the configuration is as follows: + +| Configuration Item | Type | Default Value | Description | +| ------------------ | ---- | ------------- | ----------- | +| `antd` | `Boolean` | `false`; if the project contains `antd` dependency, then `true` | `antd` internationalization support. For more introduction, refer to [this document](https://ant.design/docs/react/i18n-cn). | +| `baseNavigator` | `Boolean` | `true` | Enable **browser language detection**. By default, the recognition of the current language environment follows: `localStorage` `umi_locale` value > browser detection > `default` set default language > `zh-CN` | +| `baseSeparator` | `String` | `-` | **Delimiter** between Language and Country. The default is `-`, resulting in languages and directory files like `zh-CN`, `en-US` and `sk`, etc. If specified as `_`, then `default` defaults to `zh_CN`. | +| `default` | `String` | `zh-CN` | **Default language** of the project. When a specific language is not detected, use the default language set by `default`. | +| `title` | `Boolean` | `false` | Enable [**title internationalization**](#title-internationalization). | +| `useLocalStorage` | `Boolean` | `true` | Automatically using `localStorage` to save the currently used language. | + +### Title Internationalization + +Add a `title` item in the route configuration to enable internationalization support, automatically converting the page title to the corresponding multilingual content. + +For example, the multilingual files are written as follows: + +```ts +// src/locales/zh-CN.ts +export default { + 'site.title': 'Umi - 企业级 React 应用开发框架', + 'about.title': 'Umi - 关于我', +}; +``` + +```ts +// src/locales/en-US.ts +export default { + 'site.title': 'Umi - Enterprise-level React Application Framework', + 'about.title': 'Umi - About me', +}; +``` + +Configure the route content as follows: + +```ts +// .umirc.ts +export default { + title: 'site.title', + routes: [ + { + path: '/', + component: 'Index', + }, + { + path: '/about', + component: 'About', + title: 'about.title', + }, + ], +}; +``` + +When visiting the page: + +- `/` route. When the multilingual option is `zh-CN`, the page title is `Umi - 企业级 React 应用开发框架`; for `en-US`, the page title is `Umi - Enterprise-level React Application Framework`. +- `/about` route. When the multilingual option is `zh-CN`, the page title is `Umi - 关于我`; for `en-US`, the page title is `Umi - About me`. + +## Runtime Expansion + +The internationalization plugin allows you to expand and customize it at runtime. + +### Custom `getLocale` + +You can customize the logic of the `getLocale()` method for getting the page language, for example, by recognizing the link `?locale=en-US`, make `en-US` the language of the current page: + +```ts +// src/app.ts +import qs from 'qs'; + +export const locale = { + getLocale() { + const { search } = window.location; + const { locale = 'zh-CN'} = qs.parse(search, { ignoreQueryPrefix: true }); + return locale; + }, +}; +``` + +### Custom Option Configuration +Umi’s i18n is implemented based on `react-intl`. +When you need to configure more `react-intl` initialization options, you can configure them in `app.ts`. You can refer to the [react-intl documentation](https://formatjs.io/docs/react-intl/components) for specific configuration options +```js +// src/app.ts +import { RuntimeConfig } from '@umijs/max' + +export const locale: RuntimeConfig['locale'] = { + textComponent: 'span', + onError: () => { + console.log('error handler...'); + } + // locale: string + // formats: CustomFormats + // messages: Record | Record + // defaultLocale: string + // defaultFormats: CustomFormats + // timeZone?: string + // textComponent?: React.ComponentType | keyof React.ReactHTML + // wrapRichTextChunksInFragment?: boolean + // defaultRichTextElements?: Record> + // onError(err: string): void +} + +``` +## FAQ + +### Why not use the `formatMessage` syntax sugar directly? + +Although `formatMessage` is very convenient to use directly, it is detached from React's lifecycle. The most serious issue is that switching languages cannot trigger DOM re-rendering. To solve this problem, we need to refresh the browser when switching languages, which is a poor user experience. Therefore, we recommend using `useIntl` or `injectIntl`, which can achieve the same functionality. diff --git a/docs/docs/docs/max/introduce.en-US.md b/docs/docs/docs/max/introduce.en-US.md new file mode 100644 index 000000000000..6efc6270e9db --- /dev/null +++ b/docs/docs/docs/max/introduce.en-US.md @@ -0,0 +1,49 @@ +--- +order: 1 +toc: content +translated_at: '2024-03-17T09:02:46.141Z' +--- + +# Umi Max Introduction + +Umi, as an extensible enterprise-level front end application framework, has already served 10,000+ applications within Ant Group, either directly or indirectly. Throughout the process of engineering practice, it has solved numerous common problems encountered in front end development, and these experiences have been accumulated into various Umi plugins. In order to make it more convenient for developers to utilize these plugins, we have integrated them into `@umijs/max` based on their open-source foundation. This allows developers to immediately obtain the same development experience as developing Umi applications within Ant Group through a scaffold. + +## How to use + +By selecting the `Ant Design Pro` template when using `create-umi`, you can use `@umijs/max` to create a project. + +```bash {4} +$ npx create-umi@latest +? Pick Umi App Template › - Use arrow-keys. Return to submit. + Simple App +❯ Ant Design Pro + Vue Simple App +``` + +:::info{title=💡} +In Umi Max projects, use the command line `max{:bash}` instead of the original `umi{:bash}`, as shown below. +::: + +```bash /max/ +$ npx max g jest +``` + +Newly created projects come with the following plugins installed by default, which can be enabled as needed: + +- [Access Control](./access) +- [Site Analytics](./analytics) +- [Ant Design (Antd)](./antd) +- [Charts](./charts) +- [dva](./dva) +- [Initial State](./data-flow#global-initial-state) +- [Data Flow](./data-flow) +- [Layout and Menu](./layout-menu) +- [Internationalization (i18n)](./i18n) +- [model](./data-flow) +- [Qiankun Micro-frontends](./micro-frontend) +- [Request Library](./request) +- [Tailwind CSS](./tailwindcss) +- [CSS-IN-JS](./styled-components) +- [Request Strategy](./react-query) +- [Global Data Store Strategy](./valtio) +- [Module Federation](./mf) diff --git a/docs/docs/docs/max/layout-menu.en-US.md b/docs/docs/docs/max/layout-menu.en-US.md new file mode 100644 index 000000000000..34c68a592c41 --- /dev/null +++ b/docs/docs/docs/max/layout-menu.en-US.md @@ -0,0 +1,243 @@ +--- +order: 2 +toc: content +translated_at: '2024-03-17T09:01:40.363Z' +--- + +# Layout and Menu + +## How to Enable + +Configure to enable. + +```ts +// config/config.ts +export default { + layout: { + title: 'your app title', + }, +}; +``` + +## Introduction + +To further reduce R&D costs, we have integrated the layout as a Umi plugin. With simple configurations, you can have the Ant Design Layout ([ProLayout](https://procomponents.ant.design/components/layout)), including the navigation and sidebar. Thus, users don't need to worry about the layout. + +- The default is Ant Design's Layout [@ant-design/pro-layout](https://www.npmjs.com/package/@ant-design/pro-layout), supporting all of its configuration items. +- The top navigation/side menu is automatically generated based on the configuration in the route. +- Default support for 403/404 handling of routes and Error Boundary. +- Used in conjunction with the `access` [plugin](./access), it can control route permissions. +- Used in conjunction with the `initial-state` [plugin](https://github.com/umijs/umi/blob/master/packages/plugins/src/initial-state.ts) and [data flow](./data-flow) plugins, it can display default user login information. + +> Want dynamic menus? See here [Advanced Usage of Menu](https://beta-pro.ant.design/docs/advanced-menu-cn) + +## Configuration + +### Build-time Configuration + +The plugin can be enabled through the `layout` property in the `config/config.ts` configuration file. + +```ts +import { defineConfig } from 'umi'; + +export default defineConfig({ + layout: { + title: 'Ant Design', + locale: false, // Default is enabled, can be disabled if menu internationalization is not needed + }, +}); +``` + +#### title + +- Type: `string` +- Default: `name` in package.json + +The product name displayed in the upper left corner of the layout, the default value is the package name. + +#### locale + +- Type: `boolean` +- Default: `false` + +Whether to start internationalization configuration. After enabling, the menu names configured in the route will be treated as the keys for menu name internationalization. The plugin will look for the corresponding text in the locales file for `menu.[key]`, with the default value as that key, and the value of the name field configured in the route is the corresponding key value. If the menu is a multi-level route, assuming a second-level route menu, then the plugin will look for the corresponding text in the locales file for `menu.[key].[key]`. This feature requires the configuration of [`i18n`](./i18n). Can be configured as `false` to disable if menu internationalization is not needed. + +### Runtime Configuration + +Runtime configuration is written in `src/app.tsx`, with the key as `layout`. + +```tsx +import { RunTimeLayoutConfig } from '@umijs/max'; + +export const layout: RunTimeLayoutConfig = (initialState) => { + return { + // Commonly used properties + title: 'Ant Design', + logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg', + + // Default layout adjustments + rightContentRender: () => , + footerRender: () =>