diff --git a/.changeset/dirty-bats-fly.md b/.changeset/dirty-bats-fly.md
new file mode 100644
index 0000000000..c57d60d627
--- /dev/null
+++ b/.changeset/dirty-bats-fly.md
@@ -0,0 +1,5 @@
+---
+'@ice/app': patch
+---
+
+feat: support add routes definition
diff --git a/.changeset/fluffy-hounds-smoke.md b/.changeset/fluffy-hounds-smoke.md
new file mode 100644
index 0000000000..d34ef0bed8
--- /dev/null
+++ b/.changeset/fluffy-hounds-smoke.md
@@ -0,0 +1,5 @@
+---
+'@ice/route-manifest': minor
+---
+
+refactor: route id generation
diff --git a/.changeset/hip-balloons-brush.md b/.changeset/hip-balloons-brush.md
new file mode 100644
index 0000000000..d12a0eea30
--- /dev/null
+++ b/.changeset/hip-balloons-brush.md
@@ -0,0 +1,5 @@
+---
+'@ice/plugin-i18n': major
+---
+
+feat: init plugin
diff --git a/.changeset/hot-baboons-retire.md b/.changeset/hot-baboons-retire.md
new file mode 100644
index 0000000000..8a20317596
--- /dev/null
+++ b/.changeset/hot-baboons-retire.md
@@ -0,0 +1,5 @@
+---
+'@ice/route-manifest': minor
+---
+
+feat: support accept one more `defineExtraRoutes` functions
diff --git a/.changeset/red-gorillas-remember.md b/.changeset/red-gorillas-remember.md
new file mode 100644
index 0000000000..1d630a0e84
--- /dev/null
+++ b/.changeset/red-gorillas-remember.md
@@ -0,0 +1,5 @@
+---
+'@ice/runtime': patch
+---
+
+feat: support handler response
diff --git a/.changeset/tasty-spies-think.md b/.changeset/tasty-spies-think.md
new file mode 100644
index 0000000000..af1f2150c2
--- /dev/null
+++ b/.changeset/tasty-spies-think.md
@@ -0,0 +1,5 @@
+---
+'@ice/app': patch
+---
+
+fix: routeSpecifier is not unique
diff --git a/.gitignore b/.gitignore
index 4a5e4f589e..16b66d940f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@ yalc.lock
# Packages
packages/*/lib/
packages/*/esm/
+packages/*/es2017/
# temp folder .ice
examples/*/.ice
diff --git a/examples/with-antd5/ice.config.mts b/examples/with-antd5/ice.config.mts
new file mode 100644
index 0000000000..63f3c0bcab
--- /dev/null
+++ b/examples/with-antd5/ice.config.mts
@@ -0,0 +1,5 @@
+import { defineConfig } from '@ice/app';
+
+export default defineConfig(() => ({
+ ssg: false,
+}));
diff --git a/examples/with-antd5/package.json b/examples/with-antd5/package.json
new file mode 100644
index 0000000000..b4b9872217
--- /dev/null
+++ b/examples/with-antd5/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@examples/with-antd5",
+ "private": true,
+ "version": "1.0.0",
+ "scripts": {
+ "start": "ice start",
+ "build": "ice build"
+ },
+ "dependencies": {
+ "@ice/app": "workspace:*",
+ "@ice/runtime": "workspace:*",
+ "antd": "^5.0.0",
+ "dayjs": "^1.11.7",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0",
+ "react-intl": "^6.3.2"
+ },
+ "devDependencies": {
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.2"
+ }
+}
\ No newline at end of file
diff --git a/examples/with-antd5/src/app.tsx b/examples/with-antd5/src/app.tsx
new file mode 100644
index 0000000000..38e608b8f4
--- /dev/null
+++ b/examples/with-antd5/src/app.tsx
@@ -0,0 +1,7 @@
+import { defineAppConfig } from 'ice';
+
+export default defineAppConfig(() => ({
+ app: {
+ rootId: 'app',
+ },
+}));
diff --git a/examples/with-antd5/src/document.tsx b/examples/with-antd5/src/document.tsx
new file mode 100644
index 0000000000..1e7b99c49d
--- /dev/null
+++ b/examples/with-antd5/src/document.tsx
@@ -0,0 +1,22 @@
+import { Meta, Title, Links, Main, Scripts } from 'ice';
+
+function Document() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default Document;
diff --git a/examples/with-antd5/src/locales.ts b/examples/with-antd5/src/locales.ts
new file mode 100644
index 0000000000..f839a418ad
--- /dev/null
+++ b/examples/with-antd5/src/locales.ts
@@ -0,0 +1,10 @@
+export const messages: Record = {
+ en: {
+ changeLanguageTitle: 'Change locale:',
+ indexTitle: 'Index',
+ },
+ 'zh-cn': {
+ changeLanguageTitle: '修改语言:',
+ indexTitle: '首页',
+ },
+};
diff --git a/examples/with-antd5/src/pages/index.tsx b/examples/with-antd5/src/pages/index.tsx
new file mode 100644
index 0000000000..cb885ff566
--- /dev/null
+++ b/examples/with-antd5/src/pages/index.tsx
@@ -0,0 +1,12 @@
+import { DatePicker, Pagination } from 'antd';
+import { FormattedMessage } from 'react-intl';
+
+export default function Index() {
+ return (
+
+ );
+}
diff --git a/examples/with-antd5/src/pages/layout.tsx b/examples/with-antd5/src/pages/layout.tsx
new file mode 100644
index 0000000000..31a75d9cff
--- /dev/null
+++ b/examples/with-antd5/src/pages/layout.tsx
@@ -0,0 +1,47 @@
+import { Outlet } from 'ice';
+import { useState } from 'react';
+import { IntlProvider, FormattedMessage } from 'react-intl';
+import { ConfigProvider, Radio } from 'antd';
+import type { RadioChangeEvent } from 'antd';
+import enUS from 'antd/locale/en_US';
+import zhCN from 'antd/locale/zh_CN';
+import type { Locale } from 'antd/es/locale';
+
+import * as dayjs from 'dayjs';
+import { messages } from '@/locales';
+import 'dayjs/locale/zh-cn';
+
+export default function Layout() {
+ const [locale, setLocale] = useState(enUS);
+
+ const changeLocale = (e: RadioChangeEvent) => {
+ const localeValue = e.target.value;
+ setLocale(localeValue);
+ if (localeValue) {
+ dayjs.locale('zh-cn');
+ } else {
+ dayjs.locale('en');
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ English
+
+
+ 中文
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/with-antd5/src/typings.d.ts b/examples/with-antd5/src/typings.d.ts
new file mode 100644
index 0000000000..1f6ba4ffa6
--- /dev/null
+++ b/examples/with-antd5/src/typings.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/with-antd5/tsconfig.json b/examples/with-antd5/tsconfig.json
new file mode 100644
index 0000000000..55aebc3d90
--- /dev/null
+++ b/examples/with-antd5/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "baseUrl": "./",
+ "module": "ESNext",
+ "target": "ESNext",
+ "lib": ["DOM", "ESNext", "DOM.Iterable"],
+ "jsx": "react-jsx",
+ "moduleResolution": "node",
+ "strict": true,
+ "skipLibCheck": true,
+ "paths": {
+ "@/*": ["./src/*"],
+ "ice": [".ice"]
+ }
+ },
+ "include": ["src", ".ice"],
+}
diff --git a/examples/with-fusion/ice.config.mts b/examples/with-fusion/ice.config.mts
index 46eab65bee..40cf4bdb4d 100644
--- a/examples/with-fusion/ice.config.mts
+++ b/examples/with-fusion/ice.config.mts
@@ -16,4 +16,5 @@ export default defineConfig({
locales: ['af'],
}),
],
+ ssg: false,
});
diff --git a/examples/with-fusion/package.json b/examples/with-fusion/package.json
index 88379b2318..145c92723d 100644
--- a/examples/with-fusion/package.json
+++ b/examples/with-fusion/package.json
@@ -12,16 +12,17 @@
"dependencies": {
"@alifd/next": "^1.25.49",
"@ice/runtime": "workspace:*",
+ "moment": "^2.29.4",
"react": "^18.0.0",
"react-dom": "^18.0.0",
- "moment": "^2.29.4"
+ "react-intl": "^6.3.2"
},
"devDependencies": {
- "@types/react": "^18.0.0",
- "@types/react-dom": "^18.0.2",
"@ice/app": "workspace:*",
"@ice/plugin-css-assets-local": "workspace:*",
"@ice/plugin-fusion": "workspace:*",
- "@ice/plugin-moment-locales": "workspace:*"
+ "@ice/plugin-moment-locales": "workspace:*",
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.2"
}
}
diff --git a/examples/with-fusion/src/locales.ts b/examples/with-fusion/src/locales.ts
new file mode 100644
index 0000000000..089b5f35dc
--- /dev/null
+++ b/examples/with-fusion/src/locales.ts
@@ -0,0 +1,8 @@
+export const messages: Record = {
+ en: {
+ buttonText: 'Button',
+ },
+ 'zh-cn': {
+ buttonText: '按钮',
+ },
+};
diff --git a/examples/with-fusion/src/pages/index.tsx b/examples/with-fusion/src/pages/index.tsx
index 354ffcc855..2ada9e2ec0 100644
--- a/examples/with-fusion/src/pages/index.tsx
+++ b/examples/with-fusion/src/pages/index.tsx
@@ -1,11 +1,15 @@
-import { Button } from '@alifd/next';
+import { Button, DatePicker } from '@alifd/next';
import '@alifd/next/dist/next.css';
+import { FormattedMessage } from 'react-intl';
export default function Home() {
return (
with fusion
-
+
+
);
}
\ No newline at end of file
diff --git a/examples/with-fusion/src/pages/layout.tsx b/examples/with-fusion/src/pages/layout.tsx
new file mode 100644
index 0000000000..35151701ac
--- /dev/null
+++ b/examples/with-fusion/src/pages/layout.tsx
@@ -0,0 +1,46 @@
+import { Outlet } from 'ice';
+import { useState } from 'react';
+import { ConfigProvider, Radio } from '@alifd/next';
+import enUS from '@alifd/next/lib/locale/en-us';
+import zhCN from '@alifd/next/lib/locale/zh-cn';
+import { IntlProvider } from 'react-intl';
+import { messages } from '@/locales';
+
+const localeMap = new Map([
+ ['en', enUS],
+ ['zh-cn', zhCN],
+]);
+
+export default function Layout() {
+ const [locale, setLocale] = useState('en');
+ const list = [
+ {
+ value: 'en',
+ label: 'English',
+ },
+ {
+ value: 'zh-cn',
+ label: '中文',
+ },
+ ];
+
+ function changeLocale(value: string) {
+ setLocale(value);
+ }
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
diff --git a/examples/with-i18n/ice.config.mts b/examples/with-i18n/ice.config.mts
new file mode 100644
index 0000000000..fcb7723878
--- /dev/null
+++ b/examples/with-i18n/ice.config.mts
@@ -0,0 +1,13 @@
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US'],
+ defaultLocale: 'zh-CN',
+ autoRedirect: true,
+ }),
+ ],
+ ssr: true,
+});
diff --git a/examples/with-i18n/package.json b/examples/with-i18n/package.json
new file mode 100644
index 0000000000..d671d2d1c3
--- /dev/null
+++ b/examples/with-i18n/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@examples/with-i18n",
+ "private": true,
+ "version": "1.0.0",
+ "scripts": {
+ "start": "ice start",
+ "build": "ice build",
+ "serve": "tsx server.mts"
+ },
+ "dependencies": {
+ "@ice/runtime": "workspace:*",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0",
+ "react-intl": "^6.3.2"
+ },
+ "devDependencies": {
+ "@ice/app": "workspace:*",
+ "@ice/plugin-i18n": "workspace:*",
+ "@types/express": "^4.17.14",
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.2",
+ "express": "^4.17.3",
+ "tslib": "^2.5.0",
+ "tsx": "^3.12.1"
+ }
+}
\ No newline at end of file
diff --git a/examples/with-i18n/server.mts b/examples/with-i18n/server.mts
new file mode 100644
index 0000000000..7990c499b3
--- /dev/null
+++ b/examples/with-i18n/server.mts
@@ -0,0 +1,27 @@
+import express from 'express';
+import { renderToHTML } from './build/server/index.mjs';
+
+const app = express();
+const port = 4000;
+const basename = '/app';
+
+app.use(express.static('build', {}));
+
+app.use(async (req, res) => {
+ const { statusCode, statusText, headers, value: body } = await renderToHTML({ req, res }, { basename });
+ res.statusCode = statusCode;
+ res.statusMessage = statusText;
+ Object.entries((headers || {}) as Record).forEach(([name, value]) => {
+ res.setHeader(name, value);
+ });
+ if (body && req.method !== 'HEAD') {
+ res.end(body);
+ } else {
+ res.end();
+ }
+});
+
+
+app.listen(port, () => {
+ console.log(`App listening on http://localhost:${port}${basename}`);
+});
\ No newline at end of file
diff --git a/examples/with-i18n/src/app.tsx b/examples/with-i18n/src/app.tsx
new file mode 100644
index 0000000000..d0ddd0f9ba
--- /dev/null
+++ b/examples/with-i18n/src/app.tsx
@@ -0,0 +1,12 @@
+import { defineAppConfig } from 'ice';
+import { defineI18nConfig } from '@ice/plugin-i18n/types';
+
+export default defineAppConfig(() => ({
+ router: {
+ basename: '/app',
+ },
+}));
+
+export const i18nConfig = defineI18nConfig(() => ({
+ // disabledCookie: true,
+}));
diff --git a/examples/with-i18n/src/document.tsx b/examples/with-i18n/src/document.tsx
new file mode 100644
index 0000000000..1e7b99c49d
--- /dev/null
+++ b/examples/with-i18n/src/document.tsx
@@ -0,0 +1,22 @@
+import { Meta, Title, Links, Main, Scripts } from 'ice';
+
+function Document() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default Document;
diff --git a/examples/with-i18n/src/locales.ts b/examples/with-i18n/src/locales.ts
new file mode 100644
index 0000000000..ab4863cc65
--- /dev/null
+++ b/examples/with-i18n/src/locales.ts
@@ -0,0 +1,8 @@
+export const messages: Record = {
+ 'en-US': {
+ buttonText: 'Normal Button',
+ },
+ 'zh-CN': {
+ buttonText: '普通按钮',
+ },
+};
diff --git a/examples/with-i18n/src/pages/blog/a.tsx b/examples/with-i18n/src/pages/blog/a.tsx
new file mode 100644
index 0000000000..0316e78bea
--- /dev/null
+++ b/examples/with-i18n/src/pages/blog/a.tsx
@@ -0,0 +1,13 @@
+import { Link } from 'ice';
+
+export default function BlogA() {
+ return (
+ <>
+ Blog A
+
+ >
+ );
+}
diff --git a/examples/with-i18n/src/pages/blog/index.tsx b/examples/with-i18n/src/pages/blog/index.tsx
new file mode 100644
index 0000000000..ff66b3ffae
--- /dev/null
+++ b/examples/with-i18n/src/pages/blog/index.tsx
@@ -0,0 +1,13 @@
+import { Link } from 'ice';
+
+export default function Blog() {
+ return (
+ <>
+ Blog
+
+ >
+ );
+}
diff --git a/examples/with-i18n/src/pages/index.tsx b/examples/with-i18n/src/pages/index.tsx
new file mode 100644
index 0000000000..334e5da149
--- /dev/null
+++ b/examples/with-i18n/src/pages/index.tsx
@@ -0,0 +1,15 @@
+import { Link } from 'ice';
+import { FormattedMessage } from 'react-intl';
+
+export default function Home() {
+ return (
+
+
I18n Example
+ Blog
+
+
+
+ );
+}
diff --git a/examples/with-i18n/src/pages/layout.tsx b/examples/with-i18n/src/pages/layout.tsx
new file mode 100644
index 0000000000..7d3eab9868
--- /dev/null
+++ b/examples/with-i18n/src/pages/layout.tsx
@@ -0,0 +1,38 @@
+import { Outlet, useLocale, getAllLocales, getDefaultLocale, Link, useLocation } from 'ice';
+import { IntlProvider as ReactIntlProvider } from 'react-intl';
+import { messages } from '@/locales';
+
+export default function Layout() {
+ const location = useLocation();
+ const [activeLocale, setLocale] = useLocale();
+
+ return (
+
+ Current locale: {activeLocale}
+ Default locale: {getDefaultLocale()}
+ Configured locales: {JSON.stringify(getAllLocales())}
+
+ Choose language:
+
+ {
+ getAllLocales().map((locale: string) => {
+ return (
+ -
+ setLocale(locale)}
+ // state={{ locale }}
+ >
+ {locale}
+
+
+ );
+ })
+ }
+
+
+
+
+
+ );
+}
diff --git a/examples/with-i18n/tsconfig.json b/examples/with-i18n/tsconfig.json
new file mode 100644
index 0000000000..26fd9ec799
--- /dev/null
+++ b/examples/with-i18n/tsconfig.json
@@ -0,0 +1,32 @@
+{
+ "compileOnSave": false,
+ "buildOnSave": false,
+ "compilerOptions": {
+ "baseUrl": ".",
+ "outDir": "build",
+ "module": "esnext",
+ "target": "es6",
+ "jsx": "react-jsx",
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true,
+ "lib": ["es6", "dom"],
+ "sourceMap": true,
+ "allowJs": true,
+ "rootDir": "./",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": false,
+ "importHelpers": true,
+ "strictNullChecks": true,
+ "suppressImplicitAnyIndexErrors": true,
+ "noUnusedLocals": true,
+ "skipLibCheck": true,
+ "paths": {
+ "@/*": ["./src/*"],
+ "ice": [".ice"]
+ }
+ },
+ "include": ["src", ".ice", "ice.config.*"],
+ "exclude": ["build", "public"]
+}
\ No newline at end of file
diff --git a/examples/with-nested-routes/src/pages/about/#a.tsx b/examples/with-nested-routes/src/pages/about/#a.tsx
deleted file mode 100644
index d05029d1f3..0000000000
--- a/examples/with-nested-routes/src/pages/about/#a.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function A() {
- return 111
;
-}
\ No newline at end of file
diff --git a/packages/ice/src/createService.ts b/packages/ice/src/createService.ts
index 240072f1c3..7ebaa96d69 100644
--- a/packages/ice/src/createService.ts
+++ b/packages/ice/src/createService.ts
@@ -7,7 +7,12 @@ import type { Config } from '@ice/webpack-config/types';
import type { AppConfig } from '@ice/runtime/types';
import webpack from '@ice/bundles/compiled/webpack/index.js';
import fg from 'fast-glob';
-import type { DeclarationData, PluginData, ExtendsPluginAPI, TargetDeclarationData } from './types';
+import type {
+ DeclarationData,
+ PluginData,
+ ExtendsPluginAPI,
+ TargetDeclarationData,
+} from './types/index.js';
import { DeclarationType } from './types/index.js';
import Generator from './service/runtimeGenerator.js';
import { createServerCompiler } from './service/serverCompiler.js';
@@ -19,7 +24,7 @@ import test from './commands/test.js';
import getWatchEvents from './getWatchEvents.js';
import { setEnv, updateRuntimeEnv, getCoreEnvKeys } from './utils/runtimeEnv.js';
import getRuntimeModules from './utils/getRuntimeModules.js';
-import { generateRoutesInfo, getRoutesDefination } from './routes.js';
+import { generateRoutesInfo, getRoutesDefinition } from './routes.js';
import * as config from './config.js';
import { RUNTIME_TMP_DIR, WEB, RUNTIME_EXPORTS, SERVER_ENTRY } from './constant.js';
import createSpinner from './utils/createSpinner.js';
@@ -167,6 +172,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
getRouteManifest: () => routeManifest.getNestedRoute(),
getFlattenRoutes: () => routeManifest.getFlattenRoute(),
getRoutesFile: () => routeManifest.getRoutesFile(),
+ addRoutesDefinition: routeManifest.addRoutesDefinition.bind(routeManifest),
excuteServerEntry,
context: {
webpack,
@@ -212,8 +218,9 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
const coreEnvKeys = getCoreEnvKeys();
- const routesInfo = await generateRoutesInfo(rootDir, routesConfig);
+ const routesInfo = await generateRoutesInfo(rootDir, routesConfig, routeManifest.getRoutesDefinitions());
routeManifest.setRoutes(routesInfo.routes);
+
const hasExportAppData = (await getFileExports({ rootDir, file: 'src/app' })).includes('dataLoader');
const csr = !userConfig.ssr && !userConfig.ssg;
@@ -230,7 +237,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
const iceRuntimePath = '@ice/runtime';
// Only when code splitting use the default strategy or set to `router`, the router will be lazy loaded.
const lazy = [true, 'chunks', 'page'].includes(userConfig.codeSplitting);
- const { routeImports, routeDefination } = getRoutesDefination(routesInfo.routes, lazy);
+ const { routeImports, routeDefinition } = getRoutesDefinition(routesInfo.routes, lazy);
// add render data
generator.setRenderData({
...routesInfo,
@@ -250,21 +257,25 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
jsOutput: distType.includes('javascript'),
dataLoader: userConfig.dataLoader,
routeImports,
- routeDefination,
+ routeDefinition,
});
dataCache.set('routes', JSON.stringify(routesInfo));
dataCache.set('hasExportAppData', hasExportAppData ? 'true' : '');
// Render exports files if route component export dataLoader / pageConfig.
- renderExportsTemplate({
- ...routesInfo,
- hasExportAppData,
- }, generator.addRenderFile, {
- rootDir,
- runtimeDir: RUNTIME_TMP_DIR,
- templateDir: path.join(templateDir, 'exports'),
- dataLoader: Boolean(userConfig.dataLoader),
- });
+ renderExportsTemplate(
+ {
+ ...routesInfo,
+ hasExportAppData,
+ },
+ generator.addRenderFile,
+ {
+ rootDir,
+ runtimeDir: RUNTIME_TMP_DIR,
+ templateDir: path.join(templateDir, 'exports'),
+ dataLoader: Boolean(userConfig.dataLoader),
+ },
+ );
if (typeof userConfig.dataLoader === 'object' && userConfig.dataLoader.fetcher) {
const {
diff --git a/packages/ice/src/getWatchEvents.ts b/packages/ice/src/getWatchEvents.ts
index a5b3e3a4d4..8bb5081d8f 100644
--- a/packages/ice/src/getWatchEvents.ts
+++ b/packages/ice/src/getWatchEvents.ts
@@ -2,7 +2,7 @@ import * as path from 'path';
import type { Context } from 'build-scripts';
import type { Config } from '@ice/webpack-config/types';
import type { WatchEvent } from './types/plugin.js';
-import { generateRoutesInfo, getRoutesDefination } from './routes.js';
+import { generateRoutesInfo, getRoutesDefinition } from './routes.js';
import type Generator from './service/runtimeGenerator';
import getGlobalStyleGlobPattern from './utils/getGlobalStyleGlobPattern.js';
import renderExportsTemplate from './utils/renderExportsTemplate.js';
@@ -30,7 +30,7 @@ const getWatchEvents = (options: Options): WatchEvent[] => {
async (eventName: string) => {
if (eventName === 'add' || eventName === 'unlink' || eventName === 'change') {
const routesRenderData = await generateRoutesInfo(rootDir, routesConfig);
- const { routeImports, routeDefination } = getRoutesDefination(routesRenderData.routes, lazyRoutes);
+ const { routeImports, routeDefinition } = getRoutesDefinition(routesRenderData.routes, lazyRoutes);
const stringifiedData = JSON.stringify(routesRenderData);
if (cache.get('routes') !== stringifiedData) {
cache.set('routes', stringifiedData);
@@ -38,9 +38,9 @@ const getWatchEvents = (options: Options): WatchEvent[] => {
if (eventName !== 'change') {
// Specify the route files to re-render.
generator.renderFile(
- path.join(templateDir, 'routes.ts.ejs'),
- path.join(rootDir, targetDir, 'routes.ts'),
- { routeImports, routeDefination },
+ path.join(templateDir, 'routes.tsx.ejs'),
+ path.join(rootDir, targetDir, 'routes.tsx'),
+ { routeImports, routeDefinition },
);
// Keep generate route manifest for avoid breaking change.
generator.renderFile(
diff --git a/packages/ice/src/routes.ts b/packages/ice/src/routes.ts
index 1f45ff1de6..aae6a64850 100644
--- a/packages/ice/src/routes.ts
+++ b/packages/ice/src/routes.ts
@@ -1,15 +1,19 @@
import * as path from 'path';
import { formatNestedRouteManifest, generateRouteManifest } from '@ice/route-manifest';
-import type { NestedRouteManifest } from '@ice/route-manifest';
+import type { NestedRouteManifest, DefineExtraRoutes } from '@ice/route-manifest';
import type { UserConfig } from './types/userConfig.js';
import { getFileExports } from './service/analyze.js';
import formatPath from './utils/formatPath.js';
-export async function generateRoutesInfo(rootDir: string, routesConfig: UserConfig['routes'] = {}) {
+export async function generateRoutesInfo(
+ rootDir: string,
+ routesConfig: UserConfig['routes'] = {},
+ routesDefinitions: DefineExtraRoutes[] = [],
+) {
const routeManifest = generateRouteManifest(
rootDir,
routesConfig.ignoreFiles,
- routesConfig.defineRoutes,
+ [routesConfig.defineRoutes, ...routesDefinitions],
routesConfig.config,
);
@@ -51,9 +55,9 @@ export default {
};
}
-export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], lazy = false, depth = 0) {
+export function getRoutesDefinition(nestRouteManifest: NestedRouteManifest[], lazy = false, depth = 0) {
const routeImports: string[] = [];
- const routeDefination = nestRouteManifest.reduce((prev, route, currentIndex) => {
+ const routeDefinition = nestRouteManifest.reduce((prev, route) => {
const { children, path: routePath, index, componentName, file, id, layout, exports } = route;
const componentPath = id.startsWith('__') ? file : `@/pages/${file}`.replace(new RegExp(`${path.extname(file)}$`), '');
@@ -62,7 +66,7 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
if (lazy) {
loadStatement = `import(/* webpackChunkName: "p_${componentName}" */ '${formatPath(componentPath)}')`;
} else {
- const routeSpecifier = `route_${depth}_${currentIndex}`;
+ const routeSpecifier = id.replace(/[./-]/g, '_').replace(/[:*]/, '$');
routeImports.push(`import * as ${routeSpecifier} from '${formatPath(componentPath)}';`);
loadStatement = routeSpecifier;
}
@@ -100,9 +104,9 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
routeProperties.push('layout: true,');
}
if (children) {
- const res = getRoutesDefination(children, lazy, depth + 1);
+ const res = getRoutesDefinition(children, lazy, depth + 1);
routeImports.push(...res.routeImports);
- routeProperties.push(`children: [${res.routeDefination}]`);
+ routeProperties.push(`children: [${res.routeDefinition}]`);
}
prev += formatRoutesStr(depth, routeProperties);
return prev;
@@ -110,7 +114,7 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
return {
routeImports,
- routeDefination,
+ routeDefinition,
};
}
diff --git a/packages/ice/src/service/runtimeGenerator.ts b/packages/ice/src/service/runtimeGenerator.ts
index 4878c8d79c..a8af3ace67 100644
--- a/packages/ice/src/service/runtimeGenerator.ts
+++ b/packages/ice/src/service/runtimeGenerator.ts
@@ -342,7 +342,7 @@ export default class Generator {
const renderExt = '.ejs';
const realTargetPath = path.isAbsolute(targetPath)
? targetPath : path.join(this.rootDir, this.targetDir, targetPath);
- // example: templatePath = 'routes.ts.ejs'
+ // example: templatePath = 'routes.tsx.ejs'
const realTemplatePath = path.isAbsolute(templatePath)
? templatePath : path.join(this.templateDir, templatePath);
const { ext } = path.parse(templatePath);
diff --git a/packages/ice/src/types/generator.ts b/packages/ice/src/types/generator.ts
index 52c92d428f..b1df8877d5 100644
--- a/packages/ice/src/types/generator.ts
+++ b/packages/ice/src/types/generator.ts
@@ -19,7 +19,7 @@ export interface TargetDeclarationData {
declarationType?: DeclarationType;
}
-export type RenderData = Record;
+export type RenderData = Record;
type RenderDataFunction = (renderDataFunction: RenderData) => RenderData;
export interface TemplateOptions {
template: string;
diff --git a/packages/ice/src/types/plugin.ts b/packages/ice/src/types/plugin.ts
index d85bd4af54..268f59eb95 100644
--- a/packages/ice/src/types/plugin.ts
+++ b/packages/ice/src/types/plugin.ts
@@ -2,7 +2,7 @@ import type webpack from '@ice/bundles/compiled/webpack';
import type { _Plugin, CommandArgs, TaskConfig } from 'build-scripts';
import type { Configuration, Stats, WebpackOptionsNormalized } from '@ice/bundles/compiled/webpack';
import type { esbuild } from '@ice/bundles';
-import type { NestedRouteManifest } from '@ice/route-manifest';
+import type { DefineExtraRoutes, NestedRouteManifest } from '@ice/route-manifest';
import type { Config } from '@ice/webpack-config/types';
import type { AppConfig, AssetsManifest } from '@ice/runtime/types';
import type ServerCompileTask from '../utils/ServerCompileTask.js';
@@ -158,6 +158,7 @@ export interface ExtendsPluginAPI {
getRouteManifest: () => Routes;
getFlattenRoutes: () => string[];
getRoutesFile: () => string[];
+ addRoutesDefinition: (defineRoutes: DefineExtraRoutes) => void;
dataCache: Map;
createLogger: CreateLogger;
}
diff --git a/packages/ice/src/utils/routeManifest.ts b/packages/ice/src/utils/routeManifest.ts
index 4057774c29..f433b9e6d6 100644
--- a/packages/ice/src/utils/routeManifest.ts
+++ b/packages/ice/src/utils/routeManifest.ts
@@ -1,11 +1,13 @@
-import type { NestedRouteManifest } from '@ice/route-manifest';
+import type { NestedRouteManifest, DefineExtraRoutes } from '@ice/route-manifest';
import getRoutePath, { getRoutesFile } from './getRoutePaths.js';
export default class RouteManifest {
private routeManifest: NestedRouteManifest[];
+ private routesDefinitions: DefineExtraRoutes[];
constructor() {
this.routeManifest = null;
+ this.routesDefinitions = [];
}
getNestedRoute() {
@@ -26,4 +28,12 @@ export default class RouteManifest {
getRoutesFile() {
return getRoutesFile(this.getNestedRoute());
}
-}
\ No newline at end of file
+
+ public addRoutesDefinition(defineRoutes: DefineExtraRoutes) {
+ this.routesDefinitions.push(defineRoutes);
+ }
+
+ public getRoutesDefinitions() {
+ return this.routesDefinitions;
+ }
+}
diff --git a/packages/ice/templates/core/entry.client.tsx.ejs b/packages/ice/templates/core/entry.client.tsx.ejs
index 55d5071527..5c123e8b2d 100644
--- a/packages/ice/templates/core/entry.client.tsx.ejs
+++ b/packages/ice/templates/core/entry.client.tsx.ejs
@@ -16,7 +16,7 @@ const getRouterBasename = () => {
// Otherwise chunk of route component will pack @ice/jsx-runtime and depend on framework bundle.
const App = <>>;
-const render = (customOptions = {}) => {
+const render = (customOptions: Record = {}) => {
const appProps = {
app,
runtimeModules: {
@@ -29,12 +29,15 @@ const render = (customOptions = {}) => {
memoryRouter: <%- memoryRouter || false %>,
<% if(dataLoaderImport.imports) {-%>dataLoaderFetcher,<% } -%>
...customOptions,
- <% if (runtimeOptions.exports) { -%>
runtimeOptions: {
- <%- runtimeOptions.exports %>
- ...(customOptions.runtimeOptions || {}),
+ <% if (runtimeOptions.exports) { -%>
+ <%- runtimeOptions.exports %>
+ <% } -%>
+ <% if (locals.customRuntimeOptions) { -%>
+ ...<%- JSON.stringify(customRuntimeOptions) %>,
+ <% } -%>
+ ...customOptions.runtimeOptions,
},
- <% } -%>
};
return runClientApp(appProps);
};
diff --git a/packages/ice/templates/core/entry.server.ts.ejs b/packages/ice/templates/core/entry.server.ts.ejs
index 4ea5769093..24d0ac86f5 100644
--- a/packages/ice/templates/core/entry.server.ts.ejs
+++ b/packages/ice/templates/core/entry.server.ts.ejs
@@ -92,10 +92,13 @@ function mergeOptions(options) {
routesConfig,
distType,
serverData,
-<% if (runtimeOptions.exports) { -%>
runtimeOptions: {
+ <% if (runtimeOptions.exports) { -%>
<%- runtimeOptions.exports %>
+ <% } -%>
+ <% if (locals.customRuntimeOptions) { _%>
+ ...<%- JSON.stringify(customRuntimeOptions) %>,
+ <% } _%>
},
-<% } -%>
};
}
diff --git a/packages/ice/templates/core/routes.tsx.ejs b/packages/ice/templates/core/routes.tsx.ejs
index 16b78a77da..88ca61cad8 100644
--- a/packages/ice/templates/core/routes.tsx.ejs
+++ b/packages/ice/templates/core/routes.tsx.ejs
@@ -4,5 +4,5 @@ export default ({
requestContext,
renderMode,
}) => ([
- <%- routeDefination %>
+ <%- routeDefinition %>
]);
diff --git a/packages/plugin-i18n/README.md b/packages/plugin-i18n/README.md
new file mode 100644
index 0000000000..08dc3542f6
--- /dev/null
+++ b/packages/plugin-i18n/README.md
@@ -0,0 +1,46 @@
+# @ice/plugin-i18n
+
+组件功能描述
+
+## Install
+
+```bash
+$ npm i @ice/plugin-i18n --save-dev
+```
+
+## Usage
+
+```ts
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US'],
+ defaultLocale: 'zh-CN',
+ }),
+ ],
+});
+```
+
+## Options
+
+### `locales`
+
+- **type:** `string[]`
+
+The locales you want to support in your app. This option is required.
+
+### defaultLocale
+
+- **type:** `string`
+
+The default locale you want to be used when visiting a non-locale prefixed path. This option is required.
+
+### autoRedirect
+
+- **type:** `boolean`
+- **default:** `true`
+
+Redirect to the preferred locale automatically. This option should be used with the middleware. If you deploy your application in production, you should read the [example]() for more detail.
diff --git a/packages/plugin-i18n/build.config.mts b/packages/plugin-i18n/build.config.mts
new file mode 100644
index 0000000000..c91fc6b44f
--- /dev/null
+++ b/packages/plugin-i18n/build.config.mts
@@ -0,0 +1,8 @@
+import { defineConfig } from '@ice/pkg';
+
+// https://pkg.ice.work/reference/config-list/
+export default defineConfig({
+ transform: {
+ formats: ['es2017'],
+ },
+});
diff --git a/packages/plugin-i18n/package.json b/packages/plugin-i18n/package.json
new file mode 100644
index 0000000000..8978be9658
--- /dev/null
+++ b/packages/plugin-i18n/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "@ice/plugin-i18n",
+ "version": "0.0.0",
+ "description": "I18n plugin for ice.js 3.",
+ "files": [
+ "es2017",
+ "!es2017/**/*.map"
+ ],
+ "type": "module",
+ "main": "es2017/index.js",
+ "module": "es2017/index.js",
+ "types": "es2017/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./es2017/index.d.ts",
+ "import": "./es2017/index.js",
+ "default": "./es2017/index.js"
+ },
+ "./runtime": {
+ "types": "./es2017/runtime/index.d.ts",
+ "import": "./es2017/runtime/index.js",
+ "default": "./es2017/runtime/index.js"
+ },
+ "./types": {
+ "types": "./es2017/types.d.ts",
+ "import": "./es2017/types.js",
+ "default": "./es2017/types.js"
+ },
+ "./*": "./*"
+ },
+ "sideEffects": false,
+ "scripts": {
+ "watch": "ice-pkg start",
+ "build": "ice-pkg build"
+ },
+ "keywords": [
+ "ice.js",
+ "i18n",
+ "plugin"
+ ],
+ "dependencies": {
+ "@ice/jsx-runtime": "^0.2.0",
+ "@swc/helpers": "^0.4.14",
+ "accept-language-parser": "^1.5.0",
+ "universal-cookie": "^4.0.4",
+ "url-join": "^5.0.0"
+ },
+ "devDependencies": {
+ "@ice/pkg": "^1.0.0",
+ "@ice/app": "workspace:^",
+ "@ice/runtime": "workspace:^",
+ "@remix-run/router": "^1.5.0",
+ "@types/react": "^18.0.33",
+ "@types/accept-language-parser": "^1.5.3",
+ "webpack-dev-server": "^4.13.2"
+ },
+ "peerDependencies": {
+ "@ice/app": "^3.0.0",
+ "@ice/runtime": "^1.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "license": "MIT"
+}
\ No newline at end of file
diff --git a/packages/plugin-i18n/runtime.d.ts b/packages/plugin-i18n/runtime.d.ts
new file mode 100644
index 0000000000..c26ea1b145
--- /dev/null
+++ b/packages/plugin-i18n/runtime.d.ts
@@ -0,0 +1 @@
+export * from './es2017/runtime/index';
\ No newline at end of file
diff --git a/packages/plugin-i18n/src/constants.ts b/packages/plugin-i18n/src/constants.ts
new file mode 100644
index 0000000000..00ad99dd39
--- /dev/null
+++ b/packages/plugin-i18n/src/constants.ts
@@ -0,0 +1 @@
+export const LOCALE_COOKIE_NAME = 'ice_locale';
diff --git a/packages/plugin-i18n/src/index.ts b/packages/plugin-i18n/src/index.ts
new file mode 100644
index 0000000000..3a39633b0f
--- /dev/null
+++ b/packages/plugin-i18n/src/index.ts
@@ -0,0 +1,85 @@
+import * as path from 'path';
+import { createRequire } from 'module';
+import { fileURLToPath } from 'url';
+import type { Plugin } from '@ice/app/types';
+import type { CreateLoggerReturnType } from '@ice/app';
+import type { I18nConfig } from './types.js';
+
+const _require = createRequire(import.meta.url);
+const _dirname = path.dirname(fileURLToPath(import.meta.url));
+const packageJSON = _require('../package.json');
+const { name: packageName } = packageJSON;
+
+const plugin: Plugin = (i18nConfig) => ({
+ name: packageName,
+ setup: ({ addRoutesDefinition, generator, createLogger }) => {
+ const logger = createLogger('plugin-i18n');
+ checkPluginOptions(i18nConfig, logger);
+
+ const { locales, defaultLocale } = i18nConfig;
+ const prefixedLocales = locales.filter(locale => locale !== defaultLocale);
+
+ const defineRoutes: Parameters[0] = (defineRoute, options) => {
+ function defineChildrenRoutes(children: any[], prefixedLocale: string) {
+ children.forEach(child => {
+ defineRoute(
+ child.path,
+ child.file,
+ { index: child.index },
+ () => {
+ if (child.children) {
+ defineChildrenRoutes(child.children, prefixedLocale);
+ }
+ });
+ });
+ }
+ prefixedLocales.forEach(prefixedLocale => {
+ options.nestedRouteManifest.forEach(route => {
+ const newRoutePath = `${prefixedLocale}${route.path ? `/${route.path}` : ''}`;
+ defineRoute(newRoutePath, route.file, { index: route.index }, () => {
+ route.children && defineChildrenRoutes(route.children, prefixedLocale);
+ });
+ });
+ });
+ };
+ addRoutesDefinition(defineRoutes);
+
+ generator.addRenderFile(
+ path.join(_dirname, 'templates/plugin-i18n.ts.ejs'),
+ 'plugin-i18n.ts',
+ {
+ defaultLocale: defaultLocale,
+ locales: JSON.stringify(locales),
+ },
+ );
+ generator.addExport({
+ specifier: ['getDefaultLocale', 'getAllLocales'],
+ source: './plugin-i18n',
+ });
+ generator.addExport({
+ specifier: ['withLocale', 'useLocale'],
+ source: `${packageName}/runtime`,
+ });
+
+ generator.modifyRenderData((renderData) => {
+ renderData.customRuntimeOptions ||= {};
+ (renderData.customRuntimeOptions as Record).i18nConfig = i18nConfig;
+ return renderData;
+ });
+ },
+ runtime: `${packageName}/runtime`,
+});
+
+function checkPluginOptions(options: I18nConfig, logger: CreateLoggerReturnType) {
+ const { locales, defaultLocale } = options;
+ if (!Array.isArray(locales)) {
+ logger.error(`The plugin option \`locales\` type should be array but received ${typeof locales}`);
+ process.exit(1);
+ }
+ if (typeof defaultLocale !== 'string') {
+ logger.error(`The plugin option \`defaultLocale\` type should be string but received ${typeof defaultLocale}`);
+ process.exit(1);
+ }
+}
+
+export default plugin;
diff --git a/packages/plugin-i18n/src/runtime/I18nContext.tsx b/packages/plugin-i18n/src/runtime/I18nContext.tsx
new file mode 100644
index 0000000000..0470680b83
--- /dev/null
+++ b/packages/plugin-i18n/src/runtime/I18nContext.tsx
@@ -0,0 +1,58 @@
+import type { ReactElement, SetStateAction, Dispatch } from 'react';
+import React, { createContext, useState, useContext } from 'react';
+import normalizeLocalePath from '../utils/normalizeLocalePath.js';
+import setLocaleToCookie from '../utils/setLocaleToCookie.js';
+import type { I18nConfig } from '../types.js';
+
+type ContextValue = [string, Dispatch>];
+
+interface I18nProvider {
+ children: ReactElement;
+ locales: I18nConfig['locales'];
+ defaultLocale: I18nConfig['defaultLocale'];
+ pathname: string;
+ disableCookie: boolean;
+ basename?: string;
+ headers?: {
+ [key: string]: string | string[];
+ };
+}
+
+export const I18nContext = createContext(null);
+
+I18nContext.displayName = 'I18nContext';
+
+export function I18nProvider({
+ children,
+ locales,
+ defaultLocale,
+ disableCookie,
+ pathname,
+ basename,
+}: I18nProvider) {
+ const [locale, updateLocale] = useState(
+ normalizeLocalePath({ pathname, basename, locales: locales }).pathLocale || defaultLocale,
+ );
+
+ function setLocale(locale: string) {
+ !disableCookie && setLocaleToCookie(locale);
+ updateLocale(locale);
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useLocale() {
+ return useContext(I18nContext);
+}
+
+export function withLocale(Component: React.ComponentType) {
+ return (props: Props) => {
+ const [locale, setLocale] = useLocale();
+ return ;
+ };
+}
diff --git a/packages/plugin-i18n/src/runtime/hijackHistory.tsx b/packages/plugin-i18n/src/runtime/hijackHistory.tsx
new file mode 100644
index 0000000000..984284c79a
--- /dev/null
+++ b/packages/plugin-i18n/src/runtime/hijackHistory.tsx
@@ -0,0 +1,79 @@
+import type { History, To } from '@remix-run/router';
+import urlJoin from 'url-join';
+import detectLocale from '../utils/detectLocale.js';
+import normalizeLocalePath from '../utils/normalizeLocalePath.js';
+import type { I18nConfig } from '../types.js';
+
+/**
+ * Hijack history in order to add locale prefix to the new route path.
+ */
+export default function hijackHistory(
+ history: History,
+ i18nConfig: I18nConfig,
+ disableCookie: boolean,
+ basename?: string,
+) {
+ const originHistory = { ...history };
+ const { defaultLocale, locales } = i18nConfig;
+
+ function createNewNavigate(type: 'push' | 'replace') {
+ return function (toPath: To, state?: Record) {
+ const locale = state?.locale;
+ const localePrefixPathname = getLocalePrefixPathname({
+ toPath,
+ basename,
+ locales,
+ currentLocale: locale,
+ defaultLocale,
+ disableCookie,
+ });
+ originHistory[type](localePrefixPathname, state);
+ };
+ }
+
+ history.push = createNewNavigate('push');
+
+ history.replace = createNewNavigate('replace');
+}
+
+function getLocalePrefixPathname({
+ toPath,
+ locales,
+ defaultLocale,
+ basename,
+ disableCookie,
+ currentLocale = '',
+}: {
+ toPath: To;
+ locales: string[];
+ defaultLocale: string;
+ disableCookie: boolean;
+ currentLocale: string;
+ basename?: string;
+}) {
+ const pathname = getPathname(toPath);
+ const locale = disableCookie ? currentLocale : detectLocale({
+ locales,
+ defaultLocale,
+ pathname,
+ disableCookie,
+ });
+ const { pathname: newPathname } = normalizeLocalePath({ pathname, locales, basename });
+ return urlJoin(
+ basename,
+ locale === defaultLocale ? '' : locale,
+ newPathname,
+ typeof toPath === 'string' ? '' : toPath.search,
+ );
+}
+
+function getPathname(toPath: To): string {
+ if (isPathnameString(toPath)) {
+ return toPath;
+ }
+ return toPath.pathname;
+}
+
+function isPathnameString(pathname: To): pathname is string {
+ return typeof pathname === 'string';
+}
diff --git a/packages/plugin-i18n/src/runtime/index.tsx b/packages/plugin-i18n/src/runtime/index.tsx
new file mode 100644
index 0000000000..dee5e6a12c
--- /dev/null
+++ b/packages/plugin-i18n/src/runtime/index.tsx
@@ -0,0 +1,89 @@
+import * as React from 'react';
+import type { RuntimePlugin } from '@ice/runtime/types';
+import detectLocale from '../utils/detectLocale.js';
+import type { I18nAppConfig, I18nConfig } from '../types.js';
+import getLocaleRedirectPath from '../utils/getLocaleRedirectPath.js';
+import { I18nProvider, useLocale, withLocale } from './I18nContext.js';
+import hijackHistory from './hijackHistory.js';
+
+const EXPORT_NAME = 'i18nConfig';
+// Mock it to avoid ssg error and use new URL to parse url instead of url.parse.
+const baseUrl = 'http://127.0.0.1';
+
+const runtime: RuntimePlugin<{ i18nConfig: I18nConfig }> = async (
+ {
+ appContext,
+ addProvider,
+ history,
+ addResponseHandler,
+ },
+ runtimeOptions,
+) => {
+ const { basename, requestContext, appExport } = appContext;
+ const exported = appExport[EXPORT_NAME];
+ const i18nAppConfig: I18nAppConfig = Object.assign(
+ { disableCookie: false },
+ (typeof exported === 'function' ? await exported() : exported),
+ );
+ const disableCookie = typeof i18nAppConfig.disableCookie === 'function'
+ ? i18nAppConfig.disableCookie()
+ : i18nAppConfig.disableCookie;
+
+ const { i18nConfig } = runtimeOptions;
+ const { locales, defaultLocale, autoRedirect } = i18nConfig;
+
+ addProvider(({ children }) => {
+ return (
+
+ {children}
+
+ );
+ });
+
+ if (history) {
+ hijackHistory(history, i18nConfig, disableCookie, basename);
+ }
+
+ if (autoRedirect) {
+ addResponseHandler((req) => {
+ const url = new URL(`${baseUrl}${req.url}`);
+ const detectedLocale = detectLocale({
+ locales,
+ defaultLocale,
+ basename,
+ pathname: url.pathname,
+ headers: req.headers,
+ disableCookie: false,
+ });
+
+ const localeRedirectPath = getLocaleRedirectPath({
+ pathname: url.pathname,
+ defaultLocale,
+ detectedLocale,
+ basename,
+ });
+ if (localeRedirectPath) {
+ url.pathname = localeRedirectPath;
+
+ return {
+ statusCode: 302,
+ statusText: 'Found',
+ headers: {
+ location: String(Object.assign(new URL(baseUrl), url)).replace(RegExp(`^${baseUrl}`), ''),
+ },
+ };
+ }
+ });
+ }
+};
+
+export default runtime;
+
+export { useLocale, withLocale };
\ No newline at end of file
diff --git a/packages/plugin-i18n/src/templates/plugin-i18n.ts.ejs b/packages/plugin-i18n/src/templates/plugin-i18n.ts.ejs
new file mode 100644
index 0000000000..6d87f99556
--- /dev/null
+++ b/packages/plugin-i18n/src/templates/plugin-i18n.ts.ejs
@@ -0,0 +1,7 @@
+export function getDefaultLocale() {
+ return '<%= defaultLocale %>';
+}
+
+export function getAllLocales() {
+ return <%- locales %>;
+}
diff --git a/packages/plugin-i18n/src/types.ts b/packages/plugin-i18n/src/types.ts
new file mode 100644
index 0000000000..185f58b493
--- /dev/null
+++ b/packages/plugin-i18n/src/types.ts
@@ -0,0 +1,32 @@
+export interface I18nConfig {
+ /**
+ * The locales which you want to support in your app.
+ */
+ locales: string[];
+ /**
+ * The default locale which is used when visiting a non-locale prefixed path. e.g `/home`.
+ */
+ defaultLocale: string;
+ /**
+ * Automatically redirect to the correct path which is based on user's preferred locale.
+ */
+ autoRedirect?: boolean;
+}
+
+export interface I18nAppConfig {
+ /**
+ * Weather or not current application cookie is blocked(authorized).
+ * If it is, we will not get the locale value(ice_locale) from cookie.
+ * @default {false}
+ */
+ disableCookie?: boolean | (() => boolean);
+}
+
+export function defineI18nConfig(
+ configOrDefineConfig: I18nAppConfig | (() => I18nAppConfig),
+): I18nAppConfig {
+ if (typeof configOrDefineConfig === 'function') {
+ return configOrDefineConfig();
+ }
+ return configOrDefineConfig;
+}
diff --git a/packages/plugin-i18n/src/typings.d.ts b/packages/plugin-i18n/src/typings.d.ts
new file mode 100644
index 0000000000..922f320605
--- /dev/null
+++ b/packages/plugin-i18n/src/typings.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/plugin-i18n/src/utils/detectLocale.ts b/packages/plugin-i18n/src/utils/detectLocale.ts
new file mode 100644
index 0000000000..53aaa88171
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/detectLocale.ts
@@ -0,0 +1,33 @@
+import type { I18nConfig } from '../types.js';
+import getLocaleFromCookie from './getLocaleFromCookie.js';
+import normalizeLocalePath from './normalizeLocalePath.js';
+import getPreferredLocale from './getPreferredLocale.js';
+
+interface DetectLocaleParams {
+ locales: I18nConfig['locales'];
+ defaultLocale: I18nConfig['defaultLocale'];
+ pathname: string;
+ disableCookie: boolean;
+ basename?: string;
+ headers?: {
+ [key: string]: string | string[];
+ };
+}
+
+export default function detectLocale({
+ locales,
+ defaultLocale,
+ pathname,
+ basename,
+ disableCookie,
+ headers = {},
+}: DetectLocaleParams): string {
+ const detectedLocale = (
+ normalizeLocalePath({ pathname, locales, basename }).pathLocale ||
+ (!disableCookie && getLocaleFromCookie(locales, headers)) ||
+ getPreferredLocale(locales, headers) ||
+ defaultLocale
+ );
+
+ return detectedLocale;
+}
diff --git a/packages/plugin-i18n/src/utils/getLocaleFromCookie.ts b/packages/plugin-i18n/src/utils/getLocaleFromCookie.ts
new file mode 100644
index 0000000000..fbf661e756
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/getLocaleFromCookie.ts
@@ -0,0 +1,14 @@
+import type { I18nConfig } from 'src/types.js';
+import Cookies from 'universal-cookie';
+import { LOCALE_COOKIE_NAME } from '../constants.js';
+
+export default function getLocaleFromCookie(
+ locales: I18nConfig['locales'],
+ headers: { [key: string]: string | string[] | undefined } = {},
+) {
+ const cookies: Cookies = new Cookies(typeof window === 'undefined' ? headers.cookie : undefined);
+ const iceLocale = cookies.get(LOCALE_COOKIE_NAME);
+ const locale = locales.find(locale => iceLocale === locale) || undefined;
+
+ return locale;
+}
\ No newline at end of file
diff --git a/packages/plugin-i18n/src/utils/getLocaleRedirectPath.ts b/packages/plugin-i18n/src/utils/getLocaleRedirectPath.ts
new file mode 100644
index 0000000000..68c8c55dfe
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/getLocaleRedirectPath.ts
@@ -0,0 +1,24 @@
+import urlJoin from 'url-join';
+import type { I18nConfig } from '../types.js';
+import removeBasenameFromPath from './removeBasenameFromPath.js';
+
+interface GetRedirectPathOptions {
+ pathname: string;
+ defaultLocale: I18nConfig['defaultLocale'];
+ detectedLocale: string;
+ basename?: string;
+}
+
+export default function getLocaleRedirectPath({
+ pathname,
+ defaultLocale,
+ detectedLocale,
+ basename,
+}: GetRedirectPathOptions) {
+ const normalizedPathname = removeBasenameFromPath(pathname, basename);
+ const isRootPath = normalizedPathname === '/';
+
+ if (isRootPath && defaultLocale !== detectedLocale) {
+ return urlJoin(basename, detectedLocale);
+ }
+}
diff --git a/packages/plugin-i18n/src/utils/getPreferredLocale.ts b/packages/plugin-i18n/src/utils/getPreferredLocale.ts
new file mode 100644
index 0000000000..50ff12b112
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/getPreferredLocale.ts
@@ -0,0 +1,14 @@
+import { pick as acceptLanguagePick } from 'accept-language-parser';
+
+/**
+ * Get the preferred locale by Accept-Language in request headers(Server) or window.navigator.languages(Client)
+ */
+export default function getPreferredLocale(locales: string[], headers: { [key: string]: string | string[] } = {}) {
+ if (typeof window === 'undefined') {
+ const acceptLanguageValue = headers?.['accept-language'] as string;
+ return acceptLanguagePick(locales, acceptLanguageValue);
+ } else {
+ const acceptLanguages = window.navigator.languages || [];
+ return acceptLanguages.find(acceptLanguage => locales.includes(acceptLanguage));
+ }
+}
diff --git a/packages/plugin-i18n/src/utils/normalizeLocalePath.ts b/packages/plugin-i18n/src/utils/normalizeLocalePath.ts
new file mode 100644
index 0000000000..08c0c06328
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/normalizeLocalePath.ts
@@ -0,0 +1,29 @@
+import type { I18nConfig } from '../types.js';
+import removeBasenameFromPath from './removeBasenameFromPath.js';
+
+interface NormalizeLocalePathOptions {
+ pathname: string;
+ locales: I18nConfig['locales'];
+ basename?: string;
+}
+
+export default function normalizeLocalePath({ pathname, locales, basename }: NormalizeLocalePathOptions) {
+ const originPathname = removeBasenameFromPath(pathname, basename);
+ const subPaths = originPathname.split('/');
+ let newPathname = originPathname;
+ let pathLocale: string | undefined;
+
+ for (const locale of locales) {
+ if (subPaths[1] && subPaths[1] === locale) {
+ pathLocale = locale;
+ subPaths.splice(1, 1);
+ newPathname = subPaths.join('/') || '/';
+ break;
+ }
+ }
+
+ return {
+ pathname: newPathname,
+ pathLocale,
+ };
+}
\ No newline at end of file
diff --git a/packages/plugin-i18n/src/utils/removeBasenameFromPath.ts b/packages/plugin-i18n/src/utils/removeBasenameFromPath.ts
new file mode 100644
index 0000000000..acb994b86f
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/removeBasenameFromPath.ts
@@ -0,0 +1,18 @@
+export default function removeBasenameFromPath(pathname: string, basename?: string) {
+ if (typeof basename !== 'string') {
+ return pathname;
+ }
+ if (basename[0] !== '/') {
+ // compatible with no slash. For example: ice -> /ice
+ basename = `/${basename}`;
+ }
+
+ if (pathname.startsWith(basename)) {
+ pathname = pathname.substring(basename.length);
+ if (!pathname.startsWith('/')) {
+ pathname = `/${pathname || ''}`;
+ }
+ }
+
+ return pathname;
+}
diff --git a/packages/plugin-i18n/src/utils/setLocaleToCookie.ts b/packages/plugin-i18n/src/utils/setLocaleToCookie.ts
new file mode 100644
index 0000000000..308a3afb0f
--- /dev/null
+++ b/packages/plugin-i18n/src/utils/setLocaleToCookie.ts
@@ -0,0 +1,10 @@
+import Cookies from 'universal-cookie';
+import { LOCALE_COOKIE_NAME } from '../constants.js';
+
+/**
+ * NOTE: Call this function in browser.
+ */
+export default function setLocaleToCookie(locale: string) {
+ const cookies = new Cookies();
+ cookies.set(LOCALE_COOKIE_NAME, locale, { path: '/' });
+}
diff --git a/packages/plugin-i18n/tsconfig.json b/packages/plugin-i18n/tsconfig.json
new file mode 100644
index 0000000000..67afd8c5e4
--- /dev/null
+++ b/packages/plugin-i18n/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "baseUrl": "./",
+ "rootDir": "src",
+ },
+ "include": ["src"]
+}
diff --git a/packages/plugin-i18n/types.d.ts b/packages/plugin-i18n/types.d.ts
new file mode 100644
index 0000000000..d285a37f53
--- /dev/null
+++ b/packages/plugin-i18n/types.d.ts
@@ -0,0 +1 @@
+export * from './es2017/types';
diff --git a/packages/plugin-pha/src/index.ts b/packages/plugin-pha/src/index.ts
index 0e7fc6783e..9fdff4f83c 100644
--- a/packages/plugin-pha/src/index.ts
+++ b/packages/plugin-pha/src/index.ts
@@ -34,7 +34,7 @@ const plugin: Plugin = (options) => ({
const { template = true, preload = false } = options || {};
const { command, rootDir } = context;
- const logger = createLogger('PHA');
+ const logger = createLogger('plugin-pha');
// Get variable blows from task config.
let compiler: Compiler;
diff --git a/packages/plugin-store/package.json b/packages/plugin-store/package.json
index b5e047ed08..9f18109173 100644
--- a/packages/plugin-store/package.json
+++ b/packages/plugin-store/package.json
@@ -44,8 +44,8 @@
"micromatch": "^4.0.5"
},
"devDependencies": {
- "@ice/app": "^3.1.2",
- "@ice/runtime": "^1.1.3",
+ "@ice/app": "workspace:^",
+ "@ice/runtime": "workspace:^",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@types/react": "^18.0.0",
diff --git a/packages/route-manifest/src/index.ts b/packages/route-manifest/src/index.ts
index 591b25f234..c8d62d18d3 100644
--- a/packages/route-manifest/src/index.ts
+++ b/packages/route-manifest/src/index.ts
@@ -2,14 +2,30 @@
import fs from 'fs';
import path from 'path';
import minimatch from 'minimatch';
-import { createComponentName, createRouteId, defineRoutes, normalizeSlashes } from './routes.js';
-import type { RouteManifest, DefineRouteFunction, NestedRouteManifest, ConfigRoute } from './routes.js';
+import {
+ createComponentName,
+ removeLastLayoutStrFromId,
+ createRouteId,
+ defineRoutes,
+ normalizeSlashes,
+ createFileId,
+} from './routes.js';
+import type {
+ RouteManifest,
+ DefineRouteFunction,
+ NestedRouteManifest,
+ ConfigRoute,
+ DefineRoutesOptions,
+ DefineExtraRoutes,
+} from './routes.js';
export type {
RouteManifest,
NestedRouteManifest,
DefineRouteFunction,
ConfigRoute,
+ DefineExtraRoutes,
+ DefineRoutesOptions,
};
export interface RouteItem {
@@ -38,7 +54,7 @@ export function isRouteModuleFile(filename: string): boolean {
export function generateRouteManifest(
rootDir: string,
ignoreFiles: string[] = [],
- defineExtraRoutes?: (defineRoute: DefineRouteFunction) => void,
+ defineExtraRoutesQueue?: DefineExtraRoutes[],
routeConfig?: RouteItem[],
) {
const srcDir = path.join(rootDir, 'src');
@@ -47,6 +63,10 @@ export function generateRouteManifest(
if (fs.existsSync(path.resolve(srcDir, 'pages'))) {
const conventionalRoutes = defineConventionalRoutes(
rootDir,
+ {
+ routeManifest,
+ nestedRouteManifest: formatNestedRouteManifest(routeManifest),
+ },
ignoreFiles,
);
@@ -59,15 +79,25 @@ export function generateRouteManifest(
}
}
// 3. add extra routes from user config
- if (defineExtraRoutes) {
- const extraRoutes = defineRoutes(defineExtraRoutes);
- for (const key of Object.keys(extraRoutes)) {
- const route = extraRoutes[key];
- routeManifest[route.id] = {
- ...route,
- parentId: route.parentId || undefined,
- };
- }
+ if (Array.isArray(defineExtraRoutesQueue)) {
+ defineExtraRoutesQueue.forEach((defineExtraRoutes) => {
+ if (defineExtraRoutes) {
+ const extraRoutes = defineRoutes(
+ defineExtraRoutes,
+ {
+ routeManifest,
+ nestedRouteManifest: formatNestedRouteManifest(routeManifest),
+ },
+ );
+ for (const key of Object.keys(extraRoutes)) {
+ const route = extraRoutes[key];
+ routeManifest[route.id] = {
+ ...route,
+ parentId: route.parentId || undefined,
+ };
+ }
+ }
+ });
}
// Add routes by routes config.
@@ -86,7 +116,7 @@ export function generateRouteManifest(
export function parseRoute(routeItem: RouteItem, parentId?: string, parentPath?: string) {
const routes = [];
const { path: routePath, component, children } = routeItem;
- const id = createRouteId(component);
+ const id = createRouteId(component, routePath, parentPath);
let index;
const currentPath = path.join(parentPath || '/', routePath).split(path.sep).join('/');
const isRootPath = currentPath === '/';
@@ -101,7 +131,7 @@ export function parseRoute(routeItem: RouteItem, parentId?: string, parentPath?:
id,
parentId,
file: component,
- componentName: createComponentName(id),
+ componentName: createComponentName(component),
layout: !!children,
};
routes.push(route);
@@ -128,6 +158,7 @@ export function formatNestedRouteManifest(routeManifest: RouteManifest, parentId
function defineConventionalRoutes(
rootDir: string,
+ options: DefineRoutesOptions,
ignoredFilePatterns?: string[],
): RouteManifest {
const files: { [routeId: string]: string } = {};
@@ -143,40 +174,41 @@ function defineConventionalRoutes(
}
if (isRouteModuleFile(file)) {
- let routeId = createRouteId(file);
- files[routeId] = file;
+ let fileId = createFileId(file);
+ files[fileId] = file;
return;
}
},
);
- const routeIds = Object.keys(files).sort(byLongestFirst);
+ const fileIds = Object.keys(files).sort(byLongestFirst);
const uniqueRoutes = new Map();
// 2. recurse through all routes using the public defineRoutes() API
function defineNestedRoutes(
defineRoute: DefineRouteFunction,
+ options: DefineRoutesOptions,
parentId?: string,
): void {
- const childRouteIds = routeIds.filter((id) => {
- const parentRouteId = findParentRouteId(routeIds, id);
- return parentRouteId === parentId;
+ const childFileIds = fileIds.filter((id) => {
+ const parentFileId = findParentFileId(fileIds, id);
+ return parentFileId === parentId;
});
- for (let routeId of childRouteIds) {
+ for (let fileId of childFileIds) {
const parentRoutePath = removeLastLayoutStrFromId(parentId) || '';
const routePath: string | undefined = createRoutePath(
// parentRoutePath = 'home', routeId = 'home/me', the new routeId is 'me'
// in order to escape the child route path is absolute path
- routeId.slice(parentRoutePath.length + (parentRoutePath ? 1 : 0)),
+ fileId.slice(parentRoutePath.length + (parentRoutePath ? 1 : 0)),
);
- const routeFilePath = normalizeSlashes(path.join('src', 'pages', files[routeId]));
+ const routeFilePath = normalizeSlashes(path.join('src', 'pages', files[fileId]));
if (RegExp(`[^${validRouteChar.join('')}]+`).test(routePath)) {
throw new Error(`invalid character in '${routeFilePath}'. Only support char: ${validRouteChar.join(', ')}`);
}
- const isIndexRoute = routeId === 'index' || routeId.endsWith('/index');
- const fullPath = createRoutePath(routeId);
+ const isIndexRoute = fileId === 'index' || fileId.endsWith('/index');
+ const fullPath = createRoutePath(fileId);
const uniqueRouteId = (fullPath || '') + (isIndexRoute ? '?index' : '');
if (uniqueRouteId) {
@@ -186,33 +218,33 @@ function defineConventionalRoutes(
conflicts with route ${JSON.stringify(uniqueRoutes.get(uniqueRouteId))}`,
);
} else {
- uniqueRoutes.set(uniqueRouteId, routeId);
+ uniqueRoutes.set(uniqueRouteId, fileId);
}
}
if (isIndexRoute) {
- let invalidChildRoutes = routeIds.filter(
- (id) => findParentRouteId(routeIds, id) === routeId,
+ let invalidChildRoutes = fileIds.filter(
+ (id) => findParentFileId(fileIds, id) === fileId,
);
if (invalidChildRoutes.length > 0) {
throw new Error(
- `Child routes are not allowed in index routes. Please remove child routes of ${routeId}`,
+ `Child routes are not allowed in index routes. Please remove child routes of ${fileId}`,
);
}
- defineRoute(routePath, files[routeId], {
+ defineRoute(routePath, files[fileId], {
index: true,
});
} else {
- defineRoute(routePath, files[routeId], () => {
- defineNestedRoutes(defineRoute, routeId);
+ defineRoute(routePath, files[fileId], () => {
+ defineNestedRoutes(defineRoute, options, fileId);
});
}
}
}
- return defineRoutes(defineNestedRoutes);
+ return defineRoutes(defineNestedRoutes, options);
}
const escapeStart = '[';
@@ -284,7 +316,7 @@ export function createRoutePath(routeId: string): string | undefined {
return result || undefined;
}
-function findParentRouteId(
+function findParentFileId(
routeIds: string[],
childRouteId: string,
): string | undefined {
@@ -315,16 +347,3 @@ function visitFiles(
}
}
}
-
-/**
- * remove `/layout` str if the routeId has it
- *
- * 'layout' -> ''
- * 'About/layout' -> 'About'
- * 'About/layout/index' -> 'About/layout/index'
- */
-function removeLastLayoutStrFromId(id?: string) {
- const layoutStrs = ['/layout', 'layout'];
- const currentLayoutStr = layoutStrs.find(layoutStr => id?.endsWith(layoutStr));
- return currentLayoutStr ? id.slice(0, id.length - currentLayoutStr.length) : id;
-}
diff --git a/packages/route-manifest/src/routes.ts b/packages/route-manifest/src/routes.ts
index d4042fc26a..b74ce1efdc 100644
--- a/packages/route-manifest/src/routes.ts
+++ b/packages/route-manifest/src/routes.ts
@@ -1,6 +1,6 @@
// based on https://github.com/remix-run/remix/blob/main/packages/remix-dev/config/routes.ts
-import { win32 } from 'path';
+import { win32, join } from 'path';
export interface ConfigRoute {
/**
@@ -82,8 +82,19 @@ export interface NestedRouteManifest extends ConfigRoute {
children?: ConfigRoute[];
}
+export interface DefineRoutesOptions {
+ routeManifest: RouteManifest;
+ nestedRouteManifest: NestedRouteManifest[];
+}
+
+export type DefineExtraRoutes = (
+ defineRoute: DefineRouteFunction,
+ options: DefineRoutesOptions,
+) => void;
+
export function defineRoutes(
- callback: (defineRoute: DefineRouteFunction) => void,
+ callback: (defineRoute: DefineRouteFunction, options: DefineRoutesOptions) => void,
+ options: DefineRoutesOptions,
) {
const routes: RouteManifest = Object.create(null);
const parentRoutes: ConfigRoute[] = [];
@@ -108,18 +119,19 @@ export function defineRoutes(
// route(path, file, options)
options = optionsOrChildren || {};
}
+ const parentRoute = parentRoutes.length > 0
+ ? parentRoutes[parentRoutes.length - 1]
+ : undefined;
+
+ const id = createRouteId(file, path, parentRoute?.path, options.index);
- const id = createRouteId(file);
const route: ConfigRoute = {
path,
index: options.index ? true : undefined,
id,
- parentId:
- parentRoutes.length > 0
- ? parentRoutes[parentRoutes.length - 1].id
- : undefined,
+ parentId: parentRoute ? parentRoute.id : undefined,
file,
- componentName: createComponentName(id),
+ componentName: createComponentName(file),
layout: id.endsWith('layout'),
};
@@ -132,14 +144,27 @@ export function defineRoutes(
}
};
- callback(defineRoute);
+ callback(defineRoute, options);
alreadyReturned = true;
return routes;
}
-export function createRouteId(file: string) {
+export function createRouteId(
+ file: string,
+ path?: string,
+ parentPath?: string,
+ index?: boolean,
+) {
+ return normalizeSlashes(join(
+ parentPath || '',
+ path || (index ? '/' : ''),
+ stripFileExtension(file).endsWith('layout') ? 'layout' : '',
+ ));
+}
+
+export function createFileId(file: string) {
return normalizeSlashes(stripFileExtension(file));
}
@@ -151,8 +176,21 @@ function stripFileExtension(file: string) {
return file.replace(/\.[a-z0-9]+$/i, '');
}
-export function createComponentName(id: string) {
- return id.split('/')
+export function createComponentName(file: string) {
+ return createFileId(file).split('/')
.map((item: string) => item.toLowerCase())
.join('-');
}
+
+/**
+ * remove `/layout` str if the routeId has it
+ *
+ * 'layout' -> ''
+ * 'About/layout' -> 'About'
+ * 'About/layout/index' -> 'About/layout/index'
+ */
+export function removeLastLayoutStrFromId(id?: string) {
+ const layoutStrs = ['/layout', 'layout'];
+ const currentLayoutStr = layoutStrs.find(layoutStr => id?.endsWith(layoutStr));
+ return currentLayoutStr ? id.slice(0, id.length - currentLayoutStr.length) : id;
+}
diff --git a/packages/route-manifest/tests/__snapshots__/formatNestedRouteManifest.spec.ts.snap b/packages/route-manifest/tests/__snapshots__/formatNestedRouteManifest.spec.ts.snap
index b47291d7c1..fa7914c3fe 100644
--- a/packages/route-manifest/tests/__snapshots__/formatNestedRouteManifest.spec.ts.snap
+++ b/packages/route-manifest/tests/__snapshots__/formatNestedRouteManifest.spec.ts.snap
@@ -5,7 +5,7 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
{
"componentName": "blog-index",
"file": "blog/index.tsx",
- "id": "blog/index",
+ "id": "blog",
"index": true,
"layout": false,
"parentId": undefined,
@@ -14,7 +14,7 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
{
"componentName": "blog-$id",
"file": "blog/$id.tsx",
- "id": "blog/$id",
+ "id": "blog/:id",
"index": undefined,
"layout": false,
"parentId": undefined,
@@ -32,7 +32,7 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
{
"componentName": "index",
"file": "index.tsx",
- "id": "index",
+ "id": "/",
"index": true,
"layout": false,
"parentId": undefined,
diff --git a/packages/route-manifest/tests/__snapshots__/generateRouteManifest.spec.ts.snap b/packages/route-manifest/tests/__snapshots__/generateRouteManifest.spec.ts.snap
index 76f5523f4e..4c81670ac1 100644
--- a/packages/route-manifest/tests/__snapshots__/generateRouteManifest.spec.ts.snap
+++ b/packages/route-manifest/tests/__snapshots__/generateRouteManifest.spec.ts.snap
@@ -2,19 +2,28 @@
exports[`generateRouteManifest function > basic-routes 1`] = `
{
- "About/index": {
+ "/": {
+ "componentName": "index",
+ "file": "index.tsx",
+ "id": "/",
+ "index": true,
+ "layout": false,
+ "parentId": "layout",
+ "path": undefined,
+ },
+ "About": {
"componentName": "about-index",
"file": "About/index.tsx",
- "id": "About/index",
+ "id": "About",
"index": true,
"layout": false,
"parentId": "layout",
"path": "About",
},
- "About/me/index": {
+ "About/me": {
"componentName": "about-me-index",
"file": "About/me/index.tsx",
- "id": "About/me/index",
+ "id": "About/me",
"index": true,
"layout": false,
"parentId": "layout",
@@ -29,15 +38,6 @@ exports[`generateRouteManifest function > basic-routes 1`] = `
"parentId": "layout",
"path": "home",
},
- "index": {
- "componentName": "index",
- "file": "index.tsx",
- "id": "index",
- "index": true,
- "layout": false,
- "parentId": "layout",
- "path": undefined,
- },
"layout": {
"componentName": "layout",
"file": "layout.tsx",
@@ -52,19 +52,28 @@ exports[`generateRouteManifest function > basic-routes 1`] = `
exports[`generateRouteManifest function > define-extra-routes 1`] = `
{
- "About/index": {
+ "/": {
+ "componentName": "index",
+ "file": "index.tsx",
+ "id": "/",
+ "index": true,
+ "layout": false,
+ "parentId": "layout",
+ "path": undefined,
+ },
+ "/about-me": {
"componentName": "about-index",
"file": "About/index.tsx",
- "id": "About/index",
+ "id": "/about-me",
"index": undefined,
"layout": false,
"parentId": undefined,
"path": "/about-me",
},
- "About/me/index": {
+ "About/me": {
"componentName": "about-me-index",
"file": "About/me/index.tsx",
- "id": "About/me/index",
+ "id": "About/me",
"index": true,
"layout": false,
"parentId": "layout",
@@ -79,15 +88,6 @@ exports[`generateRouteManifest function > define-extra-routes 1`] = `
"parentId": "layout",
"path": "home",
},
- "index": {
- "componentName": "index",
- "file": "index.tsx",
- "id": "index",
- "index": true,
- "layout": false,
- "parentId": "layout",
- "path": undefined,
- },
"layout": {
"componentName": "layout",
"file": "layout.tsx",
@@ -102,10 +102,10 @@ exports[`generateRouteManifest function > define-extra-routes 1`] = `
exports[`generateRouteManifest function > doc-delimeters-routes 1`] = `
{
- "home.news": {
+ "home/news": {
"componentName": "home.news",
"file": "home.news.tsx",
- "id": "home.news",
+ "id": "home/news",
"index": undefined,
"layout": false,
"parentId": "layout",
@@ -125,6 +125,15 @@ exports[`generateRouteManifest function > doc-delimeters-routes 1`] = `
exports[`generateRouteManifest function > dynamic-routes 1`] = `
{
+ "/": {
+ "componentName": "index",
+ "file": "index.tsx",
+ "id": "/",
+ "index": true,
+ "layout": false,
+ "parentId": undefined,
+ "path": undefined,
+ },
"about": {
"componentName": "about",
"file": "about.tsx",
@@ -134,51 +143,42 @@ exports[`generateRouteManifest function > dynamic-routes 1`] = `
"parentId": undefined,
"path": "about",
},
- "blog/$id": {
- "componentName": "blog-$id",
- "file": "blog/$id.tsx",
- "id": "blog/$id",
- "index": undefined,
- "layout": false,
- "parentId": undefined,
- "path": "blog/:id",
- },
- "blog/index": {
+ "blog": {
"componentName": "blog-index",
"file": "blog/index.tsx",
- "id": "blog/index",
+ "id": "blog",
"index": true,
"layout": false,
"parentId": undefined,
"path": "blog",
},
- "index": {
- "componentName": "index",
- "file": "index.tsx",
- "id": "index",
- "index": true,
+ "blog/:id": {
+ "componentName": "blog-$id",
+ "file": "blog/$id.tsx",
+ "id": "blog/:id",
+ "index": undefined,
"layout": false,
"parentId": undefined,
- "path": undefined,
+ "path": "blog/:id",
},
}
`;
exports[`generateRouteManifest function > escape-routes 1`] = `
{
- "1[.pdf]": {
+ "1.pdf": {
"componentName": "1[.pdf]",
"file": "1[.pdf].tsx",
- "id": "1[.pdf]",
+ "id": "1.pdf",
"index": undefined,
"layout": false,
"parentId": undefined,
"path": "1.pdf",
},
- "[index]": {
+ "index": {
"componentName": "[index]",
"file": "[index].tsx",
- "id": "[index]",
+ "id": "index",
"index": undefined,
"layout": false,
"parentId": undefined,
@@ -189,10 +189,19 @@ exports[`generateRouteManifest function > escape-routes 1`] = `
exports[`generateRouteManifest function > ignore-routes 1`] = `
{
- "About/me/index": {
+ "/": {
+ "componentName": "index",
+ "file": "index.tsx",
+ "id": "/",
+ "index": true,
+ "layout": false,
+ "parentId": "layout",
+ "path": undefined,
+ },
+ "About/me": {
"componentName": "about-me-index",
"file": "About/me/index.tsx",
- "id": "About/me/index",
+ "id": "About/me",
"index": true,
"layout": false,
"parentId": "layout",
@@ -207,15 +216,6 @@ exports[`generateRouteManifest function > ignore-routes 1`] = `
"parentId": "layout",
"path": "home",
},
- "index": {
- "componentName": "index",
- "file": "index.tsx",
- "id": "index",
- "index": true,
- "layout": false,
- "parentId": "layout",
- "path": undefined,
- },
"layout": {
"componentName": "layout",
"file": "layout.tsx",
@@ -230,6 +230,15 @@ exports[`generateRouteManifest function > ignore-routes 1`] = `
exports[`generateRouteManifest function > layout-routes 1`] = `
{
+ "/": {
+ "componentName": "index",
+ "file": "index.tsx",
+ "id": "/",
+ "index": true,
+ "layout": false,
+ "parentId": "layout",
+ "path": undefined,
+ },
"about": {
"componentName": "about",
"file": "about.tsx",
@@ -239,24 +248,24 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
"parentId": "layout",
"path": "about",
},
- "blog/$id": {
- "componentName": "blog-$id",
- "file": "blog/$id.tsx",
- "id": "blog/$id",
- "index": undefined,
- "layout": false,
- "parentId": "blog/layout",
- "path": ":id",
- },
- "blog/index": {
+ "blog/": {
"componentName": "blog-index",
"file": "blog/index.tsx",
- "id": "blog/index",
+ "id": "blog/",
"index": true,
"layout": false,
"parentId": "blog/layout",
"path": undefined,
},
+ "blog/:id": {
+ "componentName": "blog-$id",
+ "file": "blog/$id.tsx",
+ "id": "blog/:id",
+ "index": undefined,
+ "layout": false,
+ "parentId": "blog/layout",
+ "path": ":id",
+ },
"blog/layout": {
"componentName": "blog-layout",
"file": "blog/layout.tsx",
@@ -266,42 +275,24 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
"parentId": "layout",
"path": "blog",
},
- "home/index": {
+ "home/": {
"componentName": "home-index",
"file": "home/index.tsx",
- "id": "home/index",
+ "id": "home/",
"index": true,
"layout": false,
"parentId": "home/layout",
"path": undefined,
},
"home/layout": {
- "componentName": "home-layout",
- "file": "home/layout.tsx",
- "id": "home/layout",
- "index": undefined,
- "layout": true,
- "parentId": "layout",
- "path": "home",
- },
- "home/layout/index": {
"componentName": "home-layout-index",
"file": "home/layout/index.tsx",
- "id": "home/layout/index",
+ "id": "home/layout",
"index": true,
- "layout": false,
+ "layout": true,
"parentId": "home/layout",
"path": "layout",
},
- "index": {
- "componentName": "index",
- "file": "index.tsx",
- "id": "index",
- "index": true,
- "layout": false,
- "parentId": "layout",
- "path": undefined,
- },
"layout": {
"componentName": "layout",
"file": "layout.tsx",
@@ -325,10 +316,10 @@ exports[`generateRouteManifest function > nested-routes 1`] = `
"parentId": undefined,
"path": "a/b/c",
},
- "d.e.f": {
+ "d/e/f": {
"componentName": "d.e.f",
"file": "d.e.f.tsx",
- "id": "d.e.f",
+ "id": "d/e/f",
"index": undefined,
"layout": false,
"parentId": undefined,
@@ -339,10 +330,10 @@ exports[`generateRouteManifest function > nested-routes 1`] = `
exports[`generateRouteManifest function > splat-routes 1`] = `
{
- "$": {
+ "*": {
"componentName": "$",
"file": "$.tsx",
- "id": "$",
+ "id": "*",
"index": undefined,
"layout": false,
"parentId": "layout",
diff --git a/packages/route-manifest/tests/__snapshots__/parseRoute.spec.ts.snap b/packages/route-manifest/tests/__snapshots__/parseRoute.spec.ts.snap
index 64a09399e9..0ed2631be3 100644
--- a/packages/route-manifest/tests/__snapshots__/parseRoute.spec.ts.snap
+++ b/packages/route-manifest/tests/__snapshots__/parseRoute.spec.ts.snap
@@ -5,7 +5,7 @@ exports[`parseRoute function > basic-routes 1`] = `
{
"componentName": "index",
"file": "index.tsx",
- "id": "index",
+ "id": "/",
"index": true,
"layout": false,
"parentId": undefined,
@@ -19,7 +19,7 @@ exports[`parseRoute function > nested layout 1`] = `
{
"componentName": "layout",
"file": "layout.tsx",
- "id": "layout",
+ "id": "/layout",
"index": undefined,
"layout": true,
"parentId": undefined,
@@ -28,37 +28,37 @@ exports[`parseRoute function > nested layout 1`] = `
{
"componentName": "home",
"file": "home.tsx",
- "id": "home",
+ "id": "/home",
"index": undefined,
"layout": true,
- "parentId": "layout",
+ "parentId": "/layout",
"path": "home",
},
{
"componentName": "home1",
"file": "home1.tsx",
- "id": "home1",
+ "id": "/home/1",
"index": undefined,
"layout": false,
- "parentId": "home",
+ "parentId": "/home",
"path": "1",
},
{
"componentName": "home2",
"file": "home2.tsx",
- "id": "home2",
+ "id": "/home/2",
"index": undefined,
"layout": false,
- "parentId": "home",
+ "parentId": "/home",
"path": "2",
},
{
"componentName": "about",
"file": "about.tsx",
- "id": "about",
+ "id": "/about",
"index": undefined,
"layout": false,
- "parentId": "layout",
+ "parentId": "/layout",
"path": "about",
},
]
@@ -69,7 +69,7 @@ exports[`parseRoute function > with layout 1`] = `
{
"componentName": "layout",
"file": "layout.tsx",
- "id": "layout",
+ "id": "/layout",
"index": undefined,
"layout": true,
"parentId": undefined,
@@ -78,28 +78,28 @@ exports[`parseRoute function > with layout 1`] = `
{
"componentName": "home",
"file": "home.tsx",
- "id": "home",
+ "id": "/home",
"index": undefined,
"layout": false,
- "parentId": "layout",
+ "parentId": "/layout",
"path": "home",
},
{
"componentName": "about",
"file": "about.tsx",
- "id": "about",
+ "id": "/about",
"index": undefined,
"layout": false,
- "parentId": "layout",
+ "parentId": "/layout",
"path": "about",
},
{
"componentName": "index",
"file": "index.tsx",
- "id": "index",
+ "id": "/",
"index": true,
"layout": false,
- "parentId": "layout",
+ "parentId": "/layout",
"path": "/",
},
]
diff --git a/packages/route-manifest/tests/generateRouteManifest.spec.ts b/packages/route-manifest/tests/generateRouteManifest.spec.ts
index 36c7ff4672..d42ac858b5 100644
--- a/packages/route-manifest/tests/generateRouteManifest.spec.ts
+++ b/packages/route-manifest/tests/generateRouteManifest.spec.ts
@@ -45,9 +45,9 @@ describe('generateRouteManifest function', () => {
const routeManifest = generateRouteManifest(
path.join(fixturesDir, 'basic-routes'),
['About/index.tsx'],
- (defineRoute) => {
+ [(defineRoute) => {
defineRoute('/about-me', 'About/index.tsx');
- },
+ }],
);
expect(routeManifest).toMatchSnapshot();
});
diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx
index 706406baf7..303b2e0417 100644
--- a/packages/runtime/src/runServerApp.tsx
+++ b/packages/runtime/src/runServerApp.tsx
@@ -1,4 +1,4 @@
-import type { ServerResponse } from 'http';
+import type { ServerResponse, IncomingMessage } from 'http';
import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { parsePath } from 'react-router-dom';
@@ -55,9 +55,11 @@ interface Piper {
pipe: NodeWritablePiper;
fallback: Function;
}
-interface RenderResult {
+interface Response {
statusCode?: number;
+ statusText?: string;
value?: string | Piper;
+ headers?: Record;
}
/**
@@ -95,11 +97,11 @@ export async function renderToEntry(
export async function renderToHTML(
requestContext: ServerContext,
renderOptions: RenderOptions,
-): Promise {
+): Promise {
const result = await doRender(requestContext, renderOptions);
const { value } = result;
- if (typeof value === 'string') {
+ if (typeof value === 'string' || typeof value === 'undefined') {
return result;
}
@@ -110,6 +112,9 @@ export async function renderToHTML(
return {
value: entryStr,
+ headers: {
+ 'Content-Type': 'text/html; charset=utf-8',
+ },
statusCode: 200,
};
} catch (error) {
@@ -127,13 +132,13 @@ export async function renderToHTML(
* Render and send the result to ServerResponse.
*/
export async function renderToResponse(requestContext: ServerContext, renderOptions: RenderOptions) {
- const { res } = requestContext;
+ const { req, res } = requestContext;
const result = await doRender(requestContext, renderOptions);
const { value } = result;
- if (typeof value === 'string') {
- sendResult(res, result);
+ if (typeof value === 'string' || typeof value === 'undefined') {
+ sendResponse(req, res, result);
} else {
const { pipe, fallback } = value;
@@ -152,7 +157,7 @@ export async function renderToResponse(requestContext: ServerContext, renderOpti
console.error('PipeToResponse onShellError, downgrade to CSR.');
console.error(err);
const result = await fallback();
- sendResult(res, result);
+ sendResponse(req, res, result);
resolve();
},
onError: async (err) => {
@@ -169,21 +174,29 @@ export async function renderToResponse(requestContext: ServerContext, renderOpti
}
}
-/**
- * Send string result to ServerResponse.
- */
-async function sendResult(res: ServerResponse, result: RenderResult) {
- res.statusCode = result.statusCode;
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
- res.end(result.value);
+async function sendResponse(
+ req: IncomingMessage,
+ res: ServerResponse,
+ response: Response,
+) {
+ res.statusCode = response.statusCode;
+ res.statusMessage = response.statusText;
+ Object.entries(response.headers || {}).forEach(([name, value]) => {
+ res.setHeader(name, value);
+ });
+ if (response.value && req.method !== 'HEAD') {
+ res.end(response.value);
+ } else {
+ res.end();
+ }
}
function needRevalidate(matchedRoutes: RouteMatch[]) {
return matchedRoutes.some(({ route }) => route.exports.includes('dataLoader') && route.exports.includes('staticDataLoader'));
}
-async function doRender(serverContext: ServerContext, renderOptions: RenderOptions): Promise {
- const { req } = serverContext;
+async function doRender(serverContext: ServerContext, renderOptions: RenderOptions): Promise {
+ const { req, res } = serverContext;
const {
app,
basename,
@@ -226,6 +239,7 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
if (runtimeModules.statics) {
await Promise.all(runtimeModules.statics.map(m => runtime.loadModule(m)).filter(Boolean));
}
+
// don't need to execute getAppData in CSR
if (!documentOnly) {
try {
@@ -245,7 +259,7 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
if (documentOnly) {
return renderDocument({ matches, routePath, routes, renderOptions });
} else if (!matches.length) {
- return render404();
+ return handleNotFoundResponse();
}
try {
@@ -266,6 +280,31 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
if (runtimeModules.commons) {
await Promise.all(runtimeModules.commons.map(m => runtime.loadModule(m)).filter(Boolean));
}
+ /**
+ Plugin may register response handlers, for example:
+ ```
+ addResponseHandler((req) => {
+ if (redirect) {
+ return {
+ statusCode: 302,
+ statusText: 'Found',
+ headers: {
+ location: '/redirect',
+ },
+ };
+ }
+ });
+ ```
+ */
+ const responseHandlers = runtime.getResponseHandlers();
+ for (const responseHandler of responseHandlers) {
+ if (typeof responseHandler === 'function') {
+ const response = await responseHandler(req, res);
+ if (response) {
+ return response as Response;
+ }
+ }
+ }
return await renderServerEntry({
runtime,
@@ -283,9 +322,9 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
}
// https://github.com/ice-lab/ice-next/issues/133
-function render404(): RenderResult {
+function handleNotFoundResponse(): Response {
return {
- value: 'Not Found',
+ statusText: 'Not Found',
statusCode: 404,
};
}
@@ -306,7 +345,7 @@ async function renderServerEntry(
location,
renderOptions,
}: RenderServerEntry,
-): Promise {
+): Promise {
const { Document } = renderOptions;
const appContext = runtime.getAppContext();
const { routes, routePath, loaderData, basename } = appContext;
@@ -356,7 +395,7 @@ interface RenderDocumentOptions {
/**
* Render Document for CSR.
*/
-function renderDocument(options: RenderDocumentOptions): RenderResult {
+function renderDocument(options: RenderDocumentOptions): Response {
const {
matches,
renderOptions,
@@ -415,6 +454,9 @@ function renderDocument(options: RenderDocumentOptions): RenderResult {
return {
value: `${htmlStr}`,
+ headers: {
+ 'Content-Type': 'text/html; charset=utf-8',
+ },
statusCode: 200,
};
}
diff --git a/packages/runtime/src/runtime.tsx b/packages/runtime/src/runtime.tsx
index a5b24aaeab..e12636a3c5 100644
--- a/packages/runtime/src/runtime.tsx
+++ b/packages/runtime/src/runtime.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import type { ComponentType } from 'react';
+import { routerHistory as history } from './history.js';
import type {
Renderer,
AppContext,
@@ -15,6 +16,7 @@ import type {
SetRender,
AppRouterProps,
ComponentWithChildren,
+ ResponseHandler,
} from './types.js';
import { useData, useConfig } from './RouteContext.js';
import { useAppContext } from './AppContext.js';
@@ -32,6 +34,8 @@ class Runtime {
private render: Renderer;
+ private responseHandlers: ResponseHandler[];
+
public constructor(appContext: AppContext, runtimeOptions?: Record) {
this.AppProvider = [];
this.appContext = appContext;
@@ -42,6 +46,7 @@ class Runtime {
};
this.RouteWrappers = [];
this.runtimeOptions = runtimeOptions;
+ this.responseHandlers = [];
}
public getAppContext = () => {
@@ -66,6 +71,8 @@ class Runtime {
public async loadModule(module: RuntimePlugin | StaticRuntimePlugin | CommonJsRuntime) {
let runtimeAPI: RuntimeAPI = {
addProvider: this.addProvider,
+ addResponseHandler: this.addResponseHandler,
+ getResponseHandlers: this.getResponseHandlers,
getAppRouter: this.getAppRouter,
setRender: this.setRender,
addWrapper: this.addWrapper,
@@ -74,6 +81,7 @@ class Runtime {
useData,
useConfig,
useAppContext,
+ history,
};
const runtimeModule = ((module as CommonJsRuntime).default || module) as RuntimePlugin;
@@ -113,6 +121,14 @@ class Runtime {
public setAppRouter: SetAppRouter = (AppRouter) => {
this.AppRouter = AppRouter;
};
+
+ public addResponseHandler = (handler: ResponseHandler) => {
+ this.responseHandlers.push(handler);
+ };
+
+ public getResponseHandlers = () => {
+ return this.responseHandlers;
+ };
}
export default Runtime;
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index 138fcd5468..c1250b114c 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -1,5 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'http';
-import type { InitialEntry, AgnosticRouteObject, Location } from '@remix-run/router';
+import type { InitialEntry, AgnosticRouteObject, Location, History } from '@remix-run/router';
import type { ComponentType, PropsWithChildren } from 'react';
import type { HydrationOptions, Root } from 'react-dom/client';
import type { Params, RouteObject } from 'react-router-dom';
@@ -154,12 +154,18 @@ export interface RouteWrapperConfig {
export type AppProvider = ComponentWithChildren;
export type RouteWrapper = ComponentType;
+export type ResponseHandler = (
+ req: IncomingMessage,
+ res: ServerResponse,
+) => any | Promise;
export type SetAppRouter = (AppRouter: ComponentType) => void;
export type GetAppRouter = () => AppProvider;
export type AddProvider = (Provider: AppProvider) => void;
export type SetRender = (render: Renderer) => void;
export type AddWrapper = (wrapper: RouteWrapper, forLayout?: boolean) => void;
+export type AddResponseHandler = (handler: ResponseHandler) => void;
+export type GetResponseHandlers = () => ResponseHandler[];
export interface RouteModules {
[routeId: string]: ComponentModule;
@@ -183,12 +189,15 @@ export interface RuntimeAPI {
setAppRouter?: SetAppRouter;
getAppRouter: GetAppRouter;
addProvider: AddProvider;
+ addResponseHandler: AddResponseHandler;
+ getResponseHandlers: GetResponseHandlers;
setRender: SetRender;
addWrapper: AddWrapper;
appContext: AppContext;
useData: UseData;
useConfig: UseConfig;
useAppContext: UseAppContext;
+ history: History;
}
export interface StaticRuntimeAPI {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1dcaf5c4f4..fd7d4fd3e4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -475,6 +475,29 @@ importers:
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
+ examples/with-antd5:
+ specifiers:
+ '@ice/app': workspace:*
+ '@ice/runtime': workspace:*
+ '@types/react': ^18.0.0
+ '@types/react-dom': ^18.0.2
+ antd: ^5.0.0
+ dayjs: ^1.11.7
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ react-intl: ^6.3.2
+ dependencies:
+ '@ice/app': link:../../packages/ice
+ '@ice/runtime': link:../../packages/runtime
+ antd: 5.4.1_biqbaboplfbrettd7655fr4n2y
+ dayjs: 1.11.7
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ react-intl: 6.3.2_react@18.2.0
+ devDependencies:
+ '@types/react': 18.0.34
+ '@types/react-dom': 18.0.11
+
examples/with-auth:
specifiers:
'@ice/app': workspace:*
@@ -541,12 +564,14 @@ importers:
moment: ^2.29.4
react: ^18.0.0
react-dom: ^18.0.0
+ react-intl: ^6.3.2
dependencies:
'@alifd/next': 1.26.2_jb42yyeu5qxbfieyxjks6malva
'@ice/runtime': link:../../packages/runtime
moment: 2.29.4
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
+ react-intl: 6.3.2_react@18.2.0
devDependencies:
'@ice/app': link:../../packages/ice
'@ice/plugin-css-assets-local': link:../../packages/plugin-css-assets-local
@@ -555,6 +580,35 @@ importers:
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
+ examples/with-i18n:
+ specifiers:
+ '@ice/app': workspace:*
+ '@ice/plugin-i18n': workspace:*
+ '@ice/runtime': workspace:*
+ '@types/express': ^4.17.14
+ '@types/react': ^18.0.0
+ '@types/react-dom': ^18.0.2
+ express: ^4.17.3
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ react-intl: ^6.3.2
+ tslib: ^2.5.0
+ tsx: ^3.12.1
+ dependencies:
+ '@ice/runtime': link:../../packages/runtime
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ react-intl: 6.3.2_react@18.2.0
+ devDependencies:
+ '@ice/app': link:../../packages/ice
+ '@ice/plugin-i18n': link:../../packages/plugin-i18n
+ '@types/express': 4.17.17
+ '@types/react': 18.0.34
+ '@types/react-dom': 18.0.11
+ express: 4.18.2
+ tslib: 2.5.0
+ tsx: 3.12.3
+
examples/with-jest:
specifiers:
'@ice/app': workspace:*
@@ -1191,6 +1245,35 @@ importers:
devDependencies:
'@ice/app': link:../ice
+ packages/plugin-i18n:
+ specifiers:
+ '@ice/app': workspace:^
+ '@ice/jsx-runtime': ^0.2.0
+ '@ice/pkg': ^1.0.0
+ '@ice/runtime': workspace:^
+ '@remix-run/router': ^1.5.0
+ '@swc/helpers': ^0.4.14
+ '@types/accept-language-parser': ^1.5.3
+ '@types/react': ^18.0.33
+ accept-language-parser: ^1.5.0
+ universal-cookie: ^4.0.4
+ url-join: ^5.0.0
+ webpack-dev-server: ^4.13.2
+ dependencies:
+ '@ice/jsx-runtime': link:../jsx-runtime
+ '@swc/helpers': 0.4.14
+ accept-language-parser: 1.5.0
+ universal-cookie: 4.0.4
+ url-join: 5.0.0
+ devDependencies:
+ '@ice/app': link:../ice
+ '@ice/pkg': 1.5.5
+ '@ice/runtime': link:../runtime
+ '@remix-run/router': 1.5.0
+ '@types/accept-language-parser': 1.5.3
+ '@types/react': 18.0.34
+ webpack-dev-server: 4.13.2
+
packages/plugin-icestark:
specifiers:
'@ice/app': ^3.1.5
@@ -1351,8 +1434,8 @@ importers:
packages/plugin-store:
specifiers:
- '@ice/app': ^3.1.2
- '@ice/runtime': ^1.1.3
+ '@ice/app': workspace:^
+ '@ice/runtime': workspace:^
'@ice/store': ^2.0.3
'@types/micromatch': ^4.0.2
'@types/react': ^18.0.0
@@ -1717,6 +1800,29 @@ packages:
'@ctrl/tinycolor': 3.6.0
dev: false
+ /@ant-design/colors/7.0.0:
+ resolution: {integrity: sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg==}
+ dependencies:
+ '@ctrl/tinycolor': 3.6.0
+ dev: false
+
+ /@ant-design/cssinjs/1.8.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-pOQJV9H9viB6qB9u7hkpKEOIQGx4dd8zjpwzF1v8YNwjffbZTlyUNQYln56gwpFF7SFskpYpnSfgoqTK4sFE/Q==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@emotion/hash': 0.8.0
+ '@emotion/unitless': 0.7.5
+ classnames: 2.3.2
+ csstype: 3.1.1
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ stylis: 4.1.3
+ dev: false
+
/@ant-design/icons-svg/4.2.1:
resolution: {integrity: sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==}
dev: false
@@ -1737,6 +1843,22 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /@ant-design/icons/5.0.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-ZyF4ksXCcdtwA/1PLlnFLcF/q8/MhwxXhKHh4oCHDA4Ip+ZzAHoICtyp4wZWfiCVDP0yuz3HsjyvuldHFb3wjA==}
+ engines: {node: '>=8'}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@ant-design/colors': 7.0.0
+ '@ant-design/icons-svg': 4.2.1
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/@ant-design/react-slick/0.29.2_react@18.2.0:
resolution: {integrity: sha512-kgjtKmkGHa19FW21lHnAfyyH9AAoh35pBdcJ53rHmQ3O+cfFHGHnUbj/HFrRNJ5vIts09FKJVAD8RpaC+RaWfA==}
peerDependencies:
@@ -1750,6 +1872,19 @@ packages:
resize-observer-polyfill: 1.5.1
dev: false
+ /@ant-design/react-slick/1.0.0_react@18.2.0:
+ resolution: {integrity: sha512-OKxZsn8TAf8fYxP79rDXgLs9zvKMTslK6dJ4iLhDXOujUqC5zJPBRszyrcEHXcMPOm1Sgk40JgyF3yiL/Swd7w==}
+ peerDependencies:
+ react: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ json2mq: 0.2.0
+ react: 18.2.0
+ resize-observer-polyfill: 1.5.1
+ throttle-debounce: 5.0.0
+ dev: false
+
/@applint/commitlint-config/1.0.2:
resolution: {integrity: sha512-Je7RbW+7sQSLRq3Axfv8rPvO3SBRSgJVeYhNLisMuBDMNx/VWoFV4aX7ozZW5MUmvdFv8fVc1TNtyi5MfNflmg==}
dependencies:
@@ -2054,7 +2189,7 @@ packages:
resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.21.2
+ '@babel/types': 7.18.10
/@babel/helper-plugin-utils/7.10.4:
resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==}
@@ -3769,7 +3904,7 @@ packages:
wait-on: 6.0.1_debug@4.3.4
webpack: 5.76.2
webpack-bundle-analyzer: 4.8.0
- webpack-dev-server: 4.11.1_debug@4.3.4+webpack@5.76.2
+ webpack-dev-server: 4.13.2_debug@4.3.4+webpack@5.76.2
webpack-merge: 5.8.0
webpackbar: 5.0.2_webpack@5.76.2
transitivePeerDependencies:
@@ -3869,7 +4004,7 @@ packages:
wait-on: 6.0.1
webpack: 5.76.2
webpack-bundle-analyzer: 4.8.0
- webpack-dev-server: 4.11.1_webpack@5.76.2
+ webpack-dev-server: 4.13.2_webpack@5.76.2
webpack-merge: 5.8.0
webpackbar: 5.0.2_webpack@5.76.2
transitivePeerDependencies:
@@ -3913,8 +4048,8 @@ packages:
react: ^16.8.4 || ^17.0.0
react-dom: ^16.8.4 || ^17.0.0
dependencies:
- '@babel/parser': 7.21.2
- '@babel/traverse': 7.21.2
+ '@babel/parser': 7.18.10
+ '@babel/traverse': 7.18.10
'@docusaurus/logger': 2.3.1
'@docusaurus/utils': 2.3.1_@docusaurus+types@2.3.1
'@mdx-js/mdx': 1.6.22
@@ -3949,7 +4084,7 @@ packages:
'@docusaurus/react-loadable': 5.5.2_react@17.0.2
'@docusaurus/types': 2.3.1_sfoxds7t5ydpegc3knd667wn6m
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
'@types/react-router-config': 5.0.6
'@types/react-router-dom': 5.3.3
react: 17.0.2
@@ -4335,7 +4470,7 @@ packages:
peerDependencies:
react: '*'
dependencies:
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
prop-types: 15.8.1
react: 17.0.2
@@ -4405,7 +4540,7 @@ packages:
'@docusaurus/plugin-content-pages': 2.3.1_jgxnvbe4faw3ohf4h6p42qq6oy
'@docusaurus/utils': 2.3.1_@docusaurus+types@2.3.1
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
'@types/react-router-config': 5.0.6
clsx: 1.2.1
parse-numeric-range: 1.3.0
@@ -4494,7 +4629,7 @@ packages:
react-dom: ^16.8.4 || ^17.0.0
dependencies:
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
commander: 5.1.0
joi: 17.8.3
react: 17.0.2
@@ -4623,6 +4758,14 @@ packages:
- webpack-cli
dev: false
+ /@emotion/hash/0.8.0:
+ resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
+ dev: false
+
+ /@emotion/unitless/0.7.5:
+ resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
+ dev: false
+
/@esbuild-kit/cjs-loader/2.4.2:
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
dependencies:
@@ -5077,6 +5220,73 @@ packages:
'@floating-ui/core': 1.2.2
dev: false
+ /@formatjs/ecma402-abstract/1.14.3:
+ resolution: {integrity: sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==}
+ dependencies:
+ '@formatjs/intl-localematcher': 0.2.32
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/fast-memoize/2.0.1:
+ resolution: {integrity: sha512-M2GgV+qJn5WJQAYewz7q2Cdl6fobQa69S1AzSM2y0P68ZDbK5cWrJIcPCO395Of1ksftGZoOt4LYCO/j9BKBSA==}
+ dependencies:
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/icu-messageformat-parser/2.3.0:
+ resolution: {integrity: sha512-xqtlqYAbfJDF4b6e4O828LBNOWXrFcuYadqAbYORlDRwhyJ2bH+xpUBPldZbzRGUN2mxlZ4Ykhm7jvERtmI8NQ==}
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/icu-skeleton-parser': 1.3.18
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/icu-skeleton-parser/1.3.18:
+ resolution: {integrity: sha512-ND1ZkZfmLPcHjAH1sVpkpQxA+QYfOX3py3SjKWMUVGDow18gZ0WPqz3F+pJLYQMpS2LnnQ5zYR2jPVYTbRwMpg==}
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/intl-displaynames/6.2.6:
+ resolution: {integrity: sha512-scf5AQTk9EjpvPhboo5sizVOvidTdMOnajv9z+0cejvl7JNl9bl/aMrNBgC72UH+bP3l45usPUKAGskV6sNIrA==}
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/intl-localematcher': 0.2.32
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/intl-listformat/7.1.9:
+ resolution: {integrity: sha512-5YikxwRqRXTVWVujhswDOTCq6gs+m9IcNbNZLa6FLtyBStAjEsuE2vAU+lPsbz9ZTST57D5fodjIh2JXT6sMWQ==}
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/intl-localematcher': 0.2.32
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/intl-localematcher/0.2.32:
+ resolution: {integrity: sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==}
+ dependencies:
+ tslib: 2.5.0
+ dev: false
+
+ /@formatjs/intl/2.6.9:
+ resolution: {integrity: sha512-EtcMZ9O24YSASu/jGOaTQtArx7XROjlKiO4KmkxJ/3EyAQLCr5hrS+KKvNud0a7GIwBucOb3IFrZ7WiSm2A/Cw==}
+ peerDependencies:
+ typescript: ^4.7
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/fast-memoize': 2.0.1
+ '@formatjs/icu-messageformat-parser': 2.3.0
+ '@formatjs/intl-displaynames': 6.2.6
+ '@formatjs/intl-listformat': 7.1.9
+ intl-messageformat: 10.3.3
+ tslib: 2.5.0
+ dev: false
+
/@hapi/hoek/9.3.0:
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@@ -5149,6 +5359,48 @@ packages:
- ts-node
dev: true
+ /@ice/pkg/1.5.5:
+ resolution: {integrity: sha512-0BIfv6Uzs2wpHv7RmFwz+kWfoJLfx0yJrQyh3yqy+F6TZWxTwrqQmX+5yRmgqK5f7lGGhYfMMVNWjRSCw5MHPQ==}
+ engines: {node: '>=16.14.0'}
+ hasBin: true
+ dependencies:
+ '@ampproject/remapping': 2.2.0
+ '@babel/core': 7.21.0
+ '@babel/parser': 7.18.10
+ '@babel/preset-react': 7.18.6_@babel+core@7.21.0
+ '@babel/preset-typescript': 7.21.0_@babel+core@7.21.0
+ '@rollup/plugin-commonjs': 21.1.0_rollup@2.79.1
+ '@rollup/plugin-image': 3.0.2_rollup@2.79.1
+ '@rollup/plugin-json': 4.1.0_rollup@2.79.1
+ '@rollup/plugin-node-resolve': 13.3.0_rollup@2.79.1
+ '@rollup/plugin-replace': 5.0.2_rollup@2.79.1
+ '@rollup/pluginutils': 4.2.1
+ '@swc/core': 1.3.32
+ acorn: 8.8.2
+ autoprefixer: 10.4.13_postcss@8.4.21
+ build-scripts: 2.1.0
+ cac: 6.7.14
+ chokidar: 3.5.3
+ consola: 2.15.3
+ debug: 4.3.4
+ deepmerge: 4.3.0
+ escape-string-regexp: 5.0.0
+ fs-extra: 10.1.0
+ globby: 11.1.0
+ gzip-size: 7.0.0
+ lodash.merge: 4.6.2
+ magic-string: 0.25.9
+ picocolors: 1.0.0
+ postcss: 8.4.21
+ rollup: 2.79.1
+ rollup-plugin-styles: 4.0.0_rollup@2.79.1
+ rollup-plugin-visualizer: 5.9.0_rollup@2.79.1
+ tsc-alias: 1.8.5
+ typescript: 4.9.5
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@ice/sandbox/1.1.4:
resolution: {integrity: sha512-MEVF0Ze3McKDutnFiUAhUoc+WwOFxITVBgSSHmbGpKtWbXJX9kUVlx3VsEVJvdqU3O1kiBNx6zE1sFMjKPRTIQ==}
dev: false
@@ -5967,6 +6219,18 @@ packages:
/@polka/url/1.0.0-next.21:
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
+ /@rc-component/context/1.3.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-6QdaCJ7Wn5UZLJs15IEfqy4Ru3OaL5ctqpQYWd5rlfV9wwzrzdt6+kgAQZV/qdB0MUPN4nhyBfRembQCIvBf+w==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ rc-util: 5.29.3_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/@rc-component/mini-decimal/1.0.1:
resolution: {integrity: sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA==}
engines: {node: '>=8.x'}
@@ -5974,6 +6238,20 @@ packages:
'@babel/runtime': 7.21.0
dev: false
+ /@rc-component/mutate-observer/1.0.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-okqRJSfNisXdI6CUeOLZC5ukBW/8kir2Ii4PJiKpUt+3+uS7dxwJUMxsUZquxA1rQuL8YcEmKVp/TCnR+yUdZA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/@rc-component/portal/1.1.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-tbXM9SB1r5FOuZjRCljERFByFiEUcMmCWMXLog/NmgCzlAzreXyf23Vei3ZpSMxSMavzPnhCovfZjZdmxS3d1w==}
engines: {node: '>=8.x'}
@@ -5988,6 +6266,40 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /@rc-component/tour/1.8.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-rrRGioHTLQlGca27G2+lw7QpRb3uuMYCUIJjj31/B44VCJS0P2tqYhOgtzvWQmaLMlWH3ZlpzotkKX13NT4XEA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/portal': 1.1.0_biqbaboplfbrettd7655fr4n2y
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
+ /@rc-component/trigger/1.8.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-O9d4Tlg/FiCUlkQ+aAUUO5KmrBbj4XYq6qYfZE/hvNHzIepHqwLGx8H/d+1fG13dVPq70nGDf5ha9PQ96YRMVg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/portal': 1.1.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-align: 4.0.15_biqbaboplfbrettd7655fr4n2y
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-resize-observer: 1.3.1_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.29.3_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/@react-spring/animated/9.6.1_react@18.2.0:
resolution: {integrity: sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==}
peerDependencies:
@@ -6104,6 +6416,29 @@ packages:
rollup: 2.79.1
dev: true
+ /@rollup/plugin-image/3.0.2_rollup@2.79.1:
+ resolution: {integrity: sha512-eGVrD6lummWH5ENo9LWX3JY62uBb9okUNQ2htXkugrG6WjACrMUVhWvss+0wW3fwJWmFYpoEny3yL4spEdh15g==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 5.0.2_rollup@2.79.1
+ mini-svg-data-uri: 1.4.4
+ rollup: 2.79.1
+ dev: true
+
+ /@rollup/plugin-json/4.1.0_rollup@2.79.1:
+ resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
+ peerDependencies:
+ rollup: ^1.20.0 || ^2.0.0
+ dependencies:
+ '@rollup/pluginutils': 3.1.0_rollup@2.79.1
+ rollup: 2.79.1
+ dev: true
+
/@rollup/plugin-node-resolve/13.3.0_rollup@2.79.1:
resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==}
engines: {node: '>= 10.0.0'}
@@ -6119,6 +6454,20 @@ packages:
rollup: 2.79.1
dev: true
+ /@rollup/plugin-replace/5.0.2_rollup@2.79.1:
+ resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 5.0.2_rollup@2.79.1
+ magic-string: 0.27.0
+ rollup: 2.79.1
+ dev: true
+
/@rollup/pluginutils/3.1.0_rollup@2.79.1:
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
@@ -6138,12 +6487,27 @@ packages:
estree-walker: 2.0.2
picomatch: 2.3.1
- /@sideway/address/4.1.4:
- resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
+ /@rollup/pluginutils/5.0.2_rollup@2.79.1:
+ resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
dependencies:
- '@hapi/hoek': 9.3.0
-
- /@sideway/formula/3.0.1:
+ '@types/estree': 1.0.0
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ rollup: 2.79.1
+ dev: true
+
+ /@sideway/address/4.1.4:
+ resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
+ dependencies:
+ '@hapi/hoek': 9.3.0
+
+ /@sideway/formula/3.0.1:
resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
/@sideway/pinpoint/2.0.0:
@@ -6448,6 +6812,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-darwin-arm64/1.3.32:
+ resolution: {integrity: sha512-o19bhlxuUgjUElm6i+QhXgZ0vD6BebiB/gQpK3en5aAwhOvinwr4sah3GqFXsQzz/prKVDuMkj9SW6F/Ug5hgg==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-darwin-x64/1.3.19:
resolution: {integrity: sha512-qCDQcngYBeWrsNS1kcBslRD0dahKcYKaUUWRC9yHpRcs3SRvnSpJyWQR4y9RCdO9YNmixJ9+5+zPD9qcgL7jBw==}
engines: {node: '>=10'}
@@ -6456,6 +6829,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-darwin-x64/1.3.32:
+ resolution: {integrity: sha512-hVEGd+v5Afh+YekGADOGKwhuS4/AXk91nLuk7pmhWkk8ceQ1cfmah90kXjIXUlCe2G172MLRfHNWlZxr29E/Og==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm-gnueabihf/1.3.19:
resolution: {integrity: sha512-ufbKW6Lhii1+kVCXnsHgqYIpRvXhPjdhMudfP4KKVgJtT6TsdEIr+KRAQIBHLjRUsTKA2DLsGEpu9jfjwFiNEg==}
engines: {node: '>=10'}
@@ -6464,6 +6846,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm-gnueabihf/1.3.32:
+ resolution: {integrity: sha512-5X01WqI9EbJ69oHAOGlI08YqvEIXMfT/mCJ1UWDQBb21xWRE2W1yFAAeuqOLtiagLrXjPv/UKQ0S2gyWQR5AXQ==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm64-gnu/1.3.19:
resolution: {integrity: sha512-HHhqLRZv9Ss8orJrlEP4XRcLuqLDwFtGgbtHU8kyWBmQEtK42uT18Pf5RJBo5sPJHY8m5EO8C8y3hIbGmKtLyg==}
engines: {node: '>=10'}
@@ -6472,6 +6863,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm64-gnu/1.3.32:
+ resolution: {integrity: sha512-PTJ6oPiutkNBg+m22bUUPa4tNuMmsgpSnsnv2wnWVOgK0lhvQT6bAPTUXDq/8peVAgR/SlpP2Ht8TRRqYMRjRQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm64-musl/1.3.19:
resolution: {integrity: sha512-vipnF3C6T1368uHQqz8RpdszWxxGh0X8VBK3TdTOSWvI/duNZtZXEOZlB2Nh9w+u09umVw0MsJhvg86Aon39mA==}
engines: {node: '>=10'}
@@ -6480,6 +6880,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm64-musl/1.3.32:
+ resolution: {integrity: sha512-lG0VOuYNPWOCJ99Aza69cTljjeft/wuRQeYFF8d+1xCQS/OT7gnbgi7BOz39uSHIPTBqfzdIsuvzdKlp9QydrQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-x64-gnu/1.3.19:
resolution: {integrity: sha512-dUbq8mnIqBhU7OppfY3ncOvl26691WFGxd97QtnnlfMZrKnaofKFMIxE9sTHOLSbBo16AylnEMiwa45w2UWDEg==}
engines: {node: '>=10'}
@@ -6488,6 +6897,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-x64-gnu/1.3.32:
+ resolution: {integrity: sha512-ecqtSWX4NBrs7Ji2VX3fDWeqUfrbLlYqBuufAziCM27xMxwlAVgmyGQk4FYgoQ3SAUAu3XFH87+3Q7uWm2X7xg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-x64-musl/1.3.19:
resolution: {integrity: sha512-RiVZrlkNGcj9jZyjF7YFOW3fj9fWPC25AYkknLpWxAmLQcp1piAWj+aSixmMWUC4QJau78VZzcm+kRgIOECALw==}
engines: {node: '>=10'}
@@ -6496,6 +6914,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-x64-musl/1.3.32:
+ resolution: {integrity: sha512-rl3dMcUuENVkpk5NGW/LXovjK0+JFm4GWPjy4NM3Q5cPvhBpGwSeLZlR+zAw9K0fdGoIXiayRTTfENrQwwsH+g==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-arm64-msvc/1.3.19:
resolution: {integrity: sha512-r2U6GC+go2iiLx5JBZIJswYFiMv0yOsm+pgE1srVvAc8dP02320t9yh0Uj4Sr2hDipTWJ33Y5PMZwEsZSfBVbQ==}
engines: {node: '>=10'}
@@ -6504,6 +6931,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-arm64-msvc/1.3.32:
+ resolution: {integrity: sha512-VlybAZp8DcS66CH1LDnfp9zdwbPlnGXREtHDMHaBfK9+80AWVTg+zn0tCYz+HfcrRONqxbudwOUIPj+dwl/8jw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-ia32-msvc/1.3.19:
resolution: {integrity: sha512-SPpESDa4vr0PRvUiqXSi8oZSTmkDOGrZ/pSiLD7ISgjsQ5RQMbPkuEK0ztWljim87q2fO0bGVVhyaVYxdOVS1A==}
engines: {node: '>=10'}
@@ -6512,6 +6948,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-ia32-msvc/1.3.32:
+ resolution: {integrity: sha512-MEUMdpUFIQ+RD+K/iHhHKfu0TFNj9VXwIxT5hmPeqyboKo095CoFEFBJ0sHG04IGlnu8T9i+uE2Pi18qUEbFug==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-x64-msvc/1.3.19:
resolution: {integrity: sha512-0X5HqFC1wQlheOQDZeF6KNOSURZKkGISNK3aTSmTq9g7dDJ/kTcVjsdKbu2rK4ibCnlC9IS0cLK9FpROnsVPwA==}
engines: {node: '>=10'}
@@ -6520,6 +6965,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-x64-msvc/1.3.32:
+ resolution: {integrity: sha512-DPMoneNFQco7SqmVVOUv1Vn53YmoImEfrAPMY9KrqQzgfzqNTuL2JvfxUqfAxwQ6pEKYAdyKJvZ483rIhgG9XQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core/1.3.19:
resolution: {integrity: sha512-KiXUv2vpmOaGhoLCN9Rw7Crsfq1YmOR2ZbajiqNAh/iu0d3CKn5JZhLRs6S7nCk78cwFFac2obQfTWPePLUe/g==}
engines: {node: '>=10'}
@@ -6537,6 +6991,23 @@ packages:
'@swc/core-win32-ia32-msvc': 1.3.19
'@swc/core-win32-x64-msvc': 1.3.19
+ /@swc/core/1.3.32:
+ resolution: {integrity: sha512-Yx/n1j+uUkcqlJAW8IRg8Qymgkdow6NHJZPFShiR0YiaYq2sXY+JHmvh16O6GkL91Y+gTlDUS7uVgDz50czJUQ==}
+ engines: {node: '>=10'}
+ requiresBuild: true
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.3.32
+ '@swc/core-darwin-x64': 1.3.32
+ '@swc/core-linux-arm-gnueabihf': 1.3.32
+ '@swc/core-linux-arm64-gnu': 1.3.32
+ '@swc/core-linux-arm64-musl': 1.3.32
+ '@swc/core-linux-x64-gnu': 1.3.32
+ '@swc/core-linux-x64-musl': 1.3.32
+ '@swc/core-win32-arm64-msvc': 1.3.32
+ '@swc/core-win32-ia32-msvc': 1.3.32
+ '@swc/core-win32-x64-msvc': 1.3.32
+ dev: true
+
/@swc/helpers/0.4.14:
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
dependencies:
@@ -6617,6 +7088,10 @@ packages:
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
dev: true
+ /@types/accept-language-parser/1.5.3:
+ resolution: {integrity: sha512-S8oM29O6nnRC3/+rwYV7GBYIIgNIZ52PCxqBG7OuItq9oATnYWy8FfeLKwvq5F7pIYjeeBSCI7y+l+Z9UEQpVQ==}
+ dev: true
+
/@types/aria-query/5.0.1:
resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==}
dev: true
@@ -6654,7 +7129,7 @@ packages:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
'@types/connect': 3.4.35
- '@types/node': 18.14.6
+ '@types/node': 17.0.45
/@types/bonjour/3.5.10:
resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==}
@@ -6684,7 +7159,11 @@ packages:
/@types/connect/3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies:
- '@types/node': 18.14.6
+ '@types/node': 17.0.45
+
+ /@types/cookie/0.3.3:
+ resolution: {integrity: sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==}
+ dev: false
/@types/cross-spawn/6.0.2:
resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
@@ -6692,6 +7171,15 @@ packages:
'@types/node': 18.14.6
dev: true
+ /@types/cssnano/5.1.0_postcss@8.4.21:
+ resolution: {integrity: sha512-ikR+18UpFGgvaWSur4og6SJYF/6QEYHXvrIt36dp81p1MG3cAPTYDMBJGeyWa3LCnqEbgNMHKRb+FP0NrXtoWQ==}
+ deprecated: This is a stub types definition. cssnano provides its own type definitions, so you do not need this installed.
+ dependencies:
+ cssnano: 5.1.15_postcss@8.4.21
+ transitivePeerDependencies:
+ - postcss
+ dev: true
+
/@types/detect-indent/0.1.30:
resolution: {integrity: sha512-AUmj9JHuHTD94slY1WR1VulFxRGC6D1pcNCN0MCulKFyiihvV/28lLS8oRHgfmc2Cxq954J8Vmosa8qzm7PLGQ==}
dev: true
@@ -6732,7 +7220,7 @@ packages:
/@types/express-serve-static-core/4.17.33:
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
dependencies:
- '@types/node': 18.14.6
+ '@types/node': 17.0.45
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
@@ -6781,7 +7269,7 @@ packages:
/@types/hoist-non-react-statics/3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
hoist-non-react-statics: 3.3.2
dev: false
@@ -6954,7 +7442,7 @@ packages:
resolution: {integrity: sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
hoist-non-react-statics: 3.3.2
redux: 4.2.1
dev: false
@@ -6963,21 +7451,21 @@ packages:
resolution: {integrity: sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg==}
dependencies:
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
'@types/react-router': 5.1.20
/@types/react-router-dom/5.3.3:
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
dependencies:
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
'@types/react-router': 5.1.20
/@types/react-router/5.1.20:
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
dependencies:
'@types/history': 4.7.11
- '@types/react': 18.0.28
+ '@types/react': 18.0.34
/@types/react/17.0.53:
resolution: {integrity: sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==}
@@ -6992,6 +7480,14 @@ packages:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
csstype: 3.1.1
+ dev: true
+
+ /@types/react/18.0.34:
+ resolution: {integrity: sha512-NO1UO8941541CJl1BeOXi8a9dNKFK09Gnru5ZJqkm4Q3/WoQJtHvmwt0VX0SB9YCEwe7TfSSxDuaNmx6H2BAIQ==}
+ dependencies:
+ '@types/prop-types': 15.7.5
+ '@types/scheduler': 0.16.2
+ csstype: 3.1.1
/@types/resolve/1.17.1:
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
@@ -7033,7 +7529,7 @@ packages:
resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
dependencies:
'@types/mime': 3.0.1
- '@types/node': 18.14.6
+ '@types/node': 17.0.45
/@types/sockjs/0.3.33:
resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==}
@@ -7416,6 +7912,10 @@ packages:
event-target-shim: 5.0.1
dev: true
+ /accept-language-parser/1.5.0:
+ resolution: {integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==}
+ dev: false
+
/accepts/1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -7754,6 +8254,68 @@ packages:
scroll-into-view-if-needed: 2.2.31
dev: false
+ /antd/5.4.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-10bZn2fj2ihhBSrwTzhnuFvhLdK1LTVs2GScqhEIDHh+s93Zl7dF27ZNSvth3BUoozoLFjifSVtBbU0dCsd5lw==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@ant-design/colors': 7.0.0
+ '@ant-design/cssinjs': 1.8.1_biqbaboplfbrettd7655fr4n2y
+ '@ant-design/icons': 5.0.1_biqbaboplfbrettd7655fr4n2y
+ '@ant-design/react-slick': 1.0.0_react@18.2.0
+ '@babel/runtime': 7.21.0
+ '@ctrl/tinycolor': 3.6.0
+ '@rc-component/mutate-observer': 1.0.0_biqbaboplfbrettd7655fr4n2y
+ '@rc-component/tour': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ copy-to-clipboard: 3.3.3
+ dayjs: 1.11.7
+ qrcode.react: 3.1.0_react@18.2.0
+ rc-cascader: 3.10.1_biqbaboplfbrettd7655fr4n2y
+ rc-checkbox: 3.0.0_biqbaboplfbrettd7655fr4n2y
+ rc-collapse: 3.5.2_biqbaboplfbrettd7655fr4n2y
+ rc-dialog: 9.1.0_biqbaboplfbrettd7655fr4n2y
+ rc-drawer: 6.1.3_biqbaboplfbrettd7655fr4n2y
+ rc-dropdown: 4.0.1_biqbaboplfbrettd7655fr4n2y
+ rc-field-form: 1.29.2_biqbaboplfbrettd7655fr4n2y
+ rc-image: 5.16.0_biqbaboplfbrettd7655fr4n2y
+ rc-input: 1.0.4_biqbaboplfbrettd7655fr4n2y
+ rc-input-number: 7.4.2_biqbaboplfbrettd7655fr4n2y
+ rc-mentions: 2.2.0_biqbaboplfbrettd7655fr4n2y
+ rc-menu: 9.8.4_biqbaboplfbrettd7655fr4n2y
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-notification: 5.0.3_biqbaboplfbrettd7655fr4n2y
+ rc-pagination: 3.3.1_biqbaboplfbrettd7655fr4n2y
+ rc-picker: 3.5.1_mlnkrlbros4rghcauwy625gk7y
+ rc-progress: 3.4.1_biqbaboplfbrettd7655fr4n2y
+ rc-rate: 2.10.0_biqbaboplfbrettd7655fr4n2y
+ rc-resize-observer: 1.3.1_biqbaboplfbrettd7655fr4n2y
+ rc-segmented: 2.1.2_biqbaboplfbrettd7655fr4n2y
+ rc-select: 14.4.3_biqbaboplfbrettd7655fr4n2y
+ rc-slider: 10.1.1_biqbaboplfbrettd7655fr4n2y
+ rc-steps: 6.0.0_biqbaboplfbrettd7655fr4n2y
+ rc-switch: 4.0.0_biqbaboplfbrettd7655fr4n2y
+ rc-table: 7.31.1_biqbaboplfbrettd7655fr4n2y
+ rc-tabs: 12.5.7_biqbaboplfbrettd7655fr4n2y
+ rc-textarea: 1.2.2_biqbaboplfbrettd7655fr4n2y
+ rc-tooltip: 6.0.1_biqbaboplfbrettd7655fr4n2y
+ rc-tree: 5.7.2_biqbaboplfbrettd7655fr4n2y
+ rc-tree-select: 5.8.0_biqbaboplfbrettd7655fr4n2y
+ rc-trigger: 5.3.4_biqbaboplfbrettd7655fr4n2y
+ rc-upload: 4.3.4_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ scroll-into-view-if-needed: 3.0.10
+ throttle-debounce: 5.0.0
+ transitivePeerDependencies:
+ - date-fns
+ - luxon
+ - moment
+ dev: false
+
/anymatch/3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
@@ -8976,6 +9538,10 @@ packages:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
dev: false
+ /compute-scroll-into-view/3.0.3:
+ resolution: {integrity: sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==}
+ dev: false
+
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -9073,6 +9639,11 @@ packages:
/cookie-signature/1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+ /cookie/0.4.2:
+ resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
/cookie/0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
@@ -9724,7 +10295,6 @@ packages:
/decode-uri-component/0.2.2:
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
engines: {node: '>=0.10'}
- dev: false
/decompress-response/3.3.0:
resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==}
@@ -11448,6 +12018,11 @@ packages:
dependencies:
to-regex-range: 5.0.1
+ /filter-obj/1.1.0:
+ resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/finalhandler/1.2.0:
resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
engines: {node: '>= 0.8'}
@@ -12644,6 +13219,15 @@ packages:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
dev: false
+ /intl-messageformat/10.3.3:
+ resolution: {integrity: sha512-un/f07/g2e/3Q8e1ghDKET+el22Bi49M7O/rHxd597R+oLpPOMykSv5s51cABVfu3FZW+fea4hrzf2MHu1W4hw==}
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/fast-memoize': 2.0.1
+ '@formatjs/icu-messageformat-parser': 2.3.0
+ tslib: 2.5.0
+ dev: false
+
/invariant/2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
dependencies:
@@ -14255,6 +14839,12 @@ packages:
dependencies:
package-json: 6.5.0
+ /launch-editor/2.6.0:
+ resolution: {integrity: sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==}
+ dependencies:
+ picocolors: 1.0.0
+ shell-quote: 1.8.0
+
/less-loader/10.2.0_less@4.1.2+webpack@5.76.2:
resolution: {integrity: sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==}
engines: {node: '>= 12.13.0'}
@@ -14802,6 +15392,11 @@ packages:
schema-utils: 4.0.0
webpack: 5.76.2
+ /mini-svg-data-uri/1.4.4:
+ resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
+ hasBin: true
+ dev: true
+
/miniapp-history/0.1.7:
resolution: {integrity: sha512-q/+f8ncjeyDvPahMLEeknvJiKcVwZLVNDm3tNeB4o8sxJxoQbHIaStJ9SpQkbdhJn971kmoUQyH8aH26O7OvIw==}
dependencies:
@@ -14935,6 +15530,11 @@ packages:
/mute-stream/0.0.8:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
+ /mylas/2.1.13:
+ resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==}
+ engines: {node: '>=12.0.0'}
+ dev: true
+
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -15557,6 +16157,12 @@ packages:
dependencies:
find-up: 3.0.0
+ /plimit-lit/1.5.0:
+ resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==}
+ dependencies:
+ queue-lit: 1.5.0
+ dev: true
+
/portfinder/1.0.32:
resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==}
engines: {node: '>= 0.12.0'}
@@ -16973,6 +17579,14 @@ packages:
resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
+ /qrcode.react/3.1.0_react@18.2.0:
+ resolution: {integrity: sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/qs/6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
@@ -16984,9 +17598,23 @@ packages:
engines: {node: '>=0.6'}
dev: false
+ /query-string/7.1.3:
+ resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
+ engines: {node: '>=6'}
+ dependencies:
+ decode-uri-component: 0.2.2
+ filter-obj: 1.1.0
+ split-on-first: 1.1.0
+ strict-uri-encode: 2.0.0
+ dev: true
+
/querystringify/2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+ /queue-lit/1.5.0:
+ resolution: {integrity: sha512-IslToJ4eiCEE9xwMzq3viOO5nH8sUWUCwoElrhNMozzr9IIt2qqvB4I+uHu/zJTQVqc9R5DFwok4ijNK1pU3fA==}
+ dev: true
+
/queue-microtask/1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -17120,6 +17748,22 @@ packages:
resize-observer-polyfill: 1.5.1
dev: false
+ /rc-cascader/3.10.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-tImBYEAqLlIZ+jnRmfQQEm5gOXa09N9aGV9AKxriXlCvsNEfdZMIRyY0p74sEZIUn0ycXHo8VcOlqsgLcgFknQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ array-tree-filter: 2.1.0
+ classnames: 2.3.2
+ rc-select: 14.4.3_biqbaboplfbrettd7655fr4n2y
+ rc-tree: 5.7.2_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-cascader/3.7.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-SFtGpwmYN7RaWEAGTS4Rkc62ZV/qmQGg/tajr/7mfIkleuu8ro9Hlk6J+aA0x1YS4zlaZBtTcSaXM01QMiEV/A==}
peerDependencies:
@@ -17148,6 +17792,19 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-checkbox/3.0.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-tOEs1+wWDUei7DuP2EsJCZfam5vxMjKTCGcZdXVgsiOcNszc41Esycbo31P0/jFwUAPmd5oPYFWkcnFUCTLZxA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-collapse/3.4.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q==}
peerDependencies:
@@ -17163,6 +17820,20 @@ packages:
shallowequal: 1.1.0
dev: false
+ /rc-collapse/3.5.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-/TNiT3DW1t3sUCiVD/DPUYooJZ3BLA93/2rZsB3eM2bGJCCla2X9D2E4tgm7LGMQGy5Atb2lMUn2FQuvQNvavQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-dialog/9.0.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg==}
peerDependencies:
@@ -17178,6 +17849,21 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-dialog/9.1.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-5ry+JABAWEbaKyYsmITtrJbZbJys8CtMyzV8Xn4LYuXMeUx5XVHNyJRoqLFE4AzBuXXzOWeaC49cg+XkxK6kHA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/portal': 1.1.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-drawer/6.1.3_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-AvHisO90A+xMLMKBw2zs89HxjWxusM2BUABlgK60RhweIHF8W/wk0hSOrxBlUXoA9r1F+10na3g6GZ97y1qDZA==}
peerDependencies:
@@ -17221,6 +17907,20 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-field-form/1.29.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-gXNkthHMUjJ7gDKYmD/lJWJrpMqAjiEPQE4QmlOuZoiHF51LybCL/y+iAmLXpdEjPfJ41WtZBH5hZMUEnEnHXA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ async-validator: 4.2.5
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-image/5.13.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg==}
peerDependencies:
@@ -17237,24 +17937,27 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /rc-input-number/7.3.11_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-aMWPEjFeles6PQnMqP5eWpxzsvHm9rh1jQOWXExUEIxhX62Fyl/ptifLHOn17+waDG1T/YUb6flfJbvwRhHrbA==}
+ /rc-image/5.16.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-11DOye57IgTXh2yTsmxFNynZJG3tdx8RZnnaqb38eYWrBPPyhVHIuURxyiSZ8B68lEUAggR7SBA0Zb95KP/CyQ==}
peerDependencies:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.21.0
+ '@rc-component/portal': 1.1.0_biqbaboplfbrettd7655fr4n2y
classnames: 2.3.2
+ rc-dialog: 9.1.0_biqbaboplfbrettd7655fr4n2y
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /rc-input/0.1.4_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA==}
+ /rc-input-number/7.3.11_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-aMWPEjFeles6PQnMqP5eWpxzsvHm9rh1jQOWXExUEIxhX62Fyl/ptifLHOn17+waDG1T/YUb6flfJbvwRhHrbA==}
peerDependencies:
- react: '>=16.0.0'
- react-dom: '>=16.0.0'
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.21.0
classnames: 2.3.2
@@ -17263,8 +17966,48 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /rc-mentions/1.13.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw==}
+ /rc-input-number/7.4.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-yGturTw7WGP+M1GbJ+UTAO7L4buxeW6oilhL9Sq3DezsRS8/9qec4UiXUbeoiX9bzvRXH11JvgskBtxSp4YSNg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/mini-decimal': 1.0.1
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
+ /rc-input/0.1.4_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
+ /rc-input/1.0.4_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-clY4oneVHRtKHYf/HCxT/MO+4BGzCIywSNLosXWOm7fcQAS0jQW7n0an8Raa8JMB8kpxc8m28p7SNwFZmlMj6g==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
+ /rc-mentions/1.13.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw==}
peerDependencies:
react: '>=16.9.0'
react-dom: '>=16.9.0'
@@ -17279,6 +18022,23 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-mentions/2.2.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-R7ncCldr02uKgJBBPlXdtnOGQIjZ9C3uoIMi4fabU3CPFdmefYlNF6QM4u2AzgcGt8V0KkoHTN5T6HPdUpet8g==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-input: 1.0.4_biqbaboplfbrettd7655fr4n2y
+ rc-menu: 9.8.4_biqbaboplfbrettd7655fr4n2y
+ rc-textarea: 1.2.2_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-menu/9.8.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-EahOJVjLuEnJsThoPN+mGnVm431RzVzDLZWHRS/YnXTQULa7OsgdJa/Y7qXxc3Z5sz8mgT6xYtgpmBXLxrZFaQ==}
peerDependencies:
@@ -17295,6 +18055,22 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-menu/9.8.4_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-overflow: 1.2.8_biqbaboplfbrettd7655fr4n2y
+ rc-trigger: 5.3.4_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-motion/2.6.3_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-xFLkes3/7VL/J+ah9jJruEW/Akbx5F6jVa2wG5o/ApGKQKSOd5FR3rseHLL9+xtJg4PmCwo6/1tqhDO/T+jFHA==}
peerDependencies:
@@ -17323,6 +18099,21 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-notification/5.0.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-+wHbHu6RiTNtsZYx42WxWA+tC5m0qyKvJAauO4/6LIEyJspK8fRlFQz+OCFgFwGuNs3cOdo9tLs+cPfztSZwbQ==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-overflow/1.2.8_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ==}
peerDependencies:
@@ -17349,6 +18140,18 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-pagination/3.3.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-eI4dSeB3OrFxll7KzWa3ZH63LV2tHxt0AUmZmDwuI6vc3CK5lZhaKUYq0fRowb5586hN+L26j5WZoSz9cwEfjg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-picker/2.7.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-oZH6FZ3j4iuBxHB4NvQ6ABRsS2If/Kpty1YFFsji7/aej6ruGmfM7WnJWQ88AoPfpJ++ya5z+nVEA8yCRYGKyw==}
engines: {node: '>=8.x'}
@@ -17368,6 +18171,35 @@ packages:
shallowequal: 1.1.0
dev: false
+ /rc-picker/3.5.1_mlnkrlbros4rghcauwy625gk7y:
+ resolution: {integrity: sha512-T/rqhB2IVU014k14x713JGzHCUT56YEYsGkUT8vBVOANdoRCe18oN/8zdeWYB/7mQRTTHJ1vCSPnxOowjLyN8Q==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ date-fns: '>= 2.x'
+ dayjs: '>= 1.x'
+ luxon: '>= 3.x'
+ moment: '>= 2.x'
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ peerDependenciesMeta:
+ date-fns:
+ optional: true
+ dayjs:
+ optional: true
+ luxon:
+ optional: true
+ moment:
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ dayjs: 1.11.7
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-progress/3.4.1_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw==}
peerDependencies:
@@ -17381,6 +18213,20 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-rate/2.10.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-TCjEpKPeN1m0EnGDDbb1KyxjNTJRzoReiPdtbrBJEey4Ryf/UGOQ6vqmz2yC6DJdYVDVUoZPdoz043ryh0t/nQ==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-rate/2.9.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g==}
engines: {node: '>=8.x'}
@@ -17441,6 +18287,24 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-select/14.4.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-qoz4gNqm3SN+4dYKSCRiRkxKSEEdbS3jC6gdFYoYwEjDZ9sdQFo5jHlfQbF+hhai01HOoj1Hf8Gq6tpUvU+Gmw==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-motion: 2.6.3_biqbaboplfbrettd7655fr4n2y
+ rc-overflow: 1.2.8_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ rc-virtual-list: 3.4.13_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-slider/10.0.1_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q==}
engines: {node: '>=8.x'}
@@ -17456,6 +18320,20 @@ packages:
shallowequal: 1.1.0
dev: false
+ /rc-slider/10.1.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-gn8oXazZISEhnmRinI89Z/JD/joAaM35jp+gDtIVSTD/JJMCCBqThqLk1SVJmvtfeiEF/kKaFY0+qt4SDHFUDw==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-steps/5.0.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-9TgRvnVYirdhbV0C3syJFj9EhCRqoJAsxt4i1rED5o8/ZcSv5TLIYyo4H8MCjLPvbe2R+oBAm/IYBEtC+OS1Rw==}
engines: {node: '>=8.x'}
@@ -17470,6 +18348,20 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-steps/6.0.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-switch/3.2.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A==}
peerDependencies:
@@ -17483,6 +18375,19 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-switch/4.0.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-table/7.26.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-0cD8e6S+DTGAt5nBZQIPFYEaIukn17sfa5uFL98faHlH/whZzD8ii3dbFL4wmUDEL4BLybhYop+QUfZJ4CPvNQ==}
engines: {node: '>=8.x'}
@@ -17499,6 +18404,22 @@ packages:
shallowequal: 1.1.0
dev: false
+ /rc-table/7.31.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-KZPi35aGpv2VaL1Jbc58FBJo063HtKyVjhOFWX4AkBV7tjHHQokMdUoua5E+GPJh6QZUpK/a8PjKa9IZzPLIEA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/context': 1.3.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ rc-resize-observer: 1.3.1_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-tabs/12.5.7_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-i9gY2TcwCNmBM+bXCDDTvb6mnRYIDkkNm+UGoIqrLOFnRRbAqjsSf+tgyvzhBvbK8XcSrMhzKKLaOMbGyND8YA==}
engines: {node: '>=8.x'}
@@ -17532,6 +18453,21 @@ packages:
shallowequal: 1.1.0
dev: false
+ /rc-textarea/1.2.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-S9fkiek5VezfwJe2McEs/NH63xgnnZ4iDh6a8n01mIfzyNJj0HkS0Uz6boyR3/eONYjmKaqhrpuJJuEClRDEBw==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-input: 1.0.4_biqbaboplfbrettd7655fr4n2y
+ rc-resize-observer: 1.3.1_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-tooltip/5.2.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg==}
peerDependencies:
@@ -17545,6 +18481,19 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-tooltip/6.0.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-MdvPlsD1fDSxKp9+HjXrc/CxLmA/s11QYIh1R7aExxfodKP7CZA++DG1AjrW80F8IUdHYcR43HAm0Y2BYPelHA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@rc-component/trigger': 1.8.0_biqbaboplfbrettd7655fr4n2y
+ classnames: 2.3.2
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-tree-select/5.5.5_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-k2av7jF6tW9bIO4mQhaVdV4kJ1c54oxV3/hHVU+oD251Gb5JN+m1RbJFTMf1o0rAFqkvto33rxMdpafaGKQRJw==}
peerDependencies:
@@ -17560,6 +18509,21 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
+ /rc-tree-select/5.8.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-NozrkVLR8k3cpx8R5/YFmJMptgOacR5zEQHZGMQg31bD6jEgGiJeOn2cGRI6x0Xdyvi1CSqCbUsIoqiej74wzw==}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ classnames: 2.3.2
+ rc-select: 14.4.3_biqbaboplfbrettd7655fr4n2y
+ rc-tree: 5.7.2_biqbaboplfbrettd7655fr4n2y
+ rc-util: 5.28.0_biqbaboplfbrettd7655fr4n2y
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/rc-tree/5.7.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-nmnL6qLnfwVckO5zoqKL2I9UhwDqzyCtjITQCkwhimyz1zfuFkG5ZPIXpzD/Guzso94qQA/QrMsvzic5W6QDjg==}
engines: {node: '>=10.x'}
@@ -17617,6 +18581,18 @@ packages:
react-is: 16.13.1
dev: false
+ /rc-util/5.29.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-wX6ZwQTzY2v7phJBquN4mSEIFR0E0qumlENx0zjENtDvoVSq2s7cR95UidKRO1hOHfDsecsfM9D1gO4Kebs7fA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.21.0
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ react-is: 16.13.1
+ dev: false
+
/rc-virtual-list/3.4.13_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w==}
engines: {node: '>=8.x'}
@@ -17730,6 +18706,28 @@ packages:
react-fast-compare: 3.2.0
shallowequal: 1.1.0
+ /react-intl/6.3.2_react@18.2.0:
+ resolution: {integrity: sha512-NT03zOHRAFGcZdTx4cXcVKZtnWBOM6RfLPK8Q67eA+Ba+pHdYb+cmrahncqAnevZKgO1r/nEauiVFKwQeudLIw==}
+ peerDependencies:
+ react: ^16.6.0 || 17 || 18
+ typescript: ^4.7
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@formatjs/ecma402-abstract': 1.14.3
+ '@formatjs/icu-messageformat-parser': 2.3.0
+ '@formatjs/intl': 2.6.9
+ '@formatjs/intl-displaynames': 6.2.6
+ '@formatjs/intl-listformat': 7.1.9
+ '@types/hoist-non-react-statics': 3.3.1
+ '@types/react': 18.0.34
+ hoist-non-react-statics: 3.3.2
+ intl-messageformat: 10.3.3
+ react: 18.2.0
+ tslib: 2.5.0
+ dev: false
+
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -18367,6 +19365,50 @@ packages:
- ts-node
dev: true
+ /rollup-plugin-styles/4.0.0_rollup@2.79.1:
+ resolution: {integrity: sha512-A2K2sao84OsTmDxXG83JTCdXWrmgvQkkI38XDat46rdtpGMRm9tSYqeCdlwwGDJF4kKIafhV1mUidqu8MxUGig==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ peerDependencies:
+ rollup: ^2.63.0
+ dependencies:
+ '@rollup/pluginutils': 4.2.1
+ '@types/cssnano': 5.1.0_postcss@8.4.21
+ cosmiconfig: 7.1.0
+ cssnano: 5.1.15_postcss@8.4.21
+ fs-extra: 10.1.0
+ icss-utils: 5.1.0_postcss@8.4.21
+ mime-types: 2.1.35
+ p-queue: 6.6.2
+ postcss: 8.4.21
+ postcss-modules-extract-imports: 3.0.0_postcss@8.4.21
+ postcss-modules-local-by-default: 4.0.0_postcss@8.4.21
+ postcss-modules-scope: 3.0.0_postcss@8.4.21
+ postcss-modules-values: 4.0.0_postcss@8.4.21
+ postcss-value-parser: 4.2.0
+ query-string: 7.1.3
+ resolve: 1.22.1
+ rollup: 2.79.1
+ source-map-js: 1.0.2
+ tslib: 2.5.0
+ dev: true
+
+ /rollup-plugin-visualizer/5.9.0_rollup@2.79.1:
+ resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==}
+ engines: {node: '>=14'}
+ hasBin: true
+ peerDependencies:
+ rollup: 2.x || 3.x
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ open: 8.4.2
+ picomatch: 2.3.1
+ rollup: 2.79.1
+ source-map: 0.7.4
+ yargs: 17.7.1
+ dev: true
+
/rollup-pluginutils/2.8.2:
resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==}
dependencies:
@@ -18563,6 +19605,12 @@ packages:
compute-scroll-into-view: 1.0.20
dev: false
+ /scroll-into-view-if-needed/3.0.10:
+ resolution: {integrity: sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg==}
+ dependencies:
+ compute-scroll-into-view: 3.0.3
+ dev: false
+
/section-matter/1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
@@ -18950,6 +19998,11 @@ packages:
webpack: 5.76.2
dev: true
+ /split-on-first/1.1.0:
+ resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
+ engines: {node: '>=6'}
+ dev: true
+
/split2/3.2.2:
resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==}
dependencies:
@@ -19036,6 +20089,11 @@ packages:
mixme: 0.5.5
dev: true
+ /strict-uri-encode/2.0.0:
+ resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
+ engines: {node: '>=4'}
+ dev: true
+
/string-argv/0.3.1:
resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
engines: {node: '>=0.6.19'}
@@ -19302,6 +20360,10 @@ packages:
loader-utils: 1.4.2
dev: false
+ /stylis/4.1.3:
+ resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==}
+ dev: false
+
/supports-color/2.0.0:
resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
engines: {node: '>=0.8.0'}
@@ -19618,6 +20680,11 @@ packages:
/text-table/0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+ /throttle-debounce/5.0.0:
+ resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==}
+ engines: {node: '>=12.22'}
+ dev: false
+
/through/2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
@@ -19828,6 +20895,18 @@ packages:
yn: 3.1.1
dev: true
+ /tsc-alias/1.8.5:
+ resolution: {integrity: sha512-Y3ka0olwSRdbHPyX5kXhYY2aoBKuT53DFdeY+PpQUR4hg5M/b8eIRmC8dL4FBdd0wT366iWc6iDUUGe6QwI7mg==}
+ hasBin: true
+ dependencies:
+ chokidar: 3.5.3
+ commander: 9.5.0
+ globby: 11.1.0
+ mylas: 2.1.13
+ normalize-path: 3.0.0
+ plimit-lit: 1.5.0
+ dev: true
+
/tsconfig-paths/3.14.2:
resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==}
dependencies:
@@ -20111,6 +21190,13 @@ packages:
unist-util-is: 4.1.0
unist-util-visit-parents: 3.1.1
+ /universal-cookie/4.0.4:
+ resolution: {integrity: sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==}
+ dependencies:
+ '@types/cookie': 0.3.3
+ cookie: 0.4.2
+ dev: false
+
/universal-env/3.3.3:
resolution: {integrity: sha512-4ZyITvWhtcurCEA66Cb7jcd4zpEiAAo91wSwbEscbiu033pIsC2yjgT8LYyasFgsst6jZHD1gtVoSyYcL8oH1Q==}
engines: {npm: '>=3.0.0'}
@@ -20219,6 +21305,11 @@ packages:
/url-join/4.0.1:
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
+ /url-join/5.0.0:
+ resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: false
+
/url-loader/4.1.1_35ful32yo3wjb53le3l6xb5doy:
resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==}
engines: {node: '>= 10.13.0'}
@@ -20712,6 +21803,19 @@ packages:
- bufferutil
- utf-8-validate
+ /webpack-dev-middleware/5.3.3:
+ resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==}
+ engines: {node: '>= 12.13.0'}
+ peerDependencies:
+ webpack: ^4.0.0 || ^5.0.0
+ dependencies:
+ colorette: 2.0.19
+ memfs: 3.4.13
+ mime-types: 2.1.35
+ range-parser: 1.2.1
+ schema-utils: 4.0.0
+ dev: true
+
/webpack-dev-middleware/5.3.3_webpack@5.76.2:
resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==}
engines: {node: '>= 12.13.0'}
@@ -20725,7 +21829,7 @@ packages:
schema-utils: 4.0.0
webpack: 5.76.2_i6ic2mvdiaf3c6z2npjtaj5kuu
- /webpack-dev-server/4.11.1_debug@4.3.4+webpack@5.76.2:
+ /webpack-dev-server/4.11.1_webpack@5.76.2:
resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==}
engines: {node: '>= 12.13.0'}
hasBin: true
@@ -20735,6 +21839,106 @@ packages:
peerDependenciesMeta:
webpack-cli:
optional: true
+ dependencies:
+ '@types/bonjour': 3.5.10
+ '@types/connect-history-api-fallback': 1.3.5
+ '@types/express': 4.17.17
+ '@types/serve-index': 1.9.1
+ '@types/serve-static': 1.15.1
+ '@types/sockjs': 0.3.33
+ '@types/ws': 8.5.4
+ ansi-html-community: 0.0.8
+ bonjour-service: 1.1.0
+ chokidar: 3.5.3
+ colorette: 2.0.19
+ compression: 1.7.4
+ connect-history-api-fallback: 2.0.0
+ default-gateway: 6.0.3
+ express: 4.18.2
+ graceful-fs: 4.2.10
+ html-entities: 2.3.3
+ http-proxy-middleware: 2.0.6_@types+express@4.17.17
+ ipaddr.js: 2.0.1
+ open: 8.4.2
+ p-retry: 4.6.2
+ rimraf: 3.0.2
+ schema-utils: 4.0.0
+ selfsigned: 2.1.1
+ serve-index: 1.9.1
+ sockjs: 0.3.24
+ spdy: 4.0.2
+ webpack: 5.76.2_i6ic2mvdiaf3c6z2npjtaj5kuu
+ webpack-dev-middleware: 5.3.3_webpack@5.76.2
+ ws: 8.12.1
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: true
+
+ /webpack-dev-server/4.13.2:
+ resolution: {integrity: sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==}
+ engines: {node: '>= 12.13.0'}
+ hasBin: true
+ peerDependencies:
+ webpack: ^4.37.0 || ^5.0.0
+ webpack-cli: '*'
+ peerDependenciesMeta:
+ webpack:
+ optional: true
+ webpack-cli:
+ optional: true
+ dependencies:
+ '@types/bonjour': 3.5.10
+ '@types/connect-history-api-fallback': 1.3.5
+ '@types/express': 4.17.17
+ '@types/serve-index': 1.9.1
+ '@types/serve-static': 1.15.1
+ '@types/sockjs': 0.3.33
+ '@types/ws': 8.5.4
+ ansi-html-community: 0.0.8
+ bonjour-service: 1.1.0
+ chokidar: 3.5.3
+ colorette: 2.0.19
+ compression: 1.7.4
+ connect-history-api-fallback: 2.0.0
+ default-gateway: 6.0.3
+ express: 4.18.2
+ graceful-fs: 4.2.10
+ html-entities: 2.3.3
+ http-proxy-middleware: 2.0.6_@types+express@4.17.17
+ ipaddr.js: 2.0.1
+ launch-editor: 2.6.0
+ open: 8.4.2
+ p-retry: 4.6.2
+ rimraf: 3.0.2
+ schema-utils: 4.0.0
+ selfsigned: 2.1.1
+ serve-index: 1.9.1
+ sockjs: 0.3.24
+ spdy: 4.0.2
+ webpack-dev-middleware: 5.3.3
+ ws: 8.13.0
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: true
+
+ /webpack-dev-server/4.13.2_debug@4.3.4+webpack@5.76.2:
+ resolution: {integrity: sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==}
+ engines: {node: '>= 12.13.0'}
+ hasBin: true
+ peerDependencies:
+ webpack: ^4.37.0 || ^5.0.0
+ webpack-cli: '*'
+ peerDependenciesMeta:
+ webpack:
+ optional: true
+ webpack-cli:
+ optional: true
dependencies:
'@types/bonjour': 3.5.10
'@types/connect-history-api-fallback': 1.3.5
@@ -20755,6 +21959,7 @@ packages:
html-entities: 2.3.3
http-proxy-middleware: 2.0.6_cdocoejotnspksifechgljubnq
ipaddr.js: 2.0.1
+ launch-editor: 2.6.0
open: 8.4.2
p-retry: 4.6.2
rimraf: 3.0.2
@@ -20765,7 +21970,7 @@ packages:
spdy: 4.0.2
webpack: 5.76.2
webpack-dev-middleware: 5.3.3_webpack@5.76.2
- ws: 8.12.1
+ ws: 8.13.0
transitivePeerDependencies:
- bufferutil
- debug
@@ -20773,14 +21978,16 @@ packages:
- utf-8-validate
dev: false
- /webpack-dev-server/4.11.1_webpack@5.76.2:
- resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==}
+ /webpack-dev-server/4.13.2_webpack@5.76.2:
+ resolution: {integrity: sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==}
engines: {node: '>= 12.13.0'}
hasBin: true
peerDependencies:
webpack: ^4.37.0 || ^5.0.0
webpack-cli: '*'
peerDependenciesMeta:
+ webpack:
+ optional: true
webpack-cli:
optional: true
dependencies:
@@ -20803,6 +22010,7 @@ packages:
html-entities: 2.3.3
http-proxy-middleware: 2.0.6_@types+express@4.17.17
ipaddr.js: 2.0.1
+ launch-editor: 2.6.0
open: 8.4.2
p-retry: 4.6.2
rimraf: 3.0.2
@@ -20811,9 +22019,9 @@ packages:
serve-index: 1.9.1
sockjs: 0.3.24
spdy: 4.0.2
- webpack: 5.76.2_i6ic2mvdiaf3c6z2npjtaj5kuu
+ webpack: 5.76.2
webpack-dev-middleware: 5.3.3_webpack@5.76.2
- ws: 8.12.1
+ ws: 8.13.0
transitivePeerDependencies:
- bufferutil
- debug
@@ -21165,6 +22373,19 @@ packages:
optional: true
utf-8-validate:
optional: true
+ dev: true
+
+ /ws/8.13.0:
+ resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
/xdg-basedir/4.0.0:
resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==}
diff --git a/tests/integration/with-i18n.test.ts b/tests/integration/with-i18n.test.ts
new file mode 100644
index 0000000000..0014372a82
--- /dev/null
+++ b/tests/integration/with-i18n.test.ts
@@ -0,0 +1,44 @@
+import * as path from 'path';
+import glob from 'glob';
+import { expect, test, describe, afterAll } from 'vitest';
+import { buildFixture, setupBrowser } from '../utils/build';
+import type { Page } from '../utils/browser';
+import type Browser from '../utils/browser';
+
+const example = 'with-i18n';
+
+describe(`build ${example}`, () => {
+ let page: Page;
+ let browser: Browser;
+
+ test('generate html files with locales', async () => {
+ await buildFixture(example);
+ const res = await setupBrowser({ example, disableJS: false });
+ page = res.page;
+ browser = res.browser;
+ const distDir = path.join(__dirname, `../../examples/${example}/build`);
+ const htmlFiles = glob.sync('**/*.html', { cwd: distDir });
+
+ expect(htmlFiles).toEqual([
+ 'blog.html',
+ 'blog/a.html',
+ 'en-US.html',
+ 'en-US/blog.html',
+ 'en-US/blog/a.html',
+ 'index.html',
+ ]);
+ });
+
+ test('visit / page and get the zh-CN locale page', async () => {
+ expect(await page.$$text('#button')).toStrictEqual(['普通按钮']);
+ });
+
+ test('visit /en-US page and get the en-US locale page', async () => {
+ await page.push('/en-US.html');
+ expect(await page.$$text('#button')).toStrictEqual(['Normal Button']);
+ });
+
+ afterAll(async () => {
+ await browser.close();
+ });
+});
\ No newline at end of file
diff --git a/website/docs/guide/advanced/i18n.md b/website/docs/guide/advanced/i18n.md
index 3f5573ea1e..da53204c40 100644
--- a/website/docs/guide/advanced/i18n.md
+++ b/website/docs/guide/advanced/i18n.md
@@ -1,6 +1,380 @@
---
title: 国际化
-hide: true
---
-@TODO
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+ice.js 官方提供 i18n 国际化插件,支持在应用快速开启国际化能力。核心特性包括:
+
+1. 支持自动处理和生成国际化路由
+2. 完美支持 SSR 和 SSG,以获得更好的 SEO 优化
+3. 支持自动重定向到偏好语言对应的页面
+4. 不耦合任何一个 i18n 库(流行的 React i18n 库有 [react-intl](https://formatjs.io/docs/getting-started/installation/)、[react-i18next](https://react.i18next.com/) 等),你可以选择任一国际化的库来为你的应用设置国际化
+
+
+ 使用国际化插件的示例
+
+
+
+:::tip
+
+如果应用不需要使用国际化路由,你可以参考以下例子来让你的项目支持国际化:
+
+- [with-antd5](https://github.com/alibaba/ice/tree/master/examples/with-antd5)
+- [with-fusion](https://github.com/alibaba/ice/tree/master/examples/with-fusion)
+
+:::
+
+## 快速开始
+
+首先,我们需要在终端执行以下命令安装插件:
+
+```bash
+$ npm i @ice/plugin-i18n -D
+```
+
+然后在 `ice.config.mts` 中添加插件和选项:
+
+```ts
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US', 'de'],
+ defaultLocale: 'zh-CN',
+ }),
+ ],
+});
+```
+
+上面的 `en-US` 和 `zh-CN` 是国际化语言的缩写,它们均遵循标准的 [UTS 语言标识符](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers)。比如:
+
+- `zh-CN`:中文(中国)
+- `zh-HK`:中文(香港)
+- `en-US`:英文(美国)
+- `de`: 德文
+
+## 国际化路由
+
+国际化路由是指在页面路由地址中包含了当前页面的语言,一个国际化路由对应一个语言。
+
+假设现在插件的选项配置是:
+
+```ts
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US', 'nl-NL'],
+ defaultLocale: 'zh-CN',
+ }),
+ ],
+});
+```
+
+假设我们有一个页面 `src/pages/home.tsx`,那么将会一一对应自动生成以下的路由:
+
+- `/home`:显示 `zh-CN` 语言,默认语言对应的路由不包含语言前缀
+- `/en-US/home`:显示 `en-US` 语言
+- `/nl-NL/home`:显示 `nl-NL` 语言
+
+访问不同的路由,将会显示该语言对应页面内容。
+
+## 获取语言信息
+
+### `getLocales()`
+
+`getAllLocales()` 用于获取当前应用支持的所有语言。
+
+```ts
+import { getAllLocales } from 'ice';
+
+console.log(getAllLocales()); // ['zh-CN', 'en-US']
+```
+
+### `getDefaultLocale()`
+
+`getDefaultLocale()` 用于获取应用配置的默认语言。
+
+```ts
+import { getDefaultLocale } from 'ice';
+
+console.log(getDefaultLocale()); // 'zh-CN'
+```
+
+### `useLocale()`
+
+在 Function 组件中使用 `useLocale()` Hook API,它的返回值是一个数组,包含两个值:
+
+1. 当前页面的语言
+2. 一个 set 函数用于更新当前页面的语言。注意,默认情况下调用此 set 函数时候,同时会更新 Cookie 中 `ice_locale` 的值为当前页面的语言。这样,再次访问该页面时,从服务端请求能得知当前用户的之前设置的偏好语言,以便返回对应语言的页面内容。
+
+
+```tsx
+import { useLocale } from 'ice';
+
+export default function Home() {
+ const [locale, setLocale] = useLocale();
+
+ console.log('locale: ', locale); // 'en-US'
+ return (
+ <>
+ {/* 切换语言为 zh-CN */}
+ setLocale('zh-CN')}>Set zh-CN
+ >
+ )
+}
+```
+
+### `withLocale()`
+
+使用 `withLocale()` 方法包裹的 Class 组件,组件的 Props 会包含 `locale` 和 `setLocale()` 函数,可以查看和修改当前页面的语言。注意,默认情况下调用 `setLocale()`,会更新 Cookie 中 `ice_locale` 的值为当前页面的语言。这样,再次访问该页面时,从服务端请求能得知当前用户的之前设置的偏好语言,以便返回对应语言的页面内容。
+
+```tsx
+import { withLocale } from 'ice';
+
+function Home({ locale, setLocale }) {
+ console.log('locale: ', locale); // 'en-US'
+ return (
+ <>
+ {/* 切换语言为 zh-CN */}
+ setLocale('zh-CN')}>Set zh-CN
+ >
+ )
+}
+
+export default withLocale(Home);
+```
+
+## 切换语言
+
+推荐使用 `setLocale()` 方法配合 `` 组件或者 `useNavigate()` 方法进行语言切换:
+
+
+
+
+```tsx
+import { useLocale, getAllLocales, Link, useLocation } from 'ice';
+
+export default function Layout() {
+ const location = useLocation();
+ const [activeLocale, setLocale] = useLocale();
+
+ return (
+
+ Current locale: {activeLocale}
+
+ Choose language:
+
+ {
+ getAllLocales().map((locale: string) => {
+ return (
+ -
+ setLocale(locale)}
+ >
+ {locale}
+
+
+ );
+ })
+ }
+
+
+ );
+}
+```
+
+
+
+
+```tsx
+import { useLocale, useNavigate, useLocation } from 'ice';
+
+export default function Layout() {
+ const [, setLocale] = useLocale();
+ const location = useLocation();
+ const navigate = useNavigate();
+ const switchToZHCN = () => {
+ setLocale('zh-CN');
+ navigate(location.pathname);
+ }
+ return (
+
+
+ 点我切换到中文
+
+
+ );
+}
+```
+
+
+
+
+## 路由自动重定向
+
+路由自动重定向是指,如果当前访问的页面是根路由(`/`),将会根据当前语言环境自动跳转到对应的国际化路由。
+
+默认情况下,路由自动重定向的功能是关闭的。如果需要开启,则需要加入以下内容:
+
+```diff
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US', 'de'],
+ defaultLocale: 'zh-CN',
++ autoRedirect: true,
+ }),
+ ],
+});
+```
+其中,语言环境的识别顺序如下:
+
+- `CSR`:cookie 中 `ice_locale` 的值 > `window.navigator.language` > `defaultLocale`
+- `SSR`:cookie 中 `ice_locale` 的值 > `Request Header` 中的 `Accept-Language` > `defaultLocale`
+
+在部署阶段,路由自动重定向的功能需要配合 Node 中间件使用才能生效。比如:
+
+```ts
+import express from 'express';
+import { renderToHTML } from './build/server/index.mjs';
+
+const app = express();
+
+app.use(express.static('build', {}));
+
+app.use(async (req, res) => {
+ const { statusCode, statusText, headers, value: body } = await renderToHTML({ req, res });
+ res.statusCode = statusCode;
+ res.statusMessage = statusText;
+ Object.entries((headers || {}) as Record).forEach(([name, value]) => {
+ res.setHeader(name, value);
+ });
+ if (body && req.method !== 'HEAD') {
+ res.end(body);
+ } else {
+ res.end();
+ }
+});
+```
+
+## 禁用 Cookie
+
+在上面的章节中提到,用户设置的偏好语言是存放在 Cookie 中的 `ice_locale`,调用 `setLocale()` 时会更新到 Cookie 中,并且路由重定向和路由跳转的时候依赖 `ice_locale` 的值。
+
+假设有这么一个场景,用户拒绝接受 Cookie,为了保护隐私,这样就不能把偏好语言写到 Cookie 中了。因此需要做以下的配置来禁用 Cookie:
+
+```ts title="src/app.ts"
+import { defineI18nConfig } from '@ice/plugin-i18n/types';
+
+export const i18nConfig = defineI18nConfig(() => ({
+ // 可以是一个 function
+ disabledCookie: () => {
+ if (import.meta.renderer === 'client') {
+ return window.localStorage.getItem('acceptCookie') === 'yes';
+ }
+ return false;
+ },
+ // 也可以是 boolean 值
+ // disabledCookie: true,
+}));
+```
+
+这样,就禁用掉了 Cookie 的写入了。在切换语言的时候需要在 `state` 对象中显式传入即将要切换的新语言的值:
+
+```tsx
+import { Link, useLocale } from 'ice';
+
+export default function Home() {
+ const [, setLocale] = useLocale();
+ return (
+ <>
+ setLocale('zh-CN')}
+ state={{ locale: 'zh-CN' }}
+ >
+ 切换到 zh-CN
+
+ >
+ )
+}
+```
+
+## SSG
+
+在开启 SSG 功能后,将根据配置的 `locales` 的值,在 `build` 阶段会生成不同语言对应的 HTML。
+
+比如我们有以下的目录结构,包含 `about` 和 `index` 两个页面:
+
+```md
+├── src/pages
+| ├── about.tsx
+| └── index.tsx
+```
+
+假如插件的配置是:
+
+```ts
+import { defineConfig } from '@ice/app';
+import i18n from '@ice/plugin-i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ locales: ['zh-CN', 'en-US'],
+ defaultLocale: 'zh-CN',
+ }),
+ ],
+});
+```
+
+那么将会生成 4 个 HTML 文件:
+
+```md
+├── build
+| ├── about
+| | └── index.html
+| ├── en-US
+| | ├── about
+| | | └── index.html
+| | └── index.html
+| ├── index.html
+```
+
+## 插件选项
+
+### `locales`
+
+- **类型:**`string[]`
+
+用于声明该应用支持的语言。
+
+### `defaultLocale`
+
+- **类型:**`string`
+
+声明该应用默认的语言。需要注意的是, `locales` 数组必须包含 `defaultLocale` 的值。
+
+### `autoRedirect`
+
+- **类型:**`boolean`
+- **默认值:**`false`
+
+默认不会自动重定向到用户偏好语言对应的页面。如果设置为 `true`,在生产环境下,一般需要配合 Node 中间件一起使用才能生效。[详见](#路由自动重定向)