diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..1ed2308e
Binary files /dev/null and b/.DS_Store differ
diff --git a/package.json b/package.json
index d5f9497e..7b702acc 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,9 @@
"prettier": "prettier .prettierrc.js -w packages/**/*.{ts,tsx,js,jsx}",
"build:inula": "pnpm -F openinula build",
"test:inula": "pnpm -F openinula test",
+ "test:inula-intl": "pnpm -F inula-intl test",
+ "test:inula-request": "pnpm -F inula-request test",
+ "test:inula-router": "pnpm -F inula-router test",
"build:inula-cli": "pnpm -F inula-cli build",
"build:inula-intl": "pnpm -F inula-intl build",
"build:inula-request": "pnpm -F inula-request build",
@@ -22,46 +25,48 @@
]
},
"devDependencies": {
- "@babel/core": "7.16.7",
- "@babel/plugin-proposal-class-properties": "7.16.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
- "@babel/plugin-proposal-object-rest-spread": "7.16.7",
- "@babel/plugin-proposal-optional-chaining": "7.16.7",
- "@babel/plugin-proposal-private-methods": "7.16.7",
- "@babel/plugin-proposal-private-property-in-object": "7.16.7",
- "@babel/plugin-syntax-jsx": "7.16.7",
- "@babel/plugin-transform-arrow-functions": "7.16.7",
- "@babel/plugin-transform-block-scoped-functions": "7.16.7",
- "@babel/plugin-transform-block-scoping": "7.16.7",
- "@babel/plugin-transform-classes": "7.16.7",
- "@babel/plugin-transform-computed-properties": "7.16.7",
- "@babel/plugin-transform-destructuring": "7.16.7",
- "@babel/plugin-transform-for-of": "7.16.7",
- "@babel/plugin-transform-literals": "7.16.7",
- "@babel/plugin-transform-object-assign": "7.16.7",
- "@babel/plugin-transform-object-super": "7.16.7",
- "@babel/plugin-transform-parameters": "7.16.7",
- "@babel/plugin-transform-react-jsx": "7.16.7",
- "@babel/plugin-transform-react-jsx-source": "^7.16.7",
- "@babel/plugin-transform-runtime": "7.16.7",
- "@babel/plugin-transform-shorthand-properties": "7.16.7",
- "@babel/plugin-transform-spread": "7.16.7",
- "@babel/plugin-transform-template-literals": "7.16.7",
- "@babel/preset-env": "7.16.7",
- "@babel/preset-typescript": "7.16.7",
- "@babel/runtime": "7.16.7",
- "@commitlint/cli": "^18.4.4",
- "@commitlint/config-conventional": "^18.4.4",
- "@rollup/plugin-babel": "^5.3.1",
- "@rollup/plugin-node-resolve": "^13.3.0",
+ "@babel/core": "7.23.7",
+ "@babel/plugin-proposal-class-properties": "7.18.6",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
+ "@babel/plugin-proposal-object-rest-spread": "7.20.7",
+ "@babel/plugin-proposal-optional-chaining": "7.21.0",
+ "@babel/plugin-proposal-private-methods": "7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.11",
+ "@babel/plugin-syntax-jsx": "7.23.3",
+ "@babel/plugin-transform-arrow-functions": "7.23.3",
+ "@babel/plugin-transform-block-scoped-functions": "7.23.3",
+ "@babel/plugin-transform-block-scoping": "7.23.4",
+ "@babel/plugin-transform-classes": "7.23.8",
+ "@babel/plugin-transform-computed-properties": "7.23.3",
+ "@babel/plugin-transform-destructuring": "7.23.3",
+ "@babel/plugin-transform-for-of": "7.23.6",
+ "@babel/plugin-transform-literals": "7.23.3",
+ "@babel/plugin-transform-object-assign": "7.23.3",
+ "@babel/plugin-transform-object-super": "7.23.3",
+ "@babel/plugin-transform-parameters": "7.23.3",
+ "@babel/plugin-transform-react-jsx": "7.23.4",
+ "@babel/plugin-transform-react-jsx-source": "^7.23.3",
+ "@babel/plugin-transform-runtime": "7.23.7",
+ "@babel/plugin-transform-shorthand-properties": "7.23.3",
+ "@babel/plugin-transform-spread": "7.23.3",
+ "@babel/plugin-transform-template-literals": "7.23.3",
+ "@babel/preset-env": "7.23.8",
+ "@babel/preset-typescript": "7.23.3",
+ "@babel/runtime": "7.23.8",
+ "@commitlint/cli": "^17.8.1",
+ "@commitlint/config-conventional": "^17.8.1",
+ "@rollup/plugin-babel": "^6.0.4",
+ "@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^4.0.0",
- "@types/jest": "^26.0.24",
+ "@types/jest": "^29.5.11",
"@types/node": "^17.0.18",
- "@typescript-eslint/eslint-plugin": "4.8.0",
- "@typescript-eslint/parser": "4.8.0",
- "babel-jest": "^27.5.1",
+ "@typescript-eslint/eslint-plugin": "^6.18.1",
+ "@typescript-eslint/parser": "6.18.1",
+ "@babel/parser": "^7.24.7",
+ "magic-string": "^0.30.10",
+ "babel-jest": "^29.7.0",
"ejs": "^3.1.8",
- "eslint": "7.13.0",
+ "eslint": "^8.56.0",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-jest": "^22.15.0",
"eslint-plugin-no-function-declare-after-return": "^1.0.0",
@@ -72,9 +77,13 @@
"lint-staged": "^15.2.0",
"openinula": "workspace:*",
"prettier": "^3.1.1",
- "rollup": "^2.75.5",
+ "rollup": "^2.79.1",
+ "rollup-plugin-dts": "^6.1.0",
"rollup-plugin-execute": "^1.1.1",
"rollup-plugin-terser": "^7.0.2",
+ "rollup-plugin-esbuild": "^6.1.1",
+ "rollup-plugin-polyfill-node": "^0.13.0",
+ "ts-jest": "^29.1.1",
"typescript": "^4.9.5"
},
"engines": {
diff --git a/packages/create-inula/lib/generators/Simple-app/templates/vite/README.md b/packages/create-inula/lib/generators/Simple-app/templates/vite/README.md
deleted file mode 100644
index 542c363a..00000000
--- a/packages/create-inula/lib/generators/Simple-app/templates/vite/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# openinula + vite
-
-该模板提供了 `openinula` 工作在 `vite`的基础配置。
-> 请注意由于Vite插件有node版本限制,请使用`node -v`命令确认node版本大于等于node v18。
\ No newline at end of file
diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.jsx b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.jsx
deleted file mode 100644
index 75e326c4..00000000
--- a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula from 'openinula';
-import './styles.css';
-
-class App extends Inula.Component {
- render() {
- return (
-
-
-
欢迎来到 Inula 项目!
-
你已成功创建你的第一个 Inula 项目
-
-
-
-
开始吧
-
- 编辑 src/App.js
并保存以重新加载。
-
-
-
-
-
- );
- }
-}
-
-export default App;
diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.jsx b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.jsx
deleted file mode 100644
index c384f05a..00000000
--- a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula from 'openinula';
-import App from './App';
-
-Inula.render( , document.getElementById('root'));
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/index.js b/packages/create-inula/lib/generators/Simple-reactive-app/index.js
deleted file mode 100644
index 2d52bc4b..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-const BasicGenerator = require('../../BasicGenerator');
-
-class Generator extends BasicGenerator {
- prompting() {
- return this.prompt([
- {
- type: 'list',
- name: 'bundlerType',
- message: 'Please select the build type',
- choices: ['webpack', 'vite'],
- },
- ]).then(props => {
- this.prompts = props;
- });
- }
-
- writing() {
- const src = this.templatePath(this.prompts.bundlerType);
- const dest = this.destinationPath();
- this.writeFiles(src, dest, {
- context: {
- ...this.prompts,
- },
- });
- }
-}
-
-module.exports = Generator;
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/meta.json b/packages/create-inula/lib/generators/Simple-reactive-app/meta.json
deleted file mode 100644
index 8f8f9e66..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/meta.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "description": "simple reactive app template."
-}
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/README.md b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/README.md
deleted file mode 100644
index 542c363a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# openinula + vite
-
-该模板提供了 `openinula` 工作在 `vite`的基础配置。
-> 请注意由于Vite插件有node版本限制,请使用`node -v`命令确认node版本大于等于node v18。
\ No newline at end of file
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/index.html b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/index.html
deleted file mode 100644
index 9f37946a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- My Inula App
-
-
-
-
-
-
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/package.json b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/package.json
deleted file mode 100644
index f74bdda6..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/package.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "inula-vite-app",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "start": "vite",
- "build": "vite build"
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "dependencies": {
- "openinula": "0.0.0-experimental-20231201"
- },
- "devDependencies": {
- "@babel/core": "^7.21.4",
- "@babel/preset-env": "^7.21.4",
- "@babel/preset-react": "^7.18.6",
- "@vitejs/plugin-react": "^3.1.0",
- "@vitejs/plugin-react-refresh": "^1.3.6",
- "babel-plugin-import": "^1.13.6",
- "vite": "^4.2.1"
- }
-}
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/ReactiveComponent.jsx b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/ReactiveComponent.jsx
deleted file mode 100644
index cca83f05..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/ReactiveComponent.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula, { useComputed, useReactive, useRef } from 'openinula';
-
-function ReactiveComponent() {
- const renderCount = ++useRef(0).current;
-
- const data = useReactive({ count: 0 });
- const countText = useComputed(() => {
- return `计时: ${data.count.get()}`;
- });
-
- setInterval(() => {
- data.count.set(c => c + 1);
- }, 1000);
-
- return (
-
-
{countText}
-
组件渲染次数:{renderCount}
-
- );
-}
-
-export default ReactiveComponent;
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.css b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.css
deleted file mode 100644
index f08fdb8a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.css
+++ /dev/null
@@ -1,57 +0,0 @@
-* {
- box-sizing: border-box;
-}
-
-body,
-html {
- margin: 0;
- padding: 0;
- font-family: 'Montserrat', sans-serif;
- line-height: 1.6;
- color: #fff;
- background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%);
- height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.container {
- text-align: center;
-}
-
-.hero-title {
- font-size: 3em;
- margin-bottom: 20px;
-}
-
-.hero-subtitle {
- font-size: 1.5em;
- margin-bottom: 50px;
-}
-
-.content {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: 1vh;
-}
-
-.card {
- background: rgba(255, 255, 255, 0.1);
- border-radius: 5px;
- padding: 20px;
- width: 100%;
- box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
- backdrop-filter: blur(4px);
-}
-
-.card h2,
-.card p {
- color: #fff;
-}
-
-.card a {
- color: #fff;
- text-decoration: underline;
-}
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.jsx b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.jsx
deleted file mode 100644
index bab57690..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/src/index.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula from 'openinula';
-import ReactiveComponent from './ReactiveComponent';
-import './index.css';
-
-function App() {
- return (
-
-
-
欢迎来到 Inula 项目!
-
你已成功创建你的第一个响应式 Inula 项目
-
-
-
-
- );
-}
-
-Inula.render( , document.getElementById('root'));
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/vite.config.js b/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/vite.config.js
deleted file mode 100644
index 43065ece..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/vite/vite.config.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import react from '@vitejs/plugin-react';
-
-let alias = {
- react: 'openinula', // 新增
- 'react-dom': 'openinula', // 新增
- 'react/jsx-dev-runtime': 'openinula/jsx-dev-runtime',
-};
-
-export default {
- plugins: [react()],
- resolve: {
- alias,
- },
-};
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/package.json b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/package.json
deleted file mode 100644
index 783f626a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/package.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "inula-webpack-app",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "start": "webpack serve --mode development",
- "build": "webpack --mode production"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "openinula": "0.0.0-experimental-20231201"
- },
- "devDependencies": {
- "@babel/core": "^7.21.4",
- "@babel/preset-env": "^7.21.4",
- "@babel/preset-react": "^7.18.6",
- "babel-loader": "^9.1.2",
- "css-loader": "^6.7.3",
- "file-loader": "^6.2.0",
- "html-webpack-plugin": "^5.5.0",
- "style-loader": "^3.3.2",
- "url-loader": "^4.1.1",
- "webpack": "^5.77.0",
- "webpack-cli": "^5.0.1",
- "webpack-dev-server": "^4.13.2"
- }
-}
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/App.jsx b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/App.jsx
deleted file mode 100644
index a8b52c96..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/App.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula from 'openinula';
-import ReactiveComponent from './ReactiveComponent';
-import './styles.css';
-
-class App extends Inula.Component {
- render() {
- return (
-
-
-
欢迎来到 Inula 项目!
-
你已成功创建你的第一个响应式 Inula 项目
-
-
-
-
- );
- }
-}
-
-export default App;
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/ReactiveComponent.jsx b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/ReactiveComponent.jsx
deleted file mode 100644
index cca83f05..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/ReactiveComponent.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula, { useComputed, useReactive, useRef } from 'openinula';
-
-function ReactiveComponent() {
- const renderCount = ++useRef(0).current;
-
- const data = useReactive({ count: 0 });
- const countText = useComputed(() => {
- return `计时: ${data.count.get()}`;
- });
-
- setInterval(() => {
- data.count.set(c => c + 1);
- }, 1000);
-
- return (
-
-
{countText}
-
组件渲染次数:{renderCount}
-
- );
-}
-
-export default ReactiveComponent;
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.html b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.html
deleted file mode 100644
index c966e725..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- Inula App
-
-
-
-
-
-
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.jsx b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.jsx
deleted file mode 100644
index c384f05a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/index.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import Inula from 'openinula';
-import App from './App';
-
-Inula.render( , document.getElementById('root'));
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/styles.css b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/styles.css
deleted file mode 100644
index f08fdb8a..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/src/styles.css
+++ /dev/null
@@ -1,57 +0,0 @@
-* {
- box-sizing: border-box;
-}
-
-body,
-html {
- margin: 0;
- padding: 0;
- font-family: 'Montserrat', sans-serif;
- line-height: 1.6;
- color: #fff;
- background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%);
- height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.container {
- text-align: center;
-}
-
-.hero-title {
- font-size: 3em;
- margin-bottom: 20px;
-}
-
-.hero-subtitle {
- font-size: 1.5em;
- margin-bottom: 50px;
-}
-
-.content {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: 1vh;
-}
-
-.card {
- background: rgba(255, 255, 255, 0.1);
- border-radius: 5px;
- padding: 20px;
- width: 100%;
- box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
- backdrop-filter: blur(4px);
-}
-
-.card h2,
-.card p {
- color: #fff;
-}
-
-.card a {
- color: #fff;
- text-decoration: underline;
-}
diff --git a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/webpack.config.js b/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/webpack.config.js
deleted file mode 100644
index 8b46f7a5..00000000
--- a/packages/create-inula/lib/generators/Simple-reactive-app/templates/webpack/webpack.config.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-const path = require('path');
-const HtmlWebpackPlugin = require('html-webpack-plugin');
-
-module.exports = {
- entry: './src/index.jsx',
- output: {
- path: path.resolve(__dirname, 'dist'),
- filename: 'bundle.js',
- },
- module: {
- rules: [
- {
- test: /\.(js|jsx)$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: [
- '@babel/preset-env',
- [
- '@babel/preset-react',
- {
- runtime: 'automatic', // 新增
- importSource: 'openinula', // 新增
- },
- ],
- ],
- },
- },
- },
- {
- test: /\.css$/,
- use: ['style-loader', 'css-loader'],
- },
- {
- test: /\.(png|jpe?g|gif)$/i,
- use: [
- {
- loader: 'file-loader',
- options: {
- name: '[name].[ext]',
- outputPath: 'images/',
- publicPath: 'images/',
- },
- },
- ],
- },
- {
- test: /\.(woff|woff2|eot|ttf|otf)$/,
- use: ['file-loader'],
- },
- ],
- },
- plugins: [
- new HtmlWebpackPlugin({
- template: path.resolve(__dirname, 'src/index.html'),
- filename: 'index.html',
- }),
- ],
- devServer: {
- static: path.join(__dirname, 'dist'),
- compress: true,
- port: 9000,
- open: true,
- },
- resolve: {
- extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
- },
-};
diff --git a/packages/create-inula/lib/run.js b/packages/create-inula/lib/run.js
index d0ad06cd..a48cdacd 100644
--- a/packages/create-inula/lib/run.js
+++ b/packages/create-inula/lib/run.js
@@ -32,8 +32,8 @@ const generatorType = fs
});
const runGenerator = async (templatePath, { name = '', cwd = process.cwd(), args = {} }) => {
+ let currentPath;
return new Promise(resolve => {
- let currentPath;
if (name) {
mkdirp.sync(name);
currentPath = path.join(cwd, name);
diff --git a/packages/inula-cli/externals.d.ts b/packages/inula-cli/externals.d.ts
index 2fe4981b..ab03cf87 100644
--- a/packages/inula-cli/externals.d.ts
+++ b/packages/inula-cli/externals.d.ts
@@ -14,4 +14,3 @@
*/
declare module 'crequire';
-
diff --git a/packages/inula-cli/src/builtInPlugins/command/build/build.ts b/packages/inula-cli/src/builtInPlugins/command/build/build.ts
deleted file mode 100644
index ff9a89f8..00000000
--- a/packages/inula-cli/src/builtInPlugins/command/build/build.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import webpack from 'webpack';
-import { build } from 'vite';
-
-export default (api: any) => {
- api.registerCommand({
- name: 'build',
- description: 'build application for production',
- initialState: api.buildConfig,
- fn: async function (args: any, state: any) {
- switch (api.compileMode) {
- case 'webpack':
- if (state) {
- api.applyHook({ name: 'beforeCompile', args: state });
- state.forEach((s: any) => {
- webpack(s.config, (err: any, stats: any) => {
- if (err || stats.hasErrors()) {
- api.logger.error(`Build failed.err: ${err}, stats:${stats}`);
- }
- });
- });
- } else {
- api.logger.error(`Build failed. Can't find build config.`);
- }
- break;
- case 'vite':
- if (state) {
- api.applyHook({ name: 'beforeCompile' });
- build(state);
- } else {
- api.logger.error(`Build failed. Can't find build config.`);
- }
- break;
- }
- },
- });
-};
diff --git a/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts b/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts
index 400fbe53..9ffccf76 100644
--- a/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts
+++ b/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts
@@ -57,7 +57,7 @@ export default (api: API) => {
api.applyHook({ name: 'afterStartDevServer' });
});
} else {
- api.logger.error('Can\'t find config');
+ api.logger.error("Can't find config");
}
break;
case 'vite':
@@ -70,7 +70,7 @@ export default (api: API) => {
server.printUrls();
});
} else {
- api.logger.error('Can\'t find config');
+ api.logger.error("Can't find config");
}
break;
default:
diff --git a/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts b/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts
index 777aefe8..e71f4f96 100644
--- a/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts
+++ b/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts
@@ -33,7 +33,7 @@ export default (api: API) => {
args._.shift();
}
if (args._.length === 0) {
- api.logger.warn('Can\'t find any generate options.');
+ api.logger.warn("Can't find any generate options.");
return;
}
diff --git a/packages/inula-intl/.babelrc b/packages/inula-intl/.babelrc
new file mode 100644
index 00000000..3044a5a8
--- /dev/null
+++ b/packages/inula-intl/.babelrc
@@ -0,0 +1,42 @@
+{
+ "presets": [
+ ["@babel/preset-env", {
+ "targets": {
+ "node": "current"
+ }
+ }],
+ "@babel/preset-typescript",
+ "@babel/preset-react"
+ ],
+ "plugins": [
+ "@babel/plugin-syntax-jsx",
+ [
+ "@babel/plugin-transform-react-jsx",
+ {
+ "runtime": "automatic",
+ "importSource": "openinula"
+ }
+ ],
+ ["@babel/plugin-proposal-class-properties", { "loose": true }],
+ ["@babel/plugin-proposal-private-methods", { "loose": true }],
+ ["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
+ "@babel/plugin-transform-object-assign",
+ "@babel/plugin-transform-object-super",
+ ["@babel/plugin-proposal-object-rest-spread", { "loose": true, "useBuiltIns": true }],
+ ["@babel/plugin-transform-template-literals", { "loose": true }],
+ "@babel/plugin-transform-arrow-functions",
+ "@babel/plugin-transform-literals",
+ "@babel/plugin-transform-for-of",
+ "@babel/plugin-transform-block-scoped-functions",
+ "@babel/plugin-transform-classes",
+ "@babel/plugin-transform-shorthand-properties",
+ "@babel/plugin-transform-computed-properties",
+ "@babel/plugin-transform-parameters",
+ ["@babel/plugin-transform-spread", { "loose": true, "useBuiltIns": true }],
+ ["@babel/plugin-transform-block-scoping", { "throwIfClosureRequired": false }],
+ ["@babel/plugin-transform-destructuring", { "loose": true, "useBuiltIns": true }],
+ "@babel/plugin-transform-runtime",
+ "@babel/plugin-proposal-nullish-coalescing-operator",
+ "@babel/plugin-proposal-optional-chaining"
+ ]
+}
diff --git a/packages/inula-intl/babel.config.js b/packages/inula-intl/babel.config.js
deleted file mode 100644
index 7f94d715..00000000
--- a/packages/inula-intl/babel.config.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-const { preset } = require('./jest.config');
-module.exports = {
- presets: [
- [
- '@babel/preset-env',
- { targets: { node: 'current' } },
- ],
- ['@babel/preset-typescript'],
- [
- '@babel/preset-react',
- {
- runtime: 'automatic',
- importSource: 'openinula',
- },
- ],
- ],
-};
diff --git a/packages/inula-intl/build-type.js b/packages/inula-intl/build-type.js
index 0be14358..8e7f5dbd 100644
--- a/packages/inula-intl/build-type.js
+++ b/packages/inula-intl/build-type.js
@@ -7,12 +7,12 @@ function deleteFolder(filePath) {
if (fs.lstatSync(filePath).isDirectory()) {
const files = fs.readdirSync(filePath);
files.forEach(file => {
- const nectFilePath = path.join(filePath, file);
- const states = fs.lstatSync(nectFilePath);
+ const nextFilePath = path.join(filePath, file);
+ const states = fs.lstatSync(nextFilePath);
if (states.isDirectory()) {
- deleteFolder(nectFilePath);
+ deleteFolder(nextFilePath);
} else {
- fs.unlinkSync(nectFilePath);
+ fs.unlinkSync(nextFilePath);
}
});
fs.rmdirSync(filePath);
@@ -31,12 +31,12 @@ export function cleanUp(folders) {
return {
name: 'clean-up',
buildEnd() {
- folders.forEach(folder => deleteFolder(folder));
+ folders.forEach(f => deleteFolder(f));
},
};
}
-function builderTypeConfig() {
+function buildTypeConfig() {
return {
input: './build/@types/index.d.ts',
output: {
@@ -47,4 +47,4 @@ function builderTypeConfig() {
};
}
-export default [builderTypeConfig()];
+export default [buildTypeConfig()];
diff --git a/packages/inula-intl/example/App.tsx b/packages/inula-intl/example/App.tsx
index 8c460c57..4dfc8c3e 100644
--- a/packages/inula-intl/example/App.tsx
+++ b/packages/inula-intl/example/App.tsx
@@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula, { useState } from 'openinula';
+import { useState } from 'openinula';
import { IntlProvider } from '../index';
import zh from './locale/zh';
import en from './locale/en';
@@ -32,23 +32,29 @@ const App = () => {
const message = locale === 'zh' ? zh : en;
return (
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+ 切换语言
+
+
-
+
+
-
- 切换语言
-
-
+ >
);
};
diff --git a/packages/inula-intl/example/components/Example1.tsx b/packages/inula-intl/example/components/Example1.tsx
index 0befb70e..9562fc10 100644
--- a/packages/inula-intl/example/components/Example1.tsx
+++ b/packages/inula-intl/example/components/Example1.tsx
@@ -13,16 +13,16 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula from 'openinula';
import { useIntl } from '../../index';
const Example1 = () => {
- const { i18n } = useIntl();
+ const i18n = useIntl();
return (
useIntl方式测试Demo
{i18n.formatMessage({ id: 'text1' })}
+
{i18n.$t({ id: 'text1' })}
);
};
diff --git a/packages/inula-intl/example/components/Example2.tsx b/packages/inula-intl/example/components/Example2.tsx
index 74f73423..3aa28a6f 100644
--- a/packages/inula-intl/example/components/Example2.tsx
+++ b/packages/inula-intl/example/components/Example2.tsx
@@ -12,7 +12,6 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-import Inula from 'openinula';
import { FormattedMessage } from '../../index';
const Example2 = () => {
@@ -22,6 +21,9 @@ const Example2 = () => {
+
+ 123, testComponent2: 456 }} />
+
);
};
diff --git a/packages/inula-intl/example/components/Example3.tsx b/packages/inula-intl/example/components/Example3.tsx
index 27fdd904..70d58366 100644
--- a/packages/inula-intl/example/components/Example3.tsx
+++ b/packages/inula-intl/example/components/Example3.tsx
@@ -13,7 +13,6 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula from 'openinula';
import { FormattedMessage } from '../../index';
const Example3 = props => {
diff --git a/packages/inula-intl/example/components/Example4.tsx b/packages/inula-intl/example/components/Example4.tsx
index ec717934..f56cc4bf 100644
--- a/packages/inula-intl/example/components/Example4.tsx
+++ b/packages/inula-intl/example/components/Example4.tsx
@@ -13,7 +13,6 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula from 'openinula';
import { createIntl } from '../../index';
const Example4 = props => {
diff --git a/packages/inula-intl/example/components/Example5.tsx b/packages/inula-intl/example/components/Example5.tsx
index 6b343e81..897f1a17 100644
--- a/packages/inula-intl/example/components/Example5.tsx
+++ b/packages/inula-intl/example/components/Example5.tsx
@@ -13,23 +13,16 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula, { Component } from 'openinula';
import { injectIntl } from '../../index';
-class Example5 extends Component {
- public constructor(props: any, context) {
- super(props, context);
- }
-
- render() {
- const { intl } = this.props as any;
- return (
-
-
injectIntl方式测试Demo
-
{intl.formatMessage({ id: 'text4' })}
-
- );
- }
-}
-
+const Example5 = ({ intl }) => {
+ // 使用intl.formatMessage来获取国际化消息
+ console.log(intl + '------------intl-------------');
+ return (
+
+
injectIntl方式测试Demo
+
{intl.formatMessage({ id: 'text4' })}
+
+ );
+};
export default injectIntl(Example5);
diff --git a/packages/inula-intl/example/components/Example6.tsx b/packages/inula-intl/example/components/Example6.tsx
index 98a86729..ff220b4c 100644
--- a/packages/inula-intl/example/components/Example6.tsx
+++ b/packages/inula-intl/example/components/Example6.tsx
@@ -13,7 +13,6 @@
* See the Mulan PSL v2 for more details.
*/
-import Inula from 'openinula';
import { createIntl, createIntlCache, RawIntlProvider } from '../../index';
import Example6Child from './Example6Child';
@@ -21,7 +20,7 @@ const Example6 = (props: any) => {
const { locale, messages } = props;
const cache = createIntlCache();
- let i18n = createIntl({ locale: locale, messages: messages }, cache);
+ const i18n = createIntl({ locale: locale, messages: messages }, cache);
return (
diff --git a/packages/inula-intl/example/components/Example6Child.tsx b/packages/inula-intl/example/components/Example6Child.tsx
index 94e78714..6c19c105 100644
--- a/packages/inula-intl/example/components/Example6Child.tsx
+++ b/packages/inula-intl/example/components/Example6Child.tsx
@@ -15,7 +15,7 @@
import { useIntl } from '../../index';
-const Example6Child = (props: any) => {
+const Example6Child = () => {
const { formatMessage } = useIntl();
return (
diff --git a/packages/inula-intl/example/index.tsx b/packages/inula-intl/example/index.tsx
index f0ab1550..97910e39 100644
--- a/packages/inula-intl/example/index.tsx
+++ b/packages/inula-intl/example/index.tsx
@@ -12,7 +12,7 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-import * as Inula from 'openinula';
+import Inula from 'openinula';
import App from './App';
function render() {
diff --git a/packages/inula-intl/example/locale/en.ts b/packages/inula-intl/example/locale/en.ts
index 21462b48..3cde2724 100644
--- a/packages/inula-intl/example/locale/en.ts
+++ b/packages/inula-intl/example/locale/en.ts
@@ -19,4 +19,5 @@ export default {
text2: 'Welcome to the Inula-Intl component!',
text3: 'Welcome to the Inula-Intl component!',
text4: 'Welcome to the Inula-Intl component!',
+ text5: 'Render a component {testComponent1} {testComponent2}!',
};
diff --git a/packages/inula-intl/example/locale/zh.ts b/packages/inula-intl/example/locale/zh.ts
index 4051f195..d392b30c 100644
--- a/packages/inula-intl/example/locale/zh.ts
+++ b/packages/inula-intl/example/locale/zh.ts
@@ -18,4 +18,5 @@ export default {
text2: '欢迎使用国际化组件!',
text3: '欢迎使用国际化组件!',
text4: '欢迎使用国际化组件!',
+ text5: '渲染一个组件 {testComponent1} {testComponent2}!',
};
diff --git a/packages/inula-intl/index.ts b/packages/inula-intl/index.ts
index 776255b8..401e582e 100644
--- a/packages/inula-intl/index.ts
+++ b/packages/inula-intl/index.ts
@@ -22,7 +22,7 @@ import I18nProvider from './src/core/components/I18nProvider';
import injectIntl, { I18nContext, InjectProvider } from './src/core/components/InjectI18n';
import useI18n from './src/core/hook/useI18n';
import createI18n from './src/core/createI18n';
-import { InjectedIntl, MessageDescriptor } from './src/types/interfaces';
+import { MessageDescriptor } from './src/types/interfaces';
// 函数API
export {
I18n,
@@ -36,7 +36,7 @@ export {
// 组件
export {
FormattedMessage,
- I18nContext,
+ I18nContext as IntlContext,
I18nProvider as IntlProvider,
injectIntl as injectIntl,
InjectProvider as RawIntlProvider,
@@ -64,7 +64,3 @@ export function defineMessages(msg: T): T {
return msg;
}
-
-export interface InjectedIntlProps {
- intl: InjectedIntl;
-}
diff --git a/packages/inula-intl/jest.config.js b/packages/inula-intl/jest.config.js
index 84592edf..6c82cf3c 100644
--- a/packages/inula-intl/jest.config.js
+++ b/packages/inula-intl/jest.config.js
@@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details.
*/
-module.exports = {
+export default {
coverageDirectory: 'coverage',
resetModules: true,
preset: 'ts-jest/presets/js-with-ts',
@@ -30,8 +30,10 @@ module.exports = {
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
+ diagnostics: false,
},
},
+ testPathIgnorePatterns: ['\\\\node_modules\\\\'],
testEnvironment: 'jsdom',
};
diff --git a/packages/inula-intl/package.json b/packages/inula-intl/package.json
index 71793344..6474226c 100644
--- a/packages/inula-intl/package.json
+++ b/packages/inula-intl/package.json
@@ -3,13 +3,13 @@
"version": "0.0.5",
"description": "",
"main": "build/intl.umd.js",
- "type": "commonjs",
+ "type": "module",
"types": "build/@types/index.d.ts",
"scripts": {
"demo-serve": "webpack serve --mode=development",
- "build": "rollup --config rollup.config.js && npm run build-types",
+ "build": "rollup --config rollup.config.js && npm run build-types ",
"build-types": "tsc -p tsconfig.json && rollup -c build-type.js",
- "test": "jest --config jest.config.js",
+ "test": "jest --no-cache --config jest.config.js",
"test-c": "jest --coverage"
},
"repository": {
@@ -17,8 +17,7 @@
"url": ""
},
"files": [
- "build",
- "README.md"
+ "/build"
],
"keywords": [],
"author": "",
@@ -27,35 +26,23 @@
"openinula": ">=0.1.1"
},
"devDependencies": {
- "@babel/core": "7.21.3",
- "@babel/preset-env": "^7.16.7",
"@babel/preset-react": "^7.9.4",
- "@babel/preset-typescript": "7.16.7",
- "@rollup/plugin-babel": "^6.0.3",
- "@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-typescript": "^11.0.0",
- "rollup-plugin-dts": "^6.1.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
- "@types/node": "^16.18.27",
"@types/react": "18.0.25",
- "babel": "^6.23.0",
- "babel-jest": "^29.5.0",
"babel-loader": "^9.1.2",
"html-webpack-plugin": "^5.5.1",
- "jest": "29.3.1",
"jest-environment-jsdom": "^29.5.0",
"jsdom": "^21.1.1",
- "prettier": "^2.8.7",
- "rollup": "^2.0.0",
+ "react": "18.2.0-h3",
+ "react-dom": "18.2.0-h3",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^1.1.0",
- "rollup-plugin-terser": "^5.3.0",
- "tslib": "^2.6.1",
- "ts-jest": "29.0.3",
+ "rollup-plugin-visualizer": "^5.10.0",
"ts-node": "10.9.1",
- "typescript": "4.9.3",
- "webpack": "^5.81.0",
+ "tslib": "^2.6.1",
+ "webpack": "^5.72.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.13.3"
}
diff --git a/packages/inula-intl/report.html b/packages/inula-intl/report.html
new file mode 100644
index 00000000..7bfe3ecf
--- /dev/null
+++ b/packages/inula-intl/report.html
@@ -0,0 +1,4842 @@
+
+
+
+
+
+
+
+ Rollup Visualizer
+
+
+
+
+
+
+
+
+
diff --git a/packages/inula-intl/rollup.config.js b/packages/inula-intl/rollup.config.js
index 86cd6fb2..6e76aad6 100644
--- a/packages/inula-intl/rollup.config.js
+++ b/packages/inula-intl/rollup.config.js
@@ -19,6 +19,7 @@ import babel from '@rollup/plugin-babel';
import nodeResolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
+import { visualizer } from 'rollup-plugin-visualizer';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -29,34 +30,55 @@ const output = path.join(__dirname, '/build');
const extensions = ['.js', '.ts', '.tsx'];
-export default {
- input: entry,
- output: [
+const BuildConfig = mode => {
+ const prod = mode.startsWith('prod');
+ const outputList = [
{
- file: path.resolve(output, 'intl.umd.js'),
- name: 'InulaI18n',
- format: 'umd',
+ file: path.join(output, `cjs/intl.${prod ? 'min.' : ''}js`),
+ sourcemap: 'true',
+ format: 'cjs',
+ globals: {
+ openinula: 'Inula',
+ },
},
{
- file: path.resolve(output, 'intl.esm-browser.js'),
+ file: path.join(output, `umd/intl.${prod ? 'min.' : ''}js`),
+ name: 'InulaI18n',
+ sourcemap: 'true',
+ format: 'umd',
+ globals: {
+ openinula: 'Inula',
+ },
+ },
+ ];
+ if (!prod) {
+ outputList.push({
+ file: path.join(output, 'esm/intl.js'),
+ sourcemap: 'true',
format: 'esm',
- }
- ],
- plugins: [
- nodeResolve({
- extensions,
- modulesOnly: true,
- }),
- babel({
- exclude: 'node_modules/**',
- configFile: path.join(__dirname, '/babel.config.js'),
- extensions,
- }),
- typescript({
- tsconfig: 'tsconfig.json',
- include: ['./**/*.ts', './**/*.tsx'],
- }),
- terser(),
- ],
- external: ['openinula', 'react', 'react-dom'],
+ });
+ }
+ return {
+ input: entry,
+ output: outputList,
+ plugins: [
+ nodeResolve({
+ extensions,
+ modulesOnly: true,
+ }),
+ babel({
+ exclude: 'node_modules/**',
+ configFile: path.join(__dirname, '/.babelrc'),
+ extensions,
+ babelHelpers: 'runtime',
+ }),
+ typescript({
+ tsconfig: 'tsconfig.json',
+ include: ['./**/*.ts', './**/*.tsx'],
+ }),
+ terser(),
+ ],
+ external: ['openinula', 'react', 'react-dom'],
+ };
};
+export default [BuildConfig('dev'), BuildConfig('prod')];
diff --git a/packages/inula-intl/src/constants/index.ts b/packages/inula-intl/src/constants/index.ts
index c33b5127..f676cee3 100644
--- a/packages/inula-intl/src/constants/index.ts
+++ b/packages/inula-intl/src/constants/index.ts
@@ -18,8 +18,13 @@
* \\x[a-fA-F0-9]{2} 匹配形如 \x0A 的十六进制转义字符。
* [nrtf'"] 匹配常见的转义字符:\n(换行符)、\r(回车符)、\t(制表符)、\f(换页符)、\'(单引号)和 \"(双引号)。
*/
-export const UNICODE_REG = /\\(?:u\{[a-fA-F0-9]+}|x[a-fA-F0-9]{2}|[nrtf'"])/g;
+export const UNICODE_REG: RegExp = /\\(?:u\{[a-fA-F0-9]+}|x[a-fA-F0-9]{2}|[nrtf'"])/g;
+export const STICKY_FLAG: string = 'ym';
+export const GLOBAL_FLAG: string = 'gm';
+export const VERTICAL_LINE: string = '|';
+export const UNICODE_FLAG: string = 'u';
+export const STATE_GROUP_START_INDEX: number = 1;
// Inula 需要被保留静态常量
export const INULA_STATICS = {
childContextTypes: true,
@@ -76,3 +81,22 @@ export const INULA_MEMO_STATICS = {
// 默认复数规则
export const DEFAULT_PLURAL_KEYS = ['zero', 'one', 'two', 'few', 'many', 'other'];
+
+export const voidElementTags = [
+ 'area',
+ 'base',
+ 'br',
+ 'col',
+ 'embed',
+ 'hr',
+ 'img',
+ 'input',
+ 'keygen',
+ 'link',
+ 'meta',
+ 'param',
+ 'source',
+ 'track',
+ 'wbr',
+ 'menuitem',
+];
diff --git a/packages/inula-intl/src/core/I18n.ts b/packages/inula-intl/src/core/I18n.ts
index 337166d1..503f080d 100644
--- a/packages/inula-intl/src/core/I18n.ts
+++ b/packages/inula-intl/src/core/I18n.ts
@@ -18,30 +18,48 @@ import DateTimeFormatter from '../format/fomatters/DateTimeFormatter';
import NumberFormatter from '../format/fomatters/NumberFormatter';
import { getFormatMessage } from '../format/getFormatMessage';
import { I18nCache, I18nProps, MessageDescriptor, MessageOptions } from '../types/interfaces';
-import { Locale, Locales, Messages, AllLocaleConfig, AllMessages, LocaleConfig, Error, Events } from '../types/types';
+import {
+ Locale,
+ Locales,
+ Messages,
+ AllLocaleConfig,
+ AllMessages,
+ LocaleConfig,
+ Error,
+ Events,
+ InulaNode,
+} from '../types/types';
import creatI18nCache from '../format/cache/cache';
+import { isValidElement } from 'openinula';
export class I18n extends EventDispatcher {
public locale: Locale;
public locales: Locales;
+ public defaultLocale?: Locale;
+ public timeZone?: string;
+ private allMessages: AllMessages;
private readonly _localeConfig: AllLocaleConfig;
- private readonly allMessages: AllMessages;
- public readonly error?: Error;
+ public readonly onError?: Error;
public readonly cache?: I18nCache;
constructor(props: I18nProps) {
super();
- this.locale = 'en';
+ this.defaultLocale = 'en';
+ this.locale = this.defaultLocale;
this.locales = this.locale || '';
this.allMessages = {};
this._localeConfig = {};
- this.error = props.error;
+ this.onError = props.onError;
+ this.timeZone = '';
this.loadMessage(props.messages);
if (props.localeConfig) {
this.loadLocaleConfig(props.localeConfig);
}
+ if (props.messages) {
+ this.changeMessage(props.messages);
+ }
if (props.locale || props.locales) {
this.changeLanguage(props.locale!, props.locales);
@@ -93,6 +111,11 @@ export class I18n extends EventDispatcher {
}
}
+ changeMessage(messages: AllMessages) {
+ this.allMessages = messages;
+ this.emit('change');
+ }
+
// 加载messages
loadMessage(localeOrMessages: Locale | AllMessages | undefined, messages?: Messages) {
if (messages) {
@@ -118,9 +141,21 @@ export class I18n extends EventDispatcher {
formatMessage(
id: MessageDescriptor | string,
values: Record | undefined = {},
- { message, context, formatOptions }: MessageOptions = {}
+ { messages, context, formatOptions }: MessageOptions = {}
) {
- return getFormatMessage(this, id, values, { message, context, formatOptions });
+ // 在多次渲染时,保证存储component不丢失
+ const components: { [key: string]: InulaNode } = {};
+ const tempValues: Record = { ...values };
+ if (tempValues) {
+ Object.keys(tempValues).forEach((key, index) => {
+ const value = tempValues[key];
+ if (!isValidElement(value)) return;
+ // 将inula元素暂存
+ components[index] = value;
+ tempValues[key] = `<${index}/>`;
+ });
+ }
+ return getFormatMessage(this, id, tempValues, { messages, context, formatOptions }, components!);
}
formatDate(value: string | Date, formatOptions?: Intl.DateTimeFormatOptions): string {
diff --git a/packages/inula-intl/src/core/components/FormattedMessage.tsx b/packages/inula-intl/src/core/components/FormattedMessage.tsx
index f2a03e0a..c91464d2 100644
--- a/packages/inula-intl/src/core/components/FormattedMessage.tsx
+++ b/packages/inula-intl/src/core/components/FormattedMessage.tsx
@@ -12,7 +12,7 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-import Inula, { Children, Fragment } from 'openinula';
+import { Children, Fragment } from 'openinula';
import { FormattedMessageProps } from '../../types/interfaces';
import useI18n from '../hook/useI18n';
@@ -22,28 +22,17 @@ import useI18n from '../hook/useI18n';
* @constructor
*/
function FormattedMessage(props: FormattedMessageProps) {
- const { i18n } = useI18n();
- const {
- id,
- values,
- messages,
- formatOptions,
- context,
- tagName: TagName = Fragment,
- children,
- comment,
- useMemorize,
- }: any = props;
+ const { formatMessage } = useI18n();
+ const { id, values, messages, formatOptions, context, tagName: TagName = Fragment, children, comment }: any = props;
const formatMessageOptions = {
comment,
messages,
context,
- useMemorize,
formatOptions,
};
- let formattedMessage = i18n.formatMessage(id, values, formatMessageOptions);
+ const formattedMessage = formatMessage(id, values, formatMessageOptions);
if (typeof children === 'function') {
const childNodes = Array.isArray(formattedMessage) ? formattedMessage : [formattedMessage];
diff --git a/packages/inula-intl/src/core/components/I18nProvider.tsx b/packages/inula-intl/src/core/components/I18nProvider.tsx
index 18d8f896..3ef7e203 100644
--- a/packages/inula-intl/src/core/components/I18nProvider.tsx
+++ b/packages/inula-intl/src/core/components/I18nProvider.tsx
@@ -12,10 +12,10 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-import Inula, { useRef, useState, useEffect, useMemo } from 'openinula';
+import { useRef, useState, useEffect, useMemo } from 'openinula';
import { InjectProvider } from './InjectI18n';
import I18n, { createI18nInstance } from '../I18n';
-import { I18nProviderProps } from '../../types/types';
+import { AllMessages, I18nProviderProps, Messages } from '../../types/types';
/**
* 用于为应用程序提供国际化的格式化功能,管理程序中的语言文本信息和本地化资源信息
@@ -23,28 +23,31 @@ import { I18nProviderProps } from '../../types/types';
* @constructor
*/
const I18nProvider = (props: I18nProviderProps) => {
- const { locale, messages, children } = props;
+ const { locale, messages, children, i18n } = props;
- const i18n = useMemo(() => {
- return createI18nInstance({
- locale: locale,
- messages: messages,
- });
- }, [locale, messages]);
+ const i18nInstance =
+ i18n ||
+ useMemo(() => {
+ return createI18nInstance({
+ locale: locale,
+ messages: messages,
+ });
+ }, [locale, messages]);
// 使用useRef保存上次的locale值
- const localeRef = useRef(i18n.locale);
-
- const [context, setContext] = useState(i18n);
+ const localeRef = useRef(i18nInstance.locale);
+ const localeMessage = useRef(i18nInstance.messages);
+ const [context, setContext] = useState(i18nInstance);
useEffect(() => {
const handleChange = () => {
- if (localeRef.current !== i18n.locale) {
- localeRef.current = i18n.locale;
- setContext(i18n);
+ if (localeRef.current !== i18nInstance.locale || localeMessage.current !== i18nInstance.messages) {
+ localeRef.current = i18nInstance.locale;
+ localeMessage.current = i18nInstance.messages;
+ setContext(i18nInstance);
}
};
- let removeListener = i18n.on('change', handleChange);
+ const removeListener = i18nInstance.on('change', handleChange);
// 手动触发一次 handleChange,以确保 context 的正确性
handleChange();
@@ -53,7 +56,7 @@ const I18nProvider = (props: I18nProviderProps) => {
return () => {
removeListener();
};
- }, [i18n]);
+ }, [i18nInstance]);
// 提供一个Provider组件
return {children} ;
diff --git a/packages/inula-intl/src/core/components/InjectI18n.tsx b/packages/inula-intl/src/core/components/InjectI18n.tsx
index 82589098..20436609 100644
--- a/packages/inula-intl/src/core/components/InjectI18n.tsx
+++ b/packages/inula-intl/src/core/components/InjectI18n.tsx
@@ -31,13 +31,16 @@ export const InjectProvider = Provider;
function injectI18n(Component, options?: InjectOptions): any {
const {
isUsingForwardRef = false, // 默认不使用
+ ensureContext = false,
} = options || {};
// 定义一个名为 WrappedI18n 的函数组件,接收传入组件的 props 和 forwardedRef,返回传入组件并注入 i18n
const WrappedI18n = props => (
{context => {
- isVariantI18n(context);
+ if (ensureContext) {
+ isVariantI18n(context);
+ }
const i18nProps = {
intl: context,
diff --git a/packages/inula-intl/src/core/createI18n.ts b/packages/inula-intl/src/core/createI18n.ts
index 4629a8fc..ab41b67b 100644
--- a/packages/inula-intl/src/core/createI18n.ts
+++ b/packages/inula-intl/src/core/createI18n.ts
@@ -13,20 +13,29 @@
* See the Mulan PSL v2 for more details.
*/
import { configProps, I18nCache } from '../types/interfaces';
-import I18n, { createI18nInstance } from './I18n';
+import { createI18nInstance } from './I18n';
import creatI18nCache from '../format/cache/cache';
+import { IntlType } from '../types/types';
/**
* createI18n hook函数,用于创建国际化i8n实例,以进行相关的数据操作
*/
-export const createI18n = (config: configProps, cache?: I18nCache): I18n => {
+export const createI18n = (config: configProps, cache?: I18nCache): IntlType => {
const { locale, defaultLocale, messages } = config;
- return createI18nInstance({
- locale: locale || defaultLocale || 'zh',
+ const i18n = createI18nInstance({
+ locale: locale || defaultLocale || 'en',
messages: messages,
cache: cache ?? creatI18nCache(),
});
+ return {
+ i18n,
+ ...config,
+ formatMessage: i18n.formatMessage.bind(i18n),
+ formatNumber: i18n.formatNumber.bind(i18n),
+ formatDate: i18n.formatDate.bind(i18n),
+ $t: i18n.formatMessage.bind(i18n),
+ };
};
export default createI18n;
diff --git a/packages/inula-intl/src/core/hook/useI18n.ts b/packages/inula-intl/src/core/hook/useI18n.ts
index ae0b7f2d..ffcf709f 100644
--- a/packages/inula-intl/src/core/hook/useI18n.ts
+++ b/packages/inula-intl/src/core/hook/useI18n.ts
@@ -12,7 +12,7 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-import Inula, { useContext } from 'openinula';
+import { useContext, useMemo } from 'openinula';
import utils from '../../utils/utils';
import { I18nContext } from '../components/InjectI18n';
import I18n from '../I18n';
@@ -23,15 +23,22 @@ import { IntlType } from '../../types/types';
* 使用 useI18n 钩子函数可以更方便地在函数组件中进行国际化操作
*/
function useI18n(): IntlType {
- const i18nContext = useContext(I18nContext);
- utils.isVariantI18n(i18nContext);
- const i18n = i18nContext;
- return {
- i18n: i18n,
- formatMessage: i18n.formatMessage.bind(i18n),
- formatNumber: i18n.formatNumber.bind(i18n),
- formatDate: i18n.formatDate.bind(i18n),
- };
+ const i18n = useContext(I18nContext);
+ utils.isVariantI18n(i18n);
+ return useMemo(() => {
+ return {
+ i18n: i18n,
+ locale: i18n.locale,
+ messages: i18n.messages,
+ defaultLocale: i18n.defaultLocale,
+ timeZone: i18n.timeZone,
+ onError: i18n.onError,
+ formatMessage: i18n.formatMessage.bind(i18n),
+ formatNumber: i18n.formatNumber.bind(i18n),
+ formatDate: i18n.formatDate.bind(i18n),
+ $t: i18n.formatMessage.bind(i18n),
+ };
+ }, [i18n]);
}
export default useI18n;
diff --git a/packages/inula-intl/src/format/Translation.ts b/packages/inula-intl/src/format/Translation.ts
index 20f27b77..bf7830c9 100644
--- a/packages/inula-intl/src/format/Translation.ts
+++ b/packages/inula-intl/src/format/Translation.ts
@@ -16,7 +16,7 @@
import { CompiledMessage, Locale, LocaleConfig, Locales } from '../types/types';
import generateFormatters from './generateFormatters';
import { FormatOptions, I18nCache } from '../types/interfaces';
-import { createIntlCache } from '../../index';
+import creatI18nCache from './cache/cache';
/**
* 获取翻译结果
@@ -28,12 +28,18 @@ class Translation {
private readonly localeConfig: Record;
private readonly cache: I18nCache;
- constructor(compiledMessage, locale, locales, localeConfig, cache?) {
+ constructor(
+ compiledMessage: CompiledMessage,
+ locale: Locale,
+ locales: Locales,
+ localeConfig: LocaleConfig,
+ cache?: I18nCache
+ ) {
this.compiledMessage = compiledMessage;
this.locale = locale;
this.locales = locales;
this.localeConfig = localeConfig;
- this.cache = cache ?? createIntlCache;
+ this.cache = cache ?? creatI18nCache();
}
/**
@@ -53,7 +59,7 @@ class Translation {
const value = values[name];
const formatter = formatters[type](value, format);
- let message;
+ let message: any;
if (typeof formatter === 'function') {
message = formatter(textFormatter); // 递归调用
} else {
@@ -68,8 +74,7 @@ class Translation {
const textFormatter = createTextFormatter(this.locale, this.locales, values, formatOptions, this.localeConfig);
// 通过递归方法formatCore进行格式化处理
- const result = this.formatMessage(this.compiledMessage, textFormatter);
- return result; // 返回要格式化的结果
+ return this.formatMessage(this.compiledMessage, textFormatter); // 返回要格式化的结果
}
formatMessage(compiledMessage: CompiledMessage, textFormatter: (...args: any[]) => any) {
diff --git a/packages/inula-intl/src/format/fomatters/PluralFormatter.ts b/packages/inula-intl/src/format/fomatters/PluralFormatter.ts
index c0db6c9e..d149ae3f 100644
--- a/packages/inula-intl/src/format/fomatters/PluralFormatter.ts
+++ b/packages/inula-intl/src/format/fomatters/PluralFormatter.ts
@@ -17,7 +17,7 @@ import utils from '../../utils/utils';
import NumberFormatter from './NumberFormatter';
import { Locale, Locales } from '../../types/types';
import { I18nCache } from '../../types/interfaces';
-import { createIntlCache } from '../../../index';
+import creatI18nCache from '../cache/cache';
/**
* 复数格式化
@@ -29,12 +29,12 @@ class PluralFormatter {
private readonly message: any;
private readonly cache: I18nCache;
- constructor(locale, locales, value, message, cache?) {
+ constructor(locale: Locale, locales: Locales, value: any, message: any, cache?:I18nCache) {
this.locale = locale;
this.locales = locales;
this.value = value;
this.message = message;
- this.cache = cache ?? createIntlCache();
+ this.cache = cache ?? creatI18nCache();
}
// 将 message中的“#”替换为指定数字value,并返回新的字符串或者字符串数组
diff --git a/packages/inula-intl/src/format/fomatters/SelectFormatter.ts b/packages/inula-intl/src/format/fomatters/SelectFormatter.ts
index 1ff3e5b6..f61ee0af 100644
--- a/packages/inula-intl/src/format/fomatters/SelectFormatter.ts
+++ b/packages/inula-intl/src/format/fomatters/SelectFormatter.ts
@@ -14,7 +14,7 @@
*/
import utils from '../../utils/utils';
-import { Locale } from '../../types/types';
+import {Locale, SelectPool} from '../../types/types';
import { I18nCache } from '../../types/interfaces';
/**
@@ -26,12 +26,12 @@ class SelectFormatter {
private readonly locale: Locale;
private readonly cache: I18nCache;
- constructor(locale, cache) {
+ constructor(locale: Locale, cache: I18nCache) {
this.locale = locale;
this.cache = cache;
}
- getRule(value, rules) {
+ getRule(value: SelectPool, rules: any) {
if (this.cache.select) {
// 创建key,用于唯一标识
const cacheKey = utils.generateKey(this.locale, rules);
diff --git a/packages/inula-intl/src/format/generateFormatters.ts b/packages/inula-intl/src/format/generateFormatters.ts
index 567b9f9b..92c58693 100644
--- a/packages/inula-intl/src/format/generateFormatters.ts
+++ b/packages/inula-intl/src/format/generateFormatters.ts
@@ -19,25 +19,23 @@ import { DatePool, Locale, Locales, SelectPool } from '../types/types';
import PluralFormatter from './fomatters/PluralFormatter';
import SelectFormatter from './fomatters/SelectFormatter';
import { FormatOptions, I18nCache, IntlMessageFormat } from '../types/interfaces';
-import cache from './cache/cache';
/**
* 默认格式化接口
*/
const generateFormatters = (
- locale: Locale | Locales,
+ locale: Locale,
locales: Locales,
localeConfig: Record = { plurals: undefined },
formatOptions: FormatOptions = {}, // 自定义格式对象
cache: I18nCache
): IntlMessageFormat => {
- locale = locales || locale;
const { plurals } = localeConfig;
/**
* 样式函数 ,根据格式获取格式样式, 如货币百分比, 返回相应的格式的对象,如果没有设定格式,则返回一个空对象
* @param formatOption
*/
- const getStyleOption = formatOption => {
+ const getStyleOption = (formatOption: string | number) => {
if (typeof formatOption === 'string') {
return formatOptions[formatOption] || { option: formatOption };
} else {
@@ -58,14 +56,14 @@ const generateFormatters = (
return pluralFormatter.replaceSymbol.bind(pluralFormatter);
},
- selectordinal: (value: number, { offset = 0, ...rules }, useMemorize?) => {
+ selectordinal: (value: number, { offset = 0, ...rules }) => {
const message = rules[value] || rules[(plurals as any)?.(value - offset, true)] || rules.other;
- const pluralFormatter = new PluralFormatter(locale, locales, value - offset, message, useMemorize);
+ const pluralFormatter = new PluralFormatter(locale, locales, value - offset, message, cache);
return pluralFormatter.replaceSymbol.bind(pluralFormatter);
},
// 选择规则,如果规则对象中包含与该值相对应的属性,则返回该属性的值;否则,返回 "other" 属性的值。
- select: (value: SelectPool, formatRules) => {
+ select: (value: SelectPool, formatRules: any) => {
const selectFormatter = new SelectFormatter(locale, cache);
return selectFormatter.getRule(value, formatRules);
},
@@ -75,17 +73,16 @@ const generateFormatters = (
return new NumberFormatter(locales, getStyleOption(formatOption), cache).numberFormat(value);
},
- // 用于将日期格式化为字符串,接受一个日期对象和一个格式化规则。它会根据规则返回格式化后的字符串。
/**
+ * 用于将日期格式化为字符串,接受一个日期对象和一个格式化规则。它会根据规则返回格式化后的字符串。
* eg: { year: 'numeric', month: 'long', day: 'numeric' } 是一个用于指定DateTimeFormatter如何将日期对象转换为字符串的参数。
* \year: 'numeric' 表示年份的表示方式是数字形式(比如2023)。
* month: 'long' 表示月份的表示方式是全名(比如January)。
* day: 'numeric' 表示日期的表示方式是数字形式(比如1号)。
* @param value
* @param formatOption { year: 'numeric', month: 'long', day: 'numeric' }
- * @param useMemorize
*/
- dateTimeFormat: (value: DatePool, formatOption) => {
+ dateTimeFormat: (value: DatePool, formatOption: any) => {
return new DateTimeFormatter(locales, getStyleOption(formatOption), cache).dateTimeFormat(value, formatOption);
},
diff --git a/packages/inula-intl/src/format/getFormatMessage.ts b/packages/inula-intl/src/format/getFormatMessage.ts
index 6e34b6f5..cc55eb08 100644
--- a/packages/inula-intl/src/format/getFormatMessage.ts
+++ b/packages/inula-intl/src/format/getFormatMessage.ts
@@ -19,19 +19,21 @@ import I18n from '../core/I18n';
import { MessageDescriptor, MessageOptions } from '../types/interfaces';
import { CompiledMessage } from '../types/types';
import creatI18nCache from './cache/cache';
+import { formatElements } from '../utils/formatElements';
export function getFormatMessage(
i18n: I18n,
id: MessageDescriptor | string,
values: Record | undefined = {},
- options: MessageOptions = {}
+ options: MessageOptions = {},
+ components: any
) {
- let { message, context } = options;
+ let { messages, context } = options;
const { formatOptions } = options;
const cache = i18n.cache ?? creatI18nCache();
if (typeof id !== 'string') {
values = values || id.defaultValues;
- message = id.message || id.defaultMessage;
+ messages = id.messages || id.defaultMessage;
context = id.context;
id = id.id;
}
@@ -42,7 +44,7 @@ export function getFormatMessage(
const messageUnavailable = isMissingContextMessage || isMissingMessage;
// 对错误消息进行处理
- const messageError = i18n.error;
+ const messageError = i18n.onError;
if (messageError && messageUnavailable) {
if (typeof messageError === 'function') {
return messageError(i18n.locale, id, context);
@@ -53,14 +55,17 @@ export function getFormatMessage(
let compliedMessage: CompiledMessage;
if (context) {
- compliedMessage = i18n.messages[context][id] || message || id;
+ compliedMessage = i18n.messages[context][id] || messages || id;
} else {
- compliedMessage = i18n.messages[id] || message || id;
+ compliedMessage = i18n.messages[id] || messages || id;
}
- // 对解析的messages进行parse解析,并输出解析后的Token
+ // 对解析的message进行parse解析,并输出解析后的Token
compliedMessage = typeof compliedMessage === 'string' ? utils.compile(compliedMessage) : compliedMessage;
const translation = new Translation(compliedMessage, i18n.locale, i18n.locales, i18n.localeConfig, cache);
- return translation.translate(values, formatOptions);
+ const formatResult = translation.translate(values, formatOptions);
+
+ // 如果存在inula元素,则返回包含格式化的Inula元素的数组
+ return formatElements(formatResult, components);
}
diff --git a/packages/inula-intl/src/parser/Lexer.ts b/packages/inula-intl/src/parser/Lexer.ts
index eee91a80..4f23b752 100644
--- a/packages/inula-intl/src/parser/Lexer.ts
+++ b/packages/inula-intl/src/parser/Lexer.ts
@@ -16,9 +16,12 @@
import ruleUtils from '../utils/parseRuleUtils';
import { LexerInterface } from '../types/interfaces';
+/**
+ * 词法解析器,主要根据设计的规则对message进行处理成Token
+ */
class Lexer implements LexerInterface {
readonly startState: string;
- readonly states: Record;
+ readonly unionReg: Record;
private buffer = '';
private stack: string[] = [];
private index = 0;
@@ -28,19 +31,23 @@ class Lexer implements LexerInterface {
private state = '';
private groups: string[] = [];
private error: Record | undefined;
- private regexp;
+ private regexp: any;
private fast: Record = {};
private queuedGroup: string | null = '';
private value = '';
constructor(unionReg: Record, startState: string) {
this.startState = startState;
- this.states = unionReg;
+ this.unionReg = unionReg;
this.buffer = '';
this.stack = [];
this.reset();
}
+ /**
+ * 根据新的消息重置解析器
+ * @param data 消息数据
+ */
public reset(data?: string) {
this.buffer = data || '';
this.index = 0;
@@ -57,7 +64,7 @@ class Lexer implements LexerInterface {
return;
}
this.state = state;
- const info = this.states[state];
+ const info = this.unionReg[state];
this.groups = info.groups;
this.error = info.error;
this.regexp = info.regexp;
@@ -73,7 +80,7 @@ class Lexer implements LexerInterface {
this.setState(state);
}
- private getGroup(match: Record) {
+ private getGroup(match: Record) {
const groupCount = this.groups.length;
for (let i = 0; i < groupCount; i++) {
if (match[i + 1] !== undefined) {
@@ -87,7 +94,9 @@ class Lexer implements LexerInterface {
return this.value;
}
- // 迭代获取下一个 token
+ /**
+ * 迭代获取下一个 token
+ */
public next() {
const index = this.index;
@@ -112,7 +121,6 @@ class Lexer implements LexerInterface {
const regexp = this.regexp;
regexp.lastIndex = index;
const match = getMatch(regexp, buffer);
-
const error = this.error;
if (match == null) {
return this.getToken(error, buffer.slice(index, buffer.length), index);
@@ -131,9 +139,9 @@ class Lexer implements LexerInterface {
}
/**
- * 獲取Token
- * @param group 解析模板后獲得的屬性值
- * @param text 文本屬性的信息
+ * 获取Token
+ * @param group 解析模板后获得的属性值
+ * @param text 文本属性的信息
* @param offset 偏移量
* @private
*/
@@ -187,7 +195,7 @@ class Lexer implements LexerInterface {
return token;
}
- // 增加迭代器
+ // 增加迭代器,允许逐个访问集合中的元素方法
[Symbol.iterator]() {
return {
next: (): IteratorResult => {
@@ -198,9 +206,15 @@ class Lexer implements LexerInterface {
}
}
+/**
+ * 根据正则表达式,获取匹配到message的值
+ * 索引为 0 的元素是完整的匹配结果。
+ * 索引为 1、2、3 等的元素是正则表达式中指定的捕获组的匹配结果。
+ */
const getMatch = ruleUtils.checkSticky()
? // 正则表达式具有 sticky 标志
- (regexp, buffer) => regexp.exec(buffer)
+ (regexp: any, buffer: string) => regexp.exec(buffer)
: // 正则表达式具有 global 标志,匹配的字符串长度为 0,则表示匹配失败
- (regexp, buffer) => (regexp.exec(buffer)[0].length === 0 ? null : regexp.exec(buffer));
+ (regexp: any, buffer: string) => (regexp.exec(buffer)[0].length === 0 ? null : regexp.exec(buffer));
+
export default Lexer;
diff --git a/packages/inula-intl/src/parser/mappingRule.ts b/packages/inula-intl/src/parser/mappingRule.ts
index 1dff32ec..d11824ce 100644
--- a/packages/inula-intl/src/parser/mappingRule.ts
+++ b/packages/inula-intl/src/parser/mappingRule.ts
@@ -14,40 +14,47 @@
*/
const body: Record = {
- doubleapos: { match: '\'\'', value: () => '\'' },
+ doubleapos: { match: "''", value: () => "'" },
quoted: {
lineBreaks: true,
- match: /'[{}#](?:[^]*?[^'])?'(?!')/u,
- value: src => src.slice(1, -1).replace(/''/g, '\''),
+ match: /'[{}#](?:[^]*?[^'])?'(?!')/u, // 用以匹配单引号、花括号{}以及井号# 如'Hello' 、{name}、{}#
+ value: (src: string) => src.slice(1, -1).replace(/''/g, "'"),
},
argument: {
lineBreaks: true,
+
+ // 用于匹配{name、{Hello{World,匹配{ }花括号中有任何Unicode字符,如空格、制表符等
match: /\{\s*[^\p{Pat_Syn}\p{Pat_WS}]+\s*/u,
push: 'arg',
- value: src => src.substring(1).trim(),
+ value: (src: string) => src.substring(1).trim(),
},
octothorpe: '#',
end: { match: '}', pop: 1 },
- content: { lineBreaks: true, match: /[^][^{}#']*/u },
+ content: {
+ lineBreaks: true,
+ match: /[^][^{}#]*/u, // 主要匹配不包含[]任何字符(除了换行符)、不包含{}、#的任何个字符
+ },
};
const arg: Record = {
select: {
lineBreaks: true,
- match: /,\s*(?:plural|select|selectordinal)\s*,\s*/u,
- next: 'select',
- value: src => src.split(',')[1].trim(),
+ match: /,\s*(?:plural|select|selectordinal)\s*,\s*/u, // 匹配内容包含 plural、select 或 selectordinal
+ next: 'select', // 继续解析下一个参数
+ value: (src: string) => src.split(',')[1].trim(), // 提取第二个参数,并处理收尾空格
},
'func-args': {
+ // 匹配是否包含其他非特殊字符的参数,匹配结果包含特殊字符,如param1, param2, param3
lineBreaks: true,
match: /,\s*[^\p{Pat_Syn}\p{Pat_WS}]+\s*,/u,
next: 'body',
- value: src => src.split(',')[1].trim(),
+ value: (src: string) => src.split(',')[1].trim(), // 参数字符串去除逗号并去除首尾空格
},
'func-simple': {
+ // 匹配是否包含其他简单参数,匹配结果不包含标点符号:param1 param2 param3
lineBreaks: true,
match: /,\s*[^\p{Pat_Syn}\p{Pat_WS}]+\s*/u,
- value: src => src.substring(1).trim(),
+ value: (src: string) => src.substring(1).trim(),
},
end: { match: '}', pop: 1 },
};
@@ -55,14 +62,17 @@ const arg: Record = {
const select: Record = {
offset: {
lineBreaks: true,
- match: /\s*offset\s*:\s*\d+\s*/u,
- value: src => src.split(':')[1].trim(),
+ match: /\s*offset\s*:\s*\d+\s*/u, // 匹配message中是否包含偏移量offest信息
+ value: (src: string) => src.split(':')[1].trim(),
},
case: {
+ // 检查匹配该行是否包含分支信息。
lineBreaks: true,
+
+ // 设置规则匹配以左大括号 { 结尾的字符串,以等号 = 后跟数字开头的字符串,或者以非特殊符号和非空白字符开头的字符串,如 '=1 {'
match: /\s*(?:=\d+|[^\p{Pat_Syn}\p{Pat_WS}]+)\s*\{/u,
- push: 'body',
- value: src => src.substring(0, src.indexOf('{')).trim(),
+ push: 'body', // 匹配成功,则会push到body栈中
+ value: (src: string) => src.substring(0, src.indexOf('{')).trim(),
},
end: { match: /\s*\}/u, pop: 1 },
};
diff --git a/packages/inula-intl/src/parser/parseMappingRule.ts b/packages/inula-intl/src/parser/parseMappingRule.ts
index 5cdaf6f0..4ea561c9 100644
--- a/packages/inula-intl/src/parser/parseMappingRule.ts
+++ b/packages/inula-intl/src/parser/parseMappingRule.ts
@@ -17,12 +17,13 @@ import Lexer from './Lexer';
import { mappingRule } from './mappingRule';
import ruleUtils from '../utils/parseRuleUtils';
import { RawToken } from '../types/types';
+import { STATE_GROUP_START_INDEX, GLOBAL_FLAG, STICKY_FLAG, UNICODE_FLAG, VERTICAL_LINE } from '../constants';
const defaultErrorRule = ruleUtils.getRuleOptions('error', { lineBreaks: true, shouldThrow: true });
// 解析规则并生成词法分析器所需的数据结构,以便进行词法分析操作
-function parseRules(rules: Record, hasStates: boolean): Record {
- let errorRule: Record | null = null;
+function parseRules(rules: Record, hasStates: boolean): Record {
+ let errorRule: Record | null = null;
const fast: Record = {};
let enableFast = true;
let unicodeFlag: boolean | null = null;
@@ -58,7 +59,7 @@ function parseRules(rules: Record, hasStates: boolean): Record, hasStates: boolean): Record, hasStates: boolean): Record, name: string, map: Record) {
+export function checkStateGroup(group: Record, name: string, mappingRules: Record) {
const state = group && (group.push || group.next);
- if (state && !map[state]) {
+ if (state && !mappingRules[state]) {
throw new Error('The state is missing.');
}
- if (group && group.pop && +group.pop !== 1) {
+ if (group && group.pop && +group.pop !== STATE_GROUP_START_INDEX) {
throw new Error('The value of pop must be 1.');
}
}
// 将国际化解析规则注入分词器中
-function parseMappingRule(mappingRule: Record, startState?: string): Lexer {
+function parseMappingRule(mappingRule: Record, startState?: string): Lexer {
const keys = Object.getOwnPropertyNames(mappingRule);
if (!startState) {
@@ -133,7 +134,7 @@ function parseMappingRule(mappingRule: Record, startState?: string)
continue;
}
- const splice = [j, 1];
+ const splice = [j, STATE_GROUP_START_INDEX];
if (rule.include !== key && !included[rule.include]) {
included[rule.include] = true;
const newRules = ruleMap[rule.include];
@@ -174,17 +175,30 @@ function parseMappingRule(mappingRule: Record, startState?: string)
});
});
+ // 将规则注入到词法解析器
return new Lexer(mappingAllRules, startState);
}
-function processFast(match, fast: Record, options) {
+/**
+ * 快速匹配模式
+ * @param match
+ * @param fast
+ * @param options
+ */
+function processFast(match: Record, fast: Record = {}, options: Record) {
while (match.length && typeof match[0] === 'string' && match[0].length === 1) {
+ // 获取到数组的第一个元素
const word = match.shift();
fast[word.charCodeAt(0)] = options;
}
}
-function handleErrorRule(options, errorRule: Record) {
+/**
+ * 用以处理错误逻辑
+ * @param options 操作属性
+ * @param errorRule 错误规则
+ */
+function handleErrorRule(options: Record, errorRule: Record) {
if (!options.fallback === !errorRule.fallback) {
throw new Error('errorRule can only set one!');
} else {
@@ -192,7 +206,13 @@ function handleErrorRule(options, errorRule: Record) {
}
}
-function checkUnicode(match, unicodeFlag, options) {
+/**
+ * 用以检查message中是否包含Unicode
+ * @param match 匹配到的message
+ * @param unicodeFlag Unicode标志
+ * @param options 操作属性
+ */
+function checkUnicode(match: Record, unicodeFlag: boolean | null, options: Record) {
for (let j = 0; j < match.length; j++) {
const obj = match[j];
if (!ruleUtils.checkRegExp(obj)) {
@@ -201,14 +221,16 @@ function checkUnicode(match, unicodeFlag, options) {
if (unicodeFlag === null) {
unicodeFlag = obj.unicode;
- } else if (unicodeFlag !== obj.unicode && options.fallback === false) {
- throw new Error('If the /u flag is used, all!');
+ } else {
+ if (unicodeFlag !== obj.unicode && options.fallback === false) {
+ throw new Error('If the /u flag is used, all!');
+ }
}
}
return unicodeFlag;
}
-function checkStateOptions(hasStates: boolean, options) {
+function checkStateOptions(hasStates: boolean, options: Record) {
if (!hasStates) {
throw new Error('State toggle options are not allowed in stateless tokenizers!');
}
@@ -217,6 +239,11 @@ function checkStateOptions(hasStates: boolean, options) {
}
}
+/**
+ * 检查是否存在fallback属性,用以来判断快速匹配规则
+ * @param rules
+ * @param enableFast
+ */
function isExistsFallback(rules: Record, enableFast: boolean) {
for (let i = 0; i < rules.length; i++) {
if (rules[i].fallback) {
@@ -226,7 +253,7 @@ function isExistsFallback(rules: Record, enableFast: boolean) {
return enableFast;
}
-function isOptionsErrorOrFallback(options, errorRule: Record | null) {
+function isOptionsErrorOrFallback(options: Record, errorRule: Record | null) {
if (options.error || options.fallback) {
// 只能设置一个 errorRule
if (errorRule) {
diff --git a/packages/inula-intl/src/parser/parser.ts b/packages/inula-intl/src/parser/parser.ts
index 221a68e3..c607ae86 100644
--- a/packages/inula-intl/src/parser/parser.ts
+++ b/packages/inula-intl/src/parser/parser.ts
@@ -14,23 +14,13 @@
*/
import { lexer } from './parseMappingRule';
-import { RawToken, Token } from '../types/types';
+import { RawToken } from '../types/types';
import { DEFAULT_PLURAL_KEYS } from '../constants';
import { Content, FunctionArg, PlainArg, Select, TokenContext } from '../types/interfaces';
-import Lexer from './Lexer';
-
-const getContext = (lt: Record): TokenContext => ({
- offset: lt.offset,
- line: lt.line,
- col: lt.col,
- text: lt.text,
- lineNum: lt.lineBreaks,
-});
-
-export const checkSelectType = (value: string): boolean => {
- return value === 'plural' || value === 'select' || value === 'selectordinal';
-};
+/**
+ * 语法解析器,根据Token,获得具备上下文的AST
+ */
class Parser {
cardinalKeys: string[] = DEFAULT_PLURAL_KEYS;
ordinalKeys: string[] = DEFAULT_PLURAL_KEYS;
@@ -39,7 +29,7 @@ class Parser {
lexer.reset(message);
}
- isSelectKeyValid(token: RawToken, type: Select['type'], value: string) {
+ isSelectKeyValid(type: Select['type'], value: string) {
if (value[0] === '=') {
if (type === 'select') {
throw new Error('The key value of the select type is invalid.');
@@ -75,7 +65,7 @@ class Parser {
break;
}
case 'case': {
- this.isSelectKeyValid(token, type, token.value);
+ this.isSelectKeyValid(type, token.value);
select.cases.push({
key: token.value.replace(/=/g, ''),
tokens: this.parse(isPlural),
@@ -94,6 +84,11 @@ class Parser {
throw new Error('The message end position is invalid.');
}
+ /**
+ * 解析获得的Token
+ * @param token
+ * @param isPlural
+ */
parseToken(token: RawToken, isPlural: boolean): PlainArg | FunctionArg | Select {
const context = getContext(token);
const nextToken = lexer.next();
@@ -153,7 +148,12 @@ class Parser {
}
}
- // 在根级别解析时,遇到结束符号即结束解析并返回结果;而在非根级别解析时,遇到结束符号会被视为不合法的结束位置,抛出错误
+ /**
+ * 解析方法入口
+ * 在根级别解析时,遇到结束符号即结束解析并返回结果;而在非根级别解析时,遇到结束符号会被视为不合法的结束位置,抛出错误
+ * @param isPlural 标记复数
+ * @param isRoot 标记根节点
+ */
parse(isPlural: boolean, isRoot?: boolean): Array {
const tokens: any[] = [];
let content: string | Content | null = null;
@@ -201,6 +201,23 @@ class Parser {
}
}
+/**
+ * 获得 Token 的上下文
+ * @param Token Token
+ */
+const getContext = (Token: RawToken): TokenContext => ({
+ offset: Token.offset,
+ line: Token.line,
+ col: Token.col,
+ text: Token.text,
+ lineNum: Token.lineBreaks,
+});
+
+// 用以检查select规则中的类型
+export const checkSelectType = (value: string): boolean => {
+ return value === 'plural' || value === 'select' || value === 'selectordinal';
+};
+
export default function parse(message: string): Array {
const parser = new Parser(message);
return parser.parse(false, true);
diff --git a/packages/inula-intl/src/types/interfaces.ts b/packages/inula-intl/src/types/interfaces.ts
index 34f9f8a7..8b90e4de 100644
--- a/packages/inula-intl/src/types/interfaces.ts
+++ b/packages/inula-intl/src/types/interfaces.ts
@@ -13,15 +13,25 @@
* See the Mulan PSL v2 for more details.
*/
-import { AllLocaleConfig, AllMessages, Locale, Locales, Error, DatePool, SelectPool, RawToken } from './types';
+import {
+ AllLocaleConfig,
+ AllMessages,
+ Locale,
+ Locales,
+ Error,
+ DatePool,
+ SelectPool,
+ RawToken,
+ InulaNode,
+} from './types';
import I18n from '../core/I18n';
import Lexer from '../parser/Lexer';
+import { InulaElement, Key } from 'openinula';
// FormattedMessage的参数定义
export interface FormattedMessageProps extends MessageDescriptor {
values?: Record;
tagName?: string;
-
children?(nodes: any[]): any;
}
@@ -34,7 +44,7 @@ export interface MessageDescriptor extends MessageOptions {
export interface MessageOptions {
comment?: string;
- message?: string;
+ messages?: string;
context?: string;
formatOptions?: FormatOptions;
}
@@ -48,15 +58,26 @@ export interface I18nCache {
octothorpe: Record;
}
+export interface RichText {
+ components?: { [key: string]: InulaNode };
+}
+
+export interface InulaPortal extends InulaElement {
+ key: Key | null;
+ children: InulaNode;
+}
+
// I18n类的传参
-export interface I18nProps {
+export type I18nProps = RichText & {
locale?: Locale;
locales?: Locales;
messages?: AllMessages;
+ defaultLocale?: string;
+ timeZone?: string;
localeConfig?: AllLocaleConfig;
cache?: I18nCache;
- error?: Error;
-}
+ onError?: Error;
+};
// 消息格式化选项类型
export interface FormatOptions {
@@ -74,16 +95,13 @@ export interface I18nContextProps {
i18n?: I18n;
}
-export interface configProps {
- locale?: Locale;
- messages?: AllMessages;
- defaultLocale?: string;
+export type configProps = I18nProps & {
RenderOnLocaleChange?: boolean;
children?: any;
onWarn?: Error;
-}
+};
-export interface IntlMessageFormat extends configProps, MessageOptions {
+export interface IntlMessageFormat {
plural: (
value: number,
{
@@ -204,7 +222,6 @@ export interface InjectedIntl {
formatMessage(
messageDescriptor: MessageDescriptor,
values?: Record,
- options?: MessageOptions,
- useMemorize?: boolean
- ): string;
+ options?: MessageOptions
+ ): string | any[];
}
diff --git a/packages/inula-intl/src/types/types.ts b/packages/inula-intl/src/types/types.ts
index 917d7f07..b8252879 100644
--- a/packages/inula-intl/src/types/types.ts
+++ b/packages/inula-intl/src/types/types.ts
@@ -23,16 +23,17 @@ import {
I18nContextProps,
configProps,
InjectedIntl,
+ InulaPortal,
} from './interfaces';
-import I18n from '../core/I18n';
+import { InulaElement } from 'openinula';
-export type Error = string | ((message, id, context) => string);
+export type Error = string | ((message: any, id: any, context: any) => string);
export type Locale = string;
export type Locales = Locale | Locale[];
-export type LocaleConfig = { plurals?: (...arg: any) => any };
+export type LocaleConfig = { plurals?: (...args: any[]) => any };
export type AllLocaleConfig = Record;
@@ -59,7 +60,7 @@ export type Token = Content | PlainArg | FunctionArg | Select | Octothorpe;
export type DatePool = Date | string;
-export type SelectPool = string | Record;
+export type SelectPool = string | number;
export type RawToken = {
type: string;
@@ -74,13 +75,23 @@ export type RawToken = {
export type I18nProviderProps = I18nContextProps & configProps;
-export type IntlType = {
- i18n: I18n;
+export type IntlType = I18nContextProps & {
+ defaultLocale?: string | undefined;
+ onError?: Error | undefined;
+ messages?:
+ | string
+ | Record
+ | Record
+ | Record | Record>;
+ locale?: string;
formatMessage: (...args: any[]) => any;
formatNumber: (...args: any[]) => any;
formatDate: (...args: any[]) => any;
+ $t?: (...args: any[]) => any;
};
-export interface InjectedIntlProps {
+export type InjectedIntlProps = {
intl: InjectedIntl;
-}
+};
+
+export type InulaNode = InulaElement | string | number | Iterable | InulaPortal | boolean | null | undefined;
diff --git a/packages/inula-intl/src/utils/formatElements.ts b/packages/inula-intl/src/utils/formatElements.ts
new file mode 100644
index 00000000..fa3c77d3
--- /dev/null
+++ b/packages/inula-intl/src/utils/formatElements.ts
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
+ *
+ * openInula is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *
+ * http://license.coscl.org.cn/MulanPSL2
+ *
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+import { cloneElement, createElement, Fragment, InulaElement } from 'openinula';
+import { voidElementTags } from '../constants';
+
+// 用于匹配标签的正则表达式
+const tagReg = /<(\d+)>(.*?)<\/\1>|<(\d+)\/>/;
+
+// 用于匹配换行符的正则表达式
+const nlReg = /(?:\r\n|\r|\n)/g;
+
+export function formatElements(
+ value: string,
+ elements: { [key: string]: InulaElement } = {}
+): string | Array {
+ const elementKeyID = getElementIndex(0, '$Inula');
+
+ // value:This is a rich text with a custom component: <1/>
+ const arrays = value.replace(nlReg, '').split(tagReg);
+
+ // 若无InulaNode元素,则返回
+ if (arrays.length === 1) return value;
+
+ const result: any = [];
+
+ const before = arrays.shift();
+ if (before) {
+ result.push(before);
+ }
+
+ for (const [index, children, after] of getElements(arrays)) {
+ let element = elements[index];
+
+ if (!element || (voidElementTags[element.type as string] && children)) {
+ const errorMessage = !element
+ ? `Index not declared as ${index} in original translation`
+ : `${element.type} , No child element exists. Please check.`;
+ console.error(errorMessage);
+
+ // 对于异常元素,通过创建<>>来代替,并继续解析现有的子元素和之后的元素,并保证在构建数组时,不会因为缺少元素而导致索引错位。
+ element = createElement(Fragment, {});
+ }
+
+ // 如果存在子元素,则进行递归处理
+ const formattedChildren = children ? formatElements(children, elements) : element.props.children;
+
+ // 更新element 的属性和子元素
+ const clonedElement = cloneElement(element, { key: elementKeyID() }, formattedChildren);
+ result.push(clonedElement);
+
+ if (after) {
+ result.push(after);
+ }
+ }
+ return result;
+}
+
+/**
+ * 从arrays数组中解析出标签元素和其子元素
+ * @param arrays
+ */
+function getElements(arrays: string[]) {
+ // 如果 arrays 数组为空,则返回空数组
+ if (!arrays.length) return [];
+
+ /**
+ * pairedIndex: 第一个元素表示配对标签的内容,即 <1>...1> 形式的标签。
+ * children: 第二个元素表示配对标签内的子元素内容。
+ * unpairedIndex: 第三个元素表示自闭合标签的内容,即 <1/> 形式的标签。
+ * textAfter: 第四个元素表示标签之后的文本内容,即标签后紧跟着的文本。
+ * eg: [undefined,undefined,1,""]
+ */
+ const [pairedIndex, children, unpairedIndex, textAfter] = arrays.splice(0, 4);
+
+ // 解析当前标签元素和它的子元素,返回一个包含标签索引、子元素和后续文本的数组
+ const currentElement: [number, string, string] = [
+ parseInt(pairedIndex || unpairedIndex), // 解析标签索引,如果是自闭合标签,则使用 unpaired
+ children || '',
+ textAfter || '',
+ ];
+
+ // 递归调用 getElements 函数,处理剩余的 arrays 数组
+ const remainingElements = getElements(arrays);
+
+ // 将当前元素和递归处理后的元素数组合并并返回
+ return [currentElement, ...remainingElements];
+}
+
+// 对传入富文本元素的位置标志索引
+function getElementIndex(count = 0, prefix = '') {
+ return function () {
+ return `${prefix}_${count++}`;
+ };
+}
diff --git a/packages/inula-intl/src/utils/parseRuleUtils.ts b/packages/inula-intl/src/utils/parseRuleUtils.ts
index f950510b..bec392d4 100644
--- a/packages/inula-intl/src/utils/parseRuleUtils.ts
+++ b/packages/inula-intl/src/utils/parseRuleUtils.ts
@@ -18,6 +18,7 @@ function getType(input: any): string {
return str.slice(8, -1).toLowerCase();
}
+// 类型检查器
const createTypeChecker = (type: string) => {
return (input: any) => {
return getType(input) === type.toLowerCase();
@@ -28,24 +29,25 @@ const checkObject = (input: any) => input !== null && typeof input === 'object';
const checkRegExp = createTypeChecker('RegExp');
+// 使用正则表达式,如果对象存在则访问该属性,用来判断当前环境是否支持正则表达式sticky属性。
const checkSticky = () => typeof new RegExp('')?.sticky === 'boolean';
// 转义正则表达式中的特殊字符
-function transferReg(s: string): string {
+function transferReg(str: string): string {
// eslint-disable-next-line
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
-// 计算正则表达式中捕获组的数量
-function getRegGroups(s: string): number {
- const re = new RegExp('|' + s);
+// 计算正则表达式中捕获组的数量,用以匹配()
+function getRegGroups(str: string): number {
+ const regExp = new RegExp('|' + str);
// eslint-disable-next-line
- return re.exec('')?.length! - 1;
+ return regExp.exec('')?.length! - 1;
}
// 创建一个捕获组的正则表达式模式
-function getRegCapture(s: string): string {
- return '(' + s + ')';
+function getRegCapture(str: string): string {
+ return '(' + str + ')';
}
// 将正则表达式合并为一个联合的正则表达式模式
@@ -53,7 +55,7 @@ function getRegUnion(regexps: string[]): string {
if (!regexps.length) {
return '(?!)';
}
- const source = regexps.map(s => '(?:' + s + ')').join('|');
+ const source = regexps.map(str => '(?:' + str + ')').join('|');
return '(?:' + source + ')';
}
@@ -143,7 +145,7 @@ function getRulesByArray(array: any[]) {
return result;
}
-function getRuleOptions(type, obj) {
+function getRuleOptions(type: any, obj: any) {
// 如果 obj 不是一个对象,则将其转换为包含 'match' 属性的对象
if (!checkObject(obj)) {
obj = { match: obj };
@@ -182,23 +184,23 @@ function getRuleOptions(type, obj) {
} else {
options.match = [];
}
- options.match.sort((a, b) => {
+ options.match.sort((str1: string, str2: string) => {
// 根据规则的类型进行排序,确保正则表达式排在最前面,长度较长的规则排在前面
- if (checkRegExp(a) && checkRegExp(b)) {
+ if (checkRegExp(str1) && checkRegExp(str2)) {
return 0;
- } else if (checkRegExp(b)) {
+ } else if (checkRegExp(str2)) {
return -1;
- } else if (checkRegExp(a)) {
+ } else if (checkRegExp(str1)) {
return +1;
} else {
- return b.length - a.length;
+ return str2.length - str1.length;
}
});
return options;
}
-function getRules(spec) {
+function getRules(spec: any) {
return Array.isArray(spec) ? getRulesByArray(spec) : getRulesByObject(spec);
}
diff --git a/packages/inula-intl/src/utils/utils.ts b/packages/inula-intl/src/utils/utils.ts
index a3c22cb8..331dadf2 100644
--- a/packages/inula-intl/src/utils/utils.ts
+++ b/packages/inula-intl/src/utils/utils.ts
@@ -32,7 +32,7 @@ function compile(message: string): CompiledMessage {
try {
return getTokenAST(parse(message));
} catch (e) {
- console.error(`Message cannot be parse due to syntax errors: ${message}`);
+ console.error(`Message cannot be parse due to syntax errors: ${message},cause by ${e}`);
return message;
}
}
diff --git a/packages/inula-intl/tests/core/I18n.test.ts b/packages/inula-intl/tests/core/I18n.test.ts
deleted file mode 100644
index 77079664..00000000
--- a/packages/inula-intl/tests/core/I18n.test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-import I18n from '../../src/core/I18n';
-
-describe('I18n', () => {
- it('load catalog and merge with existing', () => {
- const i18n = new I18n({});
- const messages = {
- Hello: 'Hello',
- };
-
- i18n.loadMessage('en', messages);
- i18n.changeLanguage('en');
- expect(i18n.messages).toEqual(messages);
- i18n.loadMessage('fr', { Hello: 'Salut' });
- expect(i18n.messages).toEqual(messages);
- });
-
- it('should load multiple language ', function () {
- const enMessages = {
- Hello: 'Hello',
- };
- const frMessage = {
- Hello: 'Salut',
- };
- const intl = new I18n({});
- intl.loadMessage({
- en: enMessages,
- fr: frMessage,
- });
- intl.changeLanguage('en');
- expect(intl.messages).toEqual(enMessages);
-
- intl.changeLanguage('fr');
- expect(intl.messages).toEqual(frMessage);
- });
-
- it('should switch active locale', () => {
- const messages = {
- Hello: 'Salut',
- };
-
- const i18n = new I18n({
- locale: 'en',
- messages: {
- fr: messages,
- en: {},
- },
- });
-
- expect(i18n.locale).toEqual('en');
- expect(i18n.messages).toEqual({});
-
- i18n.changeLanguage('fr');
- expect(i18n.locale).toEqual('fr');
- expect(i18n.messages).toEqual(messages);
- });
-
- it('should switch active locale', () => {
- const messages = {
- Hello: 'Salut',
- };
-
- const i18n = new I18n({
- locale: 'en',
- messages: {
- en: messages,
- fr: {},
- },
- });
-
- i18n.changeLanguage('en');
- expect(i18n.locale).toEqual('en');
- expect(i18n.messages).toEqual(messages);
- i18n.changeLanguage('fr');
- expect(i18n.locale).toEqual('fr');
- expect(i18n.messages).toEqual({});
- });
- it('._ allow escaping syntax characters', () => {
- const messages = {
- 'My \'\'name\'\' is \'{name}\'': 'Mi \'\'nombre\'\' es \'{name}\'',
- };
- const i18n = new I18n({
- locale: 'es',
- messages: { es: messages },
- });
- expect(i18n.formatMessage('My \'\'name\'\' is \'{name}\'')).toEqual('Mi \'nombre\' es {name}');
- });
-
- it('._ should format message from catalog', function () {
- const messages = {
- Hello: 'Salut',
- id: 'Je m\'appelle {name}',
- };
- const i18n = new I18n({
- locale: 'fr',
- messages: { fr: messages },
- });
- expect(i18n.locale).toEqual('fr');
- expect(i18n.formatMessage('Hello')).toEqual('Salut');
- expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual('Je m\'appelle Fred');
- });
-
- it('should return the formatted date and time', () => {
- const i18n = new I18n({
- locale: 'fr',
- });
- const formattedDateTime = i18n.formatDate('2023-06-06T07:53:54.465Z', {
- dateStyle: 'full',
- timeStyle: 'short',
- });
- expect(typeof formattedDateTime).toBe('string');
- expect(formattedDateTime).toEqual('mardi 6 juin 2023 à 15:53');
- });
-
- it('should return the formatted number', () => {
- const i18n = new I18n({
- locale: 'en',
- });
- const formattedNumber = i18n.formatNumber(123456.789, { style: 'currency', currency: 'USD' });
- expect(typeof formattedNumber).toBe('string');
- expect(formattedNumber).toEqual('$123,456.79');
- });
-});
diff --git a/packages/inula-intl/tests/core/I18n.test.tsx b/packages/inula-intl/tests/core/I18n.test.tsx
new file mode 100644
index 00000000..cf29059c
--- /dev/null
+++ b/packages/inula-intl/tests/core/I18n.test.tsx
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+import I18n from '../../src/core/I18n';
+import { render } from '@testing-library/react';
+import '@testing-library/jest-dom/';
+
+// 测试组件
+const IndividualCustomComponent = () => {
+ return Custom Component ;
+};
+
+const CustomComponent = (props: any) => {
+ return {props.children}
;
+};
+
+const CustomComponentChildren = (props: any) => {
+ return {props.children}
;
+};
+
+describe('I18n', () => {
+ it('load catalog and merge with existing', () => {
+ const i18n = new I18n({});
+ const messages = {
+ Hello: 'Hello',
+ };
+
+ i18n.loadMessage('en', messages);
+ i18n.changeLanguage('en');
+ expect(i18n.messages).toEqual(messages);
+ i18n.loadMessage('fr', { Hello: 'Salut' });
+ expect(i18n.messages).toEqual(messages);
+ i18n.changeMessage({ Hello: 'Salut' });
+ expect(i18n.messages).toEqual({ Hello: 'Salut' });
+ });
+
+ it('should load multiple language ', function () {
+ const enMessages = {
+ Hello: 'Hello',
+ };
+ const frMessage = {
+ Hello: 'Salut',
+ };
+ const intl = new I18n({});
+ intl.loadMessage({
+ en: enMessages,
+ fr: frMessage,
+ });
+ intl.changeLanguage('en');
+ expect(intl.messages).toEqual(enMessages);
+
+ intl.changeLanguage('fr');
+ expect(intl.messages).toEqual(frMessage);
+ });
+
+ it('should switch active locale', () => {
+ const messages = {
+ Hello: 'Salut',
+ };
+
+ const i18n = new I18n({
+ locale: 'en',
+ messages: {
+ fr: messages,
+ en: {},
+ },
+ });
+
+ expect(i18n.locale).toEqual('en');
+ expect(i18n.messages).toEqual({});
+
+ i18n.changeLanguage('fr');
+ expect(i18n.locale).toEqual('fr');
+ expect(i18n.messages).toEqual(messages);
+ });
+
+ it('should switch active locale', () => {
+ const messages = {
+ Hello: 'Salut',
+ };
+
+ const i18n = new I18n({
+ locale: 'en',
+ messages: {
+ en: messages,
+ fr: {},
+ },
+ });
+
+ i18n.changeLanguage('en');
+ expect(i18n.locale).toEqual('en');
+ expect(i18n.messages).toEqual(messages);
+ i18n.changeLanguage('fr');
+ expect(i18n.locale).toEqual('fr');
+ expect(i18n.messages).toEqual({});
+ });
+ it('._ allow escaping syntax characters', () => {
+ const messages = {
+ "My ''name'' is '{name}'": "Mi ''nombre'' es '{name}'",
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ expect(i18n.formatMessage("My ''name'' is '{name}'")).toEqual("Mi ''nombre'' es '{name}'");
+ });
+
+ it('._ should format message from catalog', function () {
+ const messages = {
+ Hello: 'Salut',
+ id: "Je m'appelle {name}",
+ };
+ const i18n = new I18n({
+ locale: 'fr',
+ messages: { fr: messages },
+ });
+ expect(i18n.locale).toEqual('fr');
+ expect(i18n.formatMessage('Hello')).toEqual('Salut');
+ expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual("Je m'appelle Fred");
+ });
+
+ it('should return information with html element', () => {
+ const messages = {
+ id: 'hello, {name}',
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ const value = 'Jane ';
+ expect(i18n.formatMessage({ id: 'id' }, { name: value })).toEqual('hello, Jane ');
+ });
+
+ it('test demo from product', () => {
+ const messages = {
+ id: "服务商名称长度不能超过64个字符,允许输入中文、字母、数字、字符_-!@#$^.+'}{',且不能为关键字null(不区分大小写)。",
+ };
+ const i18n = new I18n({
+ locale: 'zh',
+ messages: { zh: messages },
+ });
+ expect(i18n.formatMessage('id')).toEqual(
+ "服务商名称长度不能超过64个字符,允许输入中文、字母、数字、字符_-!@#$^.+'}{',且不能为关键字null(不区分大小写)。"
+ );
+ });
+
+ it('Should return information with dom element', () => {
+ const messages = {
+ richText: 'This is a rich text with a custom component: {customComponent}',
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ const values = {
+ customComponent: ,
+ };
+ const formattedMessage = i18n.formatMessage({ id: 'richText' }, values);
+
+ // 渲染格式化后的文本内容
+ const { getByText } = render({formattedMessage}
);
+
+ // 检查文本内容中是否包含自定义组件的内容
+ expect(getByText('This is a rich text with a custom component')).toContain(
+ 'This is a rich text with a custom component'
+ );
+ });
+
+ it('Should return information for nested scenes with dom elements', () => {
+ const messages = {
+ richText: 'This is a rich text with a custom component: {customComponent}',
+ msg: 'test',
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ const values = {
+ customComponent: (
+
+ {i18n.formatMessage({ id: 'msg' })}
+
+ ),
+ };
+ const formattedMessage = i18n.formatMessage({ id: 'richText' }, values);
+
+ // 渲染格式化后的文本内容
+ const { getByText } = render({formattedMessage}
);
+
+ // 检查文本内容中是否包含自定义组件的内容
+ expect(getByText('test')).toBeTruthy();
+ });
+
+ it('Should return information for nested scenes with dom elements', () => {
+ const messages = {
+ richText: 'This is a rich text with a custom component: {customComponent}',
+ msg: 'test',
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ const values = {
+ customComponent: (
+
+ {i18n.formatMessage({ id: 'msg' })}
+
+ ),
+ };
+ const formattedMessage = i18n.formatMessage({ id: 'richText' }, values);
+
+ // 渲染格式化后的文本内容
+ const { getByText } = render({formattedMessage}
);
+
+ // 检查文本内容中是否包含自定义组件的内容
+ expect(getByText('test')).toBeTruthy();
+ });
+
+ it('should be returned as value when Multiple dom elements\n', () => {
+ const messages = {
+ richText: '{today}, my name is {name}, and {age} years old!',
+ };
+ const i18n = new I18n({
+ locale: 'es',
+ messages: { es: messages },
+ });
+ const Name = () => {
+ return tom ;
+ };
+ const Age = () => {
+ return 16 ;
+ };
+ const Today = () => {
+ return 3月2日 ;
+ };
+ const values = {
+ today: ,
+ name: ,
+ age: ,
+ };
+ const formattedMessage = i18n.formatMessage({ id: 'richText' }, values);
+
+ // 渲染格式化后的文本内容
+ const { getByText } = render({formattedMessage}
);
+
+ // 检查文本内容中是否包含自定义组件的内容
+ expect(getByText('my name is tom, and 16 years old!')).toBeTruthy();
+ });
+
+ it('should return the formatted date and time', () => {
+ const i18n = new I18n({
+ locale: 'fr',
+ });
+ const formattedDateTime = i18n.formatDate('2023-06-06T07:53:54.465Z', {
+ dateStyle: 'full',
+ timeStyle: 'short',
+ });
+ expect(typeof formattedDateTime).toBe('string');
+ expect(formattedDateTime).toEqual('mardi 6 juin 2023 à 15:53');
+ });
+
+ it('should return the formatted number', () => {
+ const i18n = new I18n({
+ locale: 'en',
+ });
+ const formattedNumber = i18n.formatNumber(123456.789, { style: 'currency', currency: 'USD' });
+ expect(typeof formattedNumber).toBe('string');
+ expect(formattedNumber).toEqual('$123,456.79');
+ });
+});
diff --git a/packages/inula-intl/tests/core/components/FormattedMessage.test.tsx b/packages/inula-intl/tests/core/components/FormattedMessage.test.tsx
index 5d1bd54d..9458fc3c 100644
--- a/packages/inula-intl/tests/core/components/FormattedMessage.test.tsx
+++ b/packages/inula-intl/tests/core/components/FormattedMessage.test.tsx
@@ -43,7 +43,7 @@ describe('', () => {
);
setTimeout(() => {
- expect(getByTestId('id')).toHaveTextContent(i18n.formatMessage('hello', '', {}));
+ expect(getByTestId('id').textContent).toEqual(i18n.formatMessage('hello', {}, {}));
}, 1000);
});
it('should format context', function () {
@@ -58,6 +58,6 @@ describe('', () => {
);
- expect(getByTestId('id')).toHaveTextContent(i18n.formatMessage('id', { name: 'fred' }, {}));
+ expect(getByTestId('id').textContent).toEqual(i18n.formatMessage('id', { name: 'fred' }, {}));
});
});
diff --git a/packages/inula-intl/tests/core/components/InjectI18n.test.tsx b/packages/inula-intl/tests/core/components/InjectI18n.test.tsx
index de03fb00..da380e0d 100644
--- a/packages/inula-intl/tests/core/components/InjectI18n.test.tsx
+++ b/packages/inula-intl/tests/core/components/InjectI18n.test.tsx
@@ -42,7 +42,6 @@ describe('InjectIntl', () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const Injected = injectIntl(Wrapped);
- // @ts-ignore
expect(() => render( )).toThrow("Cannot read properties of null (reading 'i18n')");
});
@@ -53,7 +52,7 @@ describe('InjectIntl', () => {
};
const { getByTestId } = mountWithProvider( );
- expect(getByTestId('test')).toHaveTextContent(
+ expect(JSON.stringify(getByTestId('test'))).toEqual(
'{"_events":{},"locale":"en","locales":["en"],"allMessages":{},"_localeData":{}}'
);
});
diff --git a/packages/inula-intl/tests/core/creatI18n.test.ts b/packages/inula-intl/tests/core/creatI18n.test.ts
index 2de6dbef..c7065ff3 100644
--- a/packages/inula-intl/tests/core/creatI18n.test.ts
+++ b/packages/inula-intl/tests/core/creatI18n.test.ts
@@ -29,6 +29,20 @@ describe('createI18n', () => {
).toBe('bar');
});
+ it('createIntl', function () {
+ const i18n = createI18n({
+ locale: 'en',
+ messages: {
+ test: 'test',
+ },
+ });
+ expect(
+ i18n.$t({
+ id: 'test',
+ })
+ ).toBe('test');
+ });
+
it('should not warn when defaultRichTextElements is not used', function () {
const onWarn = jest.fn();
createI18n({
diff --git a/packages/inula-intl/tests/core/hooks/useIntl.test.tsx b/packages/inula-intl/tests/core/hooks/useIntl.test.tsx
index cfe8dcd7..e69de29b 100644
--- a/packages/inula-intl/tests/core/hooks/useIntl.test.tsx
+++ b/packages/inula-intl/tests/core/hooks/useIntl.test.tsx
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
- *
- * openInula is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- *
- * http://license.coscl.org.cn/MulanPSL2
- *
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-import * as React from 'react';
-import { render } from '@testing-library/react';
-import { IntlProvider, useIntl } from '../../../index';
-
-const FunctionComponent = ({ spy }: { spy?: Function }) => {
- const { i18n } = useIntl();
- spy!(i18n.locale);
- return null;
-};
-
-const FC = () => {
- const i18n = useIntl();
- return i18n.formatNumber(10000, { style: 'currency', currency: 'USD' }) as any;
-};
-
-describe('useIntl() hooks', () => {
- it('throws when is missing from ancestry', () => {
- // So it doesn't spam the console
- jest.spyOn(console, 'error').mockImplementation(() => {});
- expect(() => render( )).toThrow('I18n object is not found!');
- });
-
- it('hooks onto the intl context', () => {
- const spy = jest.fn();
- render(
-
-
-
- );
- expect(spy).toHaveBeenCalledWith('en');
- });
-
- it('should work when switching locale on provider', () => {
- const { rerender, getByTestId } = render(
-
-
-
-
-
- );
- expect(getByTestId('comp')).toMatchSnapshot();
- rerender(
-
-
-
-
-
- );
- expect(getByTestId('comp')).toMatchSnapshot();
- rerender(
-
-
-
-
-
- );
-
- expect(getByTestId('comp')).toMatchSnapshot();
- });
-});
diff --git a/packages/inula-intl/tests/format/cache/cache.test.ts b/packages/inula-intl/tests/format/cache/cache.test.ts
index e9401710..6944b498 100644
--- a/packages/inula-intl/tests/format/cache/cache.test.ts
+++ b/packages/inula-intl/tests/format/cache/cache.test.ts
@@ -15,7 +15,7 @@
import creatI18nCache from '../../../src/format/cache/cache';
describe('creatI18nCache', () => {
- it('should create an empty IntlCache object', () => {
+ it('should create an empty I18nCache object', () => {
const intlCache = creatI18nCache();
expect(intlCache).toEqual({
diff --git a/packages/inula-intl/tests/format/formatters/DateTimeFormatter.test.ts b/packages/inula-intl/tests/format/formatters/DateTimeFormatter.test.ts
index 7396c273..46d2933b 100644
--- a/packages/inula-intl/tests/format/formatters/DateTimeFormatter.test.ts
+++ b/packages/inula-intl/tests/format/formatters/DateTimeFormatter.test.ts
@@ -61,7 +61,7 @@ describe('DateTimeFormatter', () => {
expect(spy).toHaveBeenCalledWith('en-GB', { month: 'short' });
});
- it('should not memoize formatter instances when memoize is false', () => {
+ it('should not memoize formatter instances when cache is effective', () => {
const spy = jest.spyOn(Intl, 'DateTimeFormat');
const formatter1 = new DateTimeFormatter('en-US', { month: 'short' });
const formatter2 = new DateTimeFormatter('en-US', { month: 'short' });
@@ -91,7 +91,7 @@ describe('DateTimeFormatter', () => {
expect(formatted).toEqual('January 1, 2023');
});
- it('should format using memorized formatter when useMemorize is true', () => {
+ it('should format using memorized formatter when cache is effective', () => {
const formatter = new DateTimeFormatter('en-US', { year: 'numeric' }, creatI18nCache());
const date = new Date(2023, 0, 1);
const formatted1 = formatter.dateTimeFormat(date);
diff --git a/packages/inula-intl/tests/format/getFormatMessage.test.ts b/packages/inula-intl/tests/format/getFormatMessage.test.ts
index f64c842f..a1ec17f8 100644
--- a/packages/inula-intl/tests/format/getFormatMessage.test.ts
+++ b/packages/inula-intl/tests/format/getFormatMessage.test.ts
@@ -24,7 +24,7 @@ describe('getFormatMessage', () => {
},
},
locale: 'en',
- error: 'missingMessage',
+ onError: 'missingMessage',
});
it('should return the correct translation for an existing message ID', () => {
@@ -32,7 +32,7 @@ describe('getFormatMessage', () => {
const values = { name: 'John' };
const expectedResult = 'Hello, John!';
- const result = getFormatMessage(i18nInstance, id, values);
+ const result = getFormatMessage(i18nInstance, id, values, {}, {});
expect(result).toEqual(expectedResult);
});
@@ -41,7 +41,7 @@ describe('getFormatMessage', () => {
const id = 'missingMessage';
const expectedResult = 'missingMessage';
- const result = getFormatMessage(i18nInstance, id);
+ const result = getFormatMessage(i18nInstance, id, {}, {}, {});
expect(result).toEqual(expectedResult);
});
diff --git a/packages/inula-intl/tests/utils/copyStatics.test.ts b/packages/inula-intl/tests/utils/copyStatics.test.ts
index ead6a6a8..d3d76228 100644
--- a/packages/inula-intl/tests/utils/copyStatics.test.ts
+++ b/packages/inula-intl/tests/utils/copyStatics.test.ts
@@ -15,7 +15,7 @@
import copyStaticProps from '../../src/utils/copyStaticProps';
describe('copyStaticProps', () => {
- test('should hoist static properties from sourceComponent to targetComponent', () => {
+ it('should hoist static properties from sourceComponent to targetComponent', () => {
class SourceComponent {
static staticProp = 'sourceProp';
}
@@ -23,11 +23,10 @@ describe('copyStaticProps', () => {
class TargetComponent {}
copyStaticProps(TargetComponent, SourceComponent);
-
expect((TargetComponent as any).staticProp).toBe('sourceProp');
});
- test('should hoist static properties from inherited components', () => {
+ it('should hoist static properties from inherited components', () => {
class SourceComponent {
static staticProp = 'sourceProp';
}
@@ -37,11 +36,10 @@ describe('copyStaticProps', () => {
class TargetComponent {}
copyStaticProps(TargetComponent, InheritedComponent);
-
expect((TargetComponent as any).staticProp).toBe('sourceProp');
});
- test('should not hoist properties if descriptor is not valid', () => {
+ it('should not hoist properties if descriptor is not valid', () => {
class SourceComponent {
get staticProp() {
return 'sourceProp';
@@ -51,11 +49,10 @@ describe('copyStaticProps', () => {
class TargetComponent {}
copyStaticProps(TargetComponent, SourceComponent);
-
expect((TargetComponent as any).staticProp).toBeUndefined();
});
- test('should not hoist properties if descriptor is not valid', () => {
+ it('should not hoist properties if descriptor is not valid', () => {
class SourceComponent {
static get staticProp() {
return 'sourceProp';
@@ -65,11 +62,10 @@ describe('copyStaticProps', () => {
class TargetComponent {}
copyStaticProps(TargetComponent, SourceComponent);
-
expect((TargetComponent as any).staticProp).toBe('sourceProp');
});
- test('copyStaticProps should not copy static properties that already exist in target or source component', () => {
+ it('copyStaticProps should not copy static properties that already exist in target or source component', () => {
const targetComponent = { staticProp: 'target' };
const sourceComponent = { staticProp: 'source' };
copyStaticProps(targetComponent, sourceComponent);
diff --git a/packages/inula-intl/tests/utils/eventListener.test.ts b/packages/inula-intl/tests/utils/eventListener.test.ts
index 17e0f43b..c72af6cf 100644
--- a/packages/inula-intl/tests/utils/eventListener.test.ts
+++ b/packages/inula-intl/tests/utils/eventListener.test.ts
@@ -43,7 +43,7 @@ describe('eventEmitter', () => {
expect(listener).not.toBeCalled();
});
- it('should do nothing when even doesn\'t exist', () => {
+ it("should do nothing when even doesn't exist", () => {
const unknown = jest.fn();
const emitter = new EventEmitter();
diff --git a/packages/inula-intl/tsconfig.json b/packages/inula-intl/tsconfig.json
index f3ff266e..7bc1bfc6 100644
--- a/packages/inula-intl/tsconfig.json
+++ b/packages/inula-intl/tsconfig.json
@@ -31,6 +31,7 @@
"declaration": true,
"experimentalDecorators": true,
"downlevelIteration": true,
+ "emitDeclarationOnly": true,
"declarationDir": "./build/@types",
// 赋值为空数组使@types/node不会起作用
"lib": [
@@ -54,7 +55,8 @@
}
},
"include": [
- "./index.ts"
+ "./index.ts",
+
],
"exclude": [
"node_modules",
diff --git a/packages/inula-intl/webpack.config.js b/packages/inula-intl/webpack.config.js
index 36c05a33..dc0ddb3b 100644
--- a/packages/inula-intl/webpack.config.js
+++ b/packages/inula-intl/webpack.config.js
@@ -12,16 +12,18 @@
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
-
-const { resolve } = require('path');
-const HtmlWebpackPlugin = require('html-webpack-plugin');
+import path from 'path';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
+import { fileURLToPath } from 'url';
const isDevelopment = process.env.NODE_ENV === 'development';
const entryPath = './example/index.tsx';
-module.exports = {
- entry: resolve(__dirname, entryPath),
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+export default {
+ entry: path.join(__dirname, entryPath),
output: {
- path: resolve(__dirname, './build'),
+ path: path.join(__dirname, './build'),
filename: 'main.js',
},
module: {
@@ -50,7 +52,7 @@ module.exports = {
mode: isDevelopment ? 'development' : 'production',
plugins: [
new HtmlWebpackPlugin({
- template: resolve(__dirname, './example/index.html'),
+ template: path.join(__dirname, './example/index.html'),
}),
],
resolve: {
diff --git a/packages/inula-request/babel.config.js b/packages/inula-request/babel.config.js
index 383563ed..f373490c 100644
--- a/packages/inula-request/babel.config.js
+++ b/packages/inula-request/babel.config.js
@@ -14,11 +14,5 @@
*/
module.exports = {
- presets: [
- [
- '@babel/preset-env',
- { targets: { node: 'current' }},
- ],
- ['@babel/preset-typescript'],
- ],
+ presets: [['@babel/preset-env', { targets: { node: 'current' } }], ['@babel/preset-typescript']],
};
diff --git a/packages/inula-request/examples/cancelRequest/cancelRequestTest.html b/packages/inula-request/examples/cancelRequest/cancelRequestTest.html
index f9a4ebcd..b5367215 100644
--- a/packages/inula-request/examples/cancelRequest/cancelRequestTest.html
+++ b/packages/inula-request/examples/cancelRequest/cancelRequestTest.html
@@ -16,26 +16,24 @@