first commit
This commit is contained in:
commit
9c0cb91f0d
|
@ -0,0 +1,10 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
**/node_modules
|
||||
build/
|
||||
*.d.ts
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
root: true,
|
||||
|
||||
plugins: ['jest', 'no-for-of-loops', 'no-function-declare-after-return', 'react', '@typescript-eslint'],
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 8,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
modules: true,
|
||||
experimentalObjectRestSpread: true,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
jest: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
semi: ['warn', 'always'],
|
||||
quotes: ['warn', 'single'],
|
||||
'accessor-pairs': 'off',
|
||||
'brace-style': ['error', '1tbs'],
|
||||
'func-style': ['warn', 'declaration', { allowArrowFunctions: true }],
|
||||
'max-lines-per-function': 'off',
|
||||
'object-curly-newline': 'off',
|
||||
// 尾随逗号
|
||||
'comma-dangle': ['error', 'only-multiline'],
|
||||
|
||||
'no-constant-condition': 'off',
|
||||
'no-for-of-loops/no-for-of-loops': 'error',
|
||||
'no-function-declare-after-return/no-function-declare-after-return': 'error',
|
||||
},
|
||||
globals: {
|
||||
isDev: true,
|
||||
isTest: true,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['scripts/__tests__/**/*.js'],
|
||||
globals: {
|
||||
container: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/node_modules
|
||||
.idea
|
||||
.vscode
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
/packages/**/node_modules
|
||||
/packages/inula-cli/lib
|
|
@ -0,0 +1,6 @@
|
|||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (c) &#36;{today.year} 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." />
|
||||
<option name="myName" value="huawei" />
|
||||
</copyright>
|
||||
</component>
|
|
@ -0,0 +1,7 @@
|
|||
<component name="CopyrightManager">
|
||||
<settings default="huawei">
|
||||
<LanguageOptions name="JavaScript">
|
||||
<option name="fileTypeOverride" value="3" />
|
||||
</LanguageOptions>
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
printWidth: 120, // 一行120字符数,如果超过会进行换行
|
||||
tabWidth: 2, // tab等2个空格
|
||||
useTabs: false, // 用空格缩进行
|
||||
semi: true, // 行尾使用分号
|
||||
singleQuote: true, // 字符串使用单引号
|
||||
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
|
||||
jsxSingleQuote: false, // 在JSX中使用双引号
|
||||
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
|
||||
bracketSpacing: true, // 对象的括号间增加空格
|
||||
bracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
|
||||
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
|
||||
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
|
||||
endOfLine: 'lf', // 仅限换行(\n)
|
||||
};
|
|
@ -0,0 +1,125 @@
|
|||
木兰宽松许可证, 第2版
|
||||
木兰宽松许可证, 第2版
|
||||
|
||||
2020年1月 http://license.coscl.org.cn/MulanPSL2
|
||||
|
||||
您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
|
||||
|
||||
0. 定义
|
||||
|
||||
“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
|
||||
|
||||
“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
|
||||
|
||||
“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
|
||||
|
||||
“法人实体” 是指提交贡献的机构及其“关联实体”。
|
||||
|
||||
“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
|
||||
|
||||
1. 授予版权许可
|
||||
|
||||
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
|
||||
|
||||
2. 授予专利许可
|
||||
|
||||
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
|
||||
|
||||
3. 无商标许可
|
||||
|
||||
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
|
||||
|
||||
4. 分发限制
|
||||
|
||||
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
|
||||
|
||||
5. 免责声明与责任限制
|
||||
|
||||
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
|
||||
|
||||
6. 语言
|
||||
|
||||
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
|
||||
|
||||
条款结束
|
||||
|
||||
如何将木兰宽松许可证,第2版,应用到您的软件
|
||||
|
||||
如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
|
||||
|
||||
1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
|
||||
|
||||
2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
|
||||
|
||||
3, 请将如下声明文本放入每个源文件的头部注释中。
|
||||
|
||||
Copyright (c) [Year] [name of copyright holder]
|
||||
[Software Name] 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.
|
||||
Mulan Permissive Software License,Version 2
|
||||
Mulan Permissive Software License,Version 2 (Mulan PSL v2)
|
||||
|
||||
January 2020 http://license.coscl.org.cn/MulanPSL2
|
||||
|
||||
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
|
||||
|
||||
0. Definition
|
||||
|
||||
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
|
||||
|
||||
Contribution means the copyrightable work licensed by a particular Contributor under this License.
|
||||
|
||||
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
|
||||
|
||||
Legal Entity means the entity making a Contribution and all its Affiliates.
|
||||
|
||||
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
|
||||
|
||||
1. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
|
||||
|
||||
2. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
|
||||
|
||||
3. No Trademark License
|
||||
|
||||
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
|
||||
|
||||
4. Distribution Restriction
|
||||
|
||||
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
|
||||
|
||||
5. Disclaimer of Warranty and Limitation of Liability
|
||||
|
||||
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
6. Language
|
||||
|
||||
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
|
||||
|
||||
END OF THE TERMS AND CONDITIONS
|
||||
|
||||
How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
|
||||
|
||||
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
|
||||
|
||||
Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
|
||||
Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
|
||||
Attach the statement to the appropriate annotated syntax at the beginning of each source file.
|
||||
Copyright (c) [Year] [name of copyright holder]
|
||||
[Software Name] 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.
|
|
@ -0,0 +1,498 @@
|
|||
THIRD PARTY OPEN SOURCE SOFTWARE NOTICE
|
||||
Please note we provide an open source software notice for the third party open source software along with this software and/or this software component contributed by Huawei (in the following just “this SOFTWARE”). The open source software licenses are granted by the respective right holders.
|
||||
|
||||
Warranty Disclaimer
|
||||
The open source software in this software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the applicable licenses for more details.
|
||||
|
||||
Copyright Notice and License Texts
|
||||
Software: glob 10.3.4
|
||||
Copyright notice: Isaac Z. Schlueter <i@izs.me> (https://blog.izs.me/)
|
||||
License: ISC License
|
||||
ISC License
|
||||
|
||||
Copyright <YEAR> <OWNER>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
Software: inquirer 8.2.6
|
||||
Copyright notice: Simon Boudrias <admin@simonboudrias.com>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: mkdirp 3.0.1
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: yargs-parser 21.1.1
|
||||
Copyright notice: Ben Coe <ben@npmjs.com>
|
||||
License: ISC License
|
||||
ISC LICENSE
|
||||
|
||||
COPYRIGHT <YEAR> <OWNER>
|
||||
|
||||
PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
Software: yeoman-environment 3.19.3
|
||||
Copyright notice: Yeoman
|
||||
License: BSD-2-Clause
|
||||
Copyright <YEAR> <COPYRIGHT HOLDER>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Software: yeoman-generator 5.9.0
|
||||
Copyright notice: Yeoman
|
||||
License: BSD-2-Clause
|
||||
COPYRIGHT <YEAR> <COPYRIGHT HOLDER>
|
||||
|
||||
REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
|
||||
|
||||
1. REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER.
|
||||
|
||||
2. REDISTRIBUTIONS IN BINARY FORM MUST REPRODUCE THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER IN THE DOCUMENTATION AND/OR OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Software: @babel/runtime 7.22.15
|
||||
Copyright notice: The Babel Team (https://babel.dev/team)
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: babel-eslint 10.1.0
|
||||
Copyright notice: Sebastian McKenzie <sebmck@gmail.com>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: chalk 4.1.2
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: chokidar 3.5.3
|
||||
Copyright notice: Paul Miller (https://paulmillr.com)
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: crequire 1.8.1
|
||||
Copyright notice: army8735 <army8735@qq.com>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: deepmerge 4.3.1
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: dotenv 16.3.1
|
||||
License: BSD-2-Clause
|
||||
COPYRIGHT <YEAR> <COPYRIGHT HOLDER>
|
||||
|
||||
REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
|
||||
|
||||
1. REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER.
|
||||
|
||||
2. REDISTRIBUTIONS IN BINARY FORM MUST REPRODUCE THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER IN THE DOCUMENTATION AND/OR OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Software: esbuild 0.18.20
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: express 4.18.2
|
||||
Copyright notice: TJ Holowaychuk <tj@vision-media.ca>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: http-proxy-middleware 2.0.6
|
||||
Copyright notice: Steven Chim
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: install 0.13.0
|
||||
Copyright notice: Ben Newman < bn@cs.stanford.edu>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: jest 29.7.0
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: lodash 4.17.21
|
||||
Copyright notice: John-David Dalton <john.david.dalton@gmail.com>
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: resolve 1.22.6
|
||||
Copyright notice: James Halliday<mail@substack.net>(http://substack.net)
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: ts-jest 29.1.1
|
||||
Copyright notice: Kulshekhar Kabra <kulshekhar@users.noreply.github.com> (https://github.com/kulshekhar)
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: ts-node 10.9.1
|
||||
Copyright notice: Blake Embrey <hello@blakeembrey.com>(http://blakeembrey.me)
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: vite 4.4.2
|
||||
Copyright notice: Evan You
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: webpack 5.88.2
|
||||
Copyright notice: Tobias Koppers @sokra
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Software: webpack-dev-server 4.15.1
|
||||
Copyright notice: Tobias Koppers @sokra
|
||||
License: MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,33 @@
|
|||
# 0.0.1 版本
|
||||
|
||||
## 新特性
|
||||
|
||||
- **新增 create-inula 包**: 提供创建 inula 项目的脚手架能力。
|
||||
- **新增 inula-cli 包**: 提供 inula 工程便捷能力,如编译构建、代理调试及插件管理等。
|
||||
- **新增 inula-intl 包**: inula 配套生态组件,提供国际化及文本格式化能力。
|
||||
- **新增 inula-router 包** inula 配套生态组件,提供组件路由转跳能力。
|
||||
- **新增 inula-request 包** inula 配套生态组件,提供发送网络请求能力。
|
||||
|
||||
## API变更
|
||||
|
||||
无
|
||||
|
||||
## Bug修复
|
||||
|
||||
无
|
||||
|
||||
## CVE漏洞修复
|
||||
|
||||
无
|
||||
|
||||
## 已知问题
|
||||
|
||||
无
|
||||
|
||||
## 支持与反馈
|
||||
|
||||
如您在使用过程中遇到任何问题或需要帮助,请联系我们的支持团队。您可以通过以下方式与我们取得联系:
|
||||
|
||||
- [openInula 官网](www.openinula.net)
|
||||
- 电子邮件:team@inulajs.org
|
||||
- 微信公众号(openInula)
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"name": "inulajs",
|
||||
"description": "InulaJS is a JavaScript framework library.",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts --fix",
|
||||
"prettier": "prettier -w libs/**/*.ts",
|
||||
"build:inula": "pnpm -F inulajs build",
|
||||
"test:inula": "pnpm -F inulajs test",
|
||||
"build:inula-cli": "pnpm -F inula-cli build",
|
||||
"build:inula-intl": "pnpm -F inula-intl rollup-build",
|
||||
"build:inula-request": "pnpm -F inula-request build",
|
||||
"build:inula-router": "pnpm -F inula-router build"
|
||||
},
|
||||
"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",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^17.0.18",
|
||||
"@typescript-eslint/eslint-plugin": "4.8.0",
|
||||
"@typescript-eslint/parser": "4.8.0",
|
||||
"babel-jest": "^27.5.1",
|
||||
"ejs": "^3.1.8",
|
||||
"eslint": "7.13.0",
|
||||
"eslint-config-prettier": "^6.9.0",
|
||||
"eslint-plugin-jest": "^22.15.0",
|
||||
"eslint-plugin-no-for-of-loops": "^1.0.0",
|
||||
"eslint-plugin-no-function-declare-after-return": "^1.0.0",
|
||||
"eslint-plugin-react": "7.14.3",
|
||||
"jest": "^25.5.4",
|
||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||
"prettier": "2.6.2",
|
||||
"rollup": "^2.75.5",
|
||||
"rollup-plugin-execute": "^1.1.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"typescript": "4.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.x",
|
||||
"npm": ">=7.x"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/node_modules
|
||||
.idea
|
||||
.vscode
|
||||
package-lock.json
|
||||
/build
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
printWidth: 120, // 一行120字符数,如果超过会进行换行
|
||||
tabWidth: 2, // tab等2个空格
|
||||
useTabs: false, // 用空格缩进行
|
||||
semi: true, // 行尾使用分号
|
||||
singleQuote: true, // 字符串使用单引号
|
||||
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
|
||||
jsxSingleQuote: false, // 在JSX中使用双引号
|
||||
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
|
||||
bracketSpacing: true, // 对象的括号间增加空格
|
||||
bracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
|
||||
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
|
||||
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
|
||||
endOfLine: 'lf', // 仅限换行(\n)
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
### 使用脚手架创建lnulaJS项目
|
||||
|
||||
##### 步骤一: 启动Inula脚手架
|
||||
|
||||
在您需要创建项目的目录下执行以下命令:
|
||||
|
||||
```bash
|
||||
npx create-inula <项目名>
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 步骤二: 选择需要项目模板
|
||||
|
||||
执行启动命令后,您将收到以下回显信息询问,可根据回显信息进行相应的输入:
|
||||
|
||||
```
|
||||
Need to install the following packages:create-inula@1.0.0
|
||||
Ok to proceed? (y) y
|
||||
? Please select the template (Use arrow keys)
|
||||
> Simple-app
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
在创建项目过程中脚手架提供了 Simple-app 模板供开发者选择(后续会加入更多模板)。
|
||||
|
||||
- Simple-app已默认安装Inula,开发者可以直接在项目中专注于核心代码的开发。
|
||||
|
||||
|
||||
##### 步骤三:选择打包方式
|
||||
|
||||
在创建项目过程中有两种打包方式供选择,您可以根据自己使用习惯选择
|
||||
|
||||
```
|
||||
? Please select the build type (Use arrow keys)
|
||||
> webpack
|
||||
vite
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果您不知如何选择可分别参考[Vite 文档](https://cn.vitejs.dev/)以及[webpack文档](https://webpack.js.org/)。
|
||||
|
||||
至此,可以使用Inula框架,通过以下命令`npm run start`命令运行项目,你会看到简单的inula的示例。当然,你也可以基于inula框架构建您的web项目。
|
|
@ -0,0 +1,18 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../index.js');
|
|
@ -0,0 +1,67 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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 yParser = require('yargs-parser');
|
||||
const chalk = require('chalk');
|
||||
const run = require('./lib/run');
|
||||
const lodash = require('lodash');
|
||||
const version = require('./package.json').version;
|
||||
|
||||
const commands = [{ name: '-v', description: 'show version' }];
|
||||
|
||||
// args 为文件名后所有输入
|
||||
const args = yParser(process.argv.slice(2));
|
||||
|
||||
if (args.v || args._[0] === 'version') {
|
||||
console.log(version);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (args.h || args._[0] === 'help') {
|
||||
console.log(`
|
||||
Usage: create-inula <command> [options]
|
||||
|
||||
${getDescriptions(commands).join('\n')}
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const name = args._[0] || '';
|
||||
|
||||
const { type } = args;
|
||||
delete args.type;
|
||||
|
||||
(async () => {
|
||||
await run({
|
||||
name,
|
||||
type,
|
||||
args,
|
||||
});
|
||||
process.exit(0);
|
||||
})();
|
||||
|
||||
function getDescription(command) {
|
||||
return ` ${chalk.green(lodash.padEnd(command.name, 10))}${command.description || ''}`;
|
||||
}
|
||||
|
||||
function getDescriptions(commands) {
|
||||
return Object.keys(commands)
|
||||
.filter(name => typeof commands[name] !== 'string')
|
||||
.map(name => {
|
||||
return getDescription(commands[name]);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 fs = require('fs');
|
||||
const path = require('path');
|
||||
const Generator = require('yeoman-generator');
|
||||
const { globSync } = require('glob');
|
||||
const { statSync } = require('fs');
|
||||
const { basename } = require('path');
|
||||
const _ = require('lodash');
|
||||
_.extend(Generator.prototype, require('yeoman-generator/lib/actions/install'));
|
||||
|
||||
function noop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
class BasicGenerator extends Generator {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
this.opts = opts;
|
||||
this.name = basename(opts.env.cwd);
|
||||
}
|
||||
|
||||
isTsFile(f) {
|
||||
return f.endsWith('.ts') || f.endsWith('.tsx') || !!/(tsconfig\.json)/g.test(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝文件
|
||||
* @param {string} src 源文件路径
|
||||
* @param {string} dest 目标文件路径
|
||||
* @param {function} filter 过滤函数
|
||||
*/
|
||||
copyFile(src, dest, filter = () => true) {
|
||||
if (src.indexOf('package.json') !== -1 && fs.existsSync(dest)) {
|
||||
this.mergePackage(filePath, destFilePath);
|
||||
return;
|
||||
}
|
||||
const readStream = fs.createReadStream(src);
|
||||
const writeStream = fs.createWriteStream(dest);
|
||||
readStream.pipe(writeStream);
|
||||
readStream.on('error', err => console.error(err));
|
||||
writeStream.on('error', err => console.error(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝文件夹
|
||||
* @param {string} src 源文件夹路径
|
||||
* @param {string} dest 目标文件夹路径
|
||||
* @param {function} filter 过滤函数
|
||||
*/
|
||||
copyFolder(src, dest, filter = () => true) {
|
||||
if (!fs.existsSync(dest)) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
const files = fs.readdirSync(src);
|
||||
for (const file of files) {
|
||||
const srcPath = path.join(src, file);
|
||||
const destPath = path.join(dest, file);
|
||||
const stats = fs.statSync(srcPath);
|
||||
if (stats.isFile() && filter(srcPath)) {
|
||||
this.copyFile(srcPath, destPath);
|
||||
} else if (stats.isDirectory()) {
|
||||
this.copyFolder(srcPath, destPath, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeFiles(src, dest, { context, filterFiles = noop }) {
|
||||
const files = globSync('**/*', {
|
||||
cwd: src,
|
||||
dot: true,
|
||||
})
|
||||
.filter(filterFiles)
|
||||
.forEach(file => {
|
||||
const filePath = path.join(src, file);
|
||||
if (statSync(filePath).isFile()) {
|
||||
this.fs.copyTpl(filePath, path.join(dest, file), context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
copyFiles({ src, dest, context, filterFiles = noop }) {
|
||||
const files = globSync('**/*', {
|
||||
cwd: src,
|
||||
dot: true,
|
||||
})
|
||||
.filter(filterFiles)
|
||||
.forEach(file => {
|
||||
const filePath = path.resolve(src, file);
|
||||
const destFilePath = path.resolve(dest, file);
|
||||
if (statSync(filePath).isFile()) {
|
||||
if (file === 'package.json' && fs.existsSync(destFilePath)) {
|
||||
this.mergePackage(filePath, destFilePath);
|
||||
} else {
|
||||
this.fs.copyTpl(filePath, path.resolve(dest, file.replace(/^_/, '.')), context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mergePackage(src, dest) {
|
||||
if (fs.existsSync(dest)) {
|
||||
const existing = JSON.parse(fs.readFileSync(dest, 'utf8'));
|
||||
const newPackage = JSON.parse(fs.readFileSync(src, 'utf8'));
|
||||
const pkg = Object.assign({}, existing, newPackage);
|
||||
fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
traverseDirCapture(dir, dirCallback, fileCallback) {
|
||||
for (const filename of fs.readdirSync(dir)) {
|
||||
const fullpath = path.resolve(dir, filename);
|
||||
if (fs.lstatSync(fullpath).isDirectory()) {
|
||||
dirCallback(fullpath);
|
||||
if (fs.existsSync(fullpath)) {
|
||||
this.traverseDirCapture(fullpath, dirCallback, fileCallback);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
fileCallback(fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
traverseDirBubble(dir, dirCallback, fileCallback) {
|
||||
for (const filename of fs.readdirSync(dir)) {
|
||||
const fullpath = path.resolve(dir, filename);
|
||||
if (fs.lstatSync(fullpath).isDirectory()) {
|
||||
this.traverseDirBubble(fullpath, dirCallback, fileCallback);
|
||||
dirCallback(fullpath);
|
||||
continue;
|
||||
}
|
||||
fileCallback(fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
emptyDir(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.traverseDirBubble(
|
||||
dir,
|
||||
dir => fs.rmdirSync(dir),
|
||||
file => fs.unlinkSync(file)
|
||||
);
|
||||
}
|
||||
|
||||
prompt(questions) {
|
||||
process.send && process.send({ type: 'prompt' });
|
||||
process.emit('message', { type: 'prompt' });
|
||||
return super.prompt(questions);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BasicGenerator;
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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;
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"description": "simple app template."
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>My Inula App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.jsx"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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.11"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
width: 45%;
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 './index.css';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div class="container">
|
||||
<div class="hero">
|
||||
<h1 class="hero-title animate__animated animate__bounceInDown">欢迎来到 Inula 项目!</h1>
|
||||
<p class="hero-subtitle animate__animated animate__bounceInUp">你已成功创建你的第一个 Inula 项目</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card animate__animated animate__zoomIn">
|
||||
<h2>开始吧</h2>
|
||||
<p>
|
||||
编辑 <code>src/index.jsx</code> 并保存以重新加载。
|
||||
</p>
|
||||
</div>
|
||||
<div class="card animate__animated animate__zoomIn">
|
||||
<h2>了解更多</h2>
|
||||
<p>
|
||||
要了解 Inula,查看{' '}
|
||||
<a href="https://openinula.com/" target="_blank">Inula 官网</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Inula.render(<App />, document.getElementById('root'));
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"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.11"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 (
|
||||
<div class="container">
|
||||
<div class="hero">
|
||||
<h1 class="hero-title animate__animated animate__bounceInDown">欢迎来到 Inula 项目!</h1>
|
||||
<p class="hero-subtitle animate__animated animate__bounceInUp">你已成功创建你的第一个 Inula 项目</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card animate__animated animate__zoomIn">
|
||||
<h2>开始吧</h2>
|
||||
<p>
|
||||
编辑 <code>src/App.js</code> 并保存以重新加载。
|
||||
</p>
|
||||
</div>
|
||||
<div class="card animate__animated animate__zoomIn">
|
||||
<h2>了解更多</h2>
|
||||
<p>
|
||||
要了解 Inula,查看{' '}
|
||||
<a href="https://openinula.org" target="_blank">Inula 官网</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Inula App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../dist/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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(<App />, document.getElementById('root'));
|
|
@ -0,0 +1,56 @@
|
|||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
width: 45%;
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.js',
|
||||
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,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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 fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const mkdirp = require('mkdirp');
|
||||
const inquirer = require('inquirer');
|
||||
const yeoman = require('yeoman-environment');
|
||||
|
||||
const generatorType = fs
|
||||
.readdirSync(`${__dirname}/generators`)
|
||||
.filter(file => !file.startsWith('.'))
|
||||
.map(file => {
|
||||
return {
|
||||
name: file,
|
||||
value: file,
|
||||
short: file,
|
||||
};
|
||||
});
|
||||
|
||||
const runGenerator = async (templatePath, { name = '', cwd = process.cwd(), args = {} }) => {
|
||||
return new Promise(resolve => {
|
||||
let currentPath;
|
||||
if (name) {
|
||||
mkdirp.sync(name);
|
||||
currentPath = path.join(cwd, name);
|
||||
}
|
||||
|
||||
const Generator = require(templatePath);
|
||||
const env = yeoman.createEnv([], {
|
||||
cwd: currentPath,
|
||||
});
|
||||
const generator = new Generator({
|
||||
name,
|
||||
env,
|
||||
resolved: require.resolve(templatePath),
|
||||
args,
|
||||
});
|
||||
return generator.run(() => {
|
||||
console.log('File Generate Done');
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const run = async config => {
|
||||
if (typeof process.send === 'function') {
|
||||
process.send({ type: 'prompt' });
|
||||
}
|
||||
process.emit('message', { type: 'prompt' });
|
||||
|
||||
let { type } = config;
|
||||
if (!type) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
name: 'type',
|
||||
message: 'Please select the template',
|
||||
type: 'list',
|
||||
choices: generatorType,
|
||||
},
|
||||
]);
|
||||
type = answers.type;
|
||||
}
|
||||
const templatePath = `./generators/${type}`;
|
||||
try {
|
||||
return runGenerator(templatePath, config);
|
||||
} catch (e) {
|
||||
console.error(chalk.red('> Generate failed'), e);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = run;
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "create-inula",
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-inula": "bin/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"index.js",
|
||||
"package.json"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MulanPSL2",
|
||||
"dependencies": {
|
||||
"glob": "^10.3.3",
|
||||
"inquirer": "^8.0.0",
|
||||
"mkdirp": "^3.0.1",
|
||||
"yargs-parser": "^21.1.1",
|
||||
"yeoman-environment": "^3.15.0",
|
||||
"yeoman-generator": "^5.8.0",
|
||||
"chalk": "^4.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
'parser': 'babel-eslint',
|
||||
'env': {
|
||||
'amd': true,
|
||||
'es6': true,
|
||||
'browser': true,
|
||||
'node': false
|
||||
},
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 6,
|
||||
'sourceType': 'module',
|
||||
'ecmaFeatures': {
|
||||
'jsx': true
|
||||
}
|
||||
},
|
||||
'ignorePatterns': [
|
||||
"src/template"
|
||||
],
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
4,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
flatTernaryExpressions: true
|
||||
}
|
||||
],
|
||||
'no-unused-vars': 'off', // 允许变量声明后未使用
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
"no-underscore-dangle": ["off", "always"], // 允许私有变量 _xxx的变量命名方式
|
||||
'filenames/match-exported': 0,
|
||||
'consistent-return': 0,
|
||||
"comma-dangle": [2, "never"], // 组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号
|
||||
'global-require': 0, // 允许require语句不出现在顶层中
|
||||
'no-nested-ternary': 0, // 允许嵌套三元表达式
|
||||
'no-unused-expressions': 0, // 允许使用未执行的表达式。比如fn是一个函数,允许 fn && fn()
|
||||
'no-throw-literal': 0, // 允许throw抛出对象格式
|
||||
'@typescript-eslint/member-ordering': 0 // 禁用TypeScript声明规范
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
webpack/
|
||||
public/
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
printWidth: 120, // 一行120字符数,如果超过会进行换行
|
||||
tabWidth: 2, // tab等2个空格
|
||||
useTabs: false, // 用空格缩进行
|
||||
semi: true, // 行尾使用分号
|
||||
singleQuote: true, // 字符串使用单引号
|
||||
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
|
||||
jsxSingleQuote: false, // 在JSX中使用双引号
|
||||
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
|
||||
bracketSpacing: true, // 对象的括号间增加空格
|
||||
jsxBracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
|
||||
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
|
||||
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
|
||||
endOfLine: 'lf', // 仅限换行(\n)
|
||||
};
|
|
@ -0,0 +1,715 @@
|
|||
# inula-cli
|
||||
|
||||
## 一、安装使用
|
||||
|
||||
### 安装Nodejs
|
||||
|
||||
inula-cli的运行需要依赖Nodejs,使用前请确保您的电脑已安装Nodejs,并且版本在16以上。您可以通过在控制台执行以下命令来确认您的版本。
|
||||
|
||||
```
|
||||
>node -v
|
||||
|
||||
v16.4.0
|
||||
```
|
||||
|
||||
如果您没有安装Nodejs,或者Nodejs版本不满足条件,推荐使用nvm工具安装和管理Nodejs版本。
|
||||
|
||||
nvm最新版本下载: [https://github.com/coreybutler/nvm-windows/releases](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fcoreybutler%2Fnvm-windows%2Freleases)
|
||||
|
||||
安装nvm之后,可以通过如下命令安装Nodejs:
|
||||
|
||||
```
|
||||
>node install 16
|
||||
|
||||
>node use 16
|
||||
```
|
||||
|
||||
执行完成后,可以通过node -v确认安装是否成功。
|
||||
|
||||
### 安装inula-cli
|
||||
|
||||
为了方便使用inula-cli的功能,推荐您全局安装inula-cli。Nodejs安装会自带npm工具用于管理模块,您可以直接运行如下命令:
|
||||
|
||||
```
|
||||
>npm install -g inula-cli
|
||||
```
|
||||
|
||||
安装完成后,使用inula-cli version命令确认安装是否完成。
|
||||
|
||||
```
|
||||
>inula-cli version
|
||||
1.1.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 二、目录结构
|
||||
|
||||
inula-cli的推荐目录结构如下:
|
||||
|
||||
```
|
||||
├── dist // 构建产物目录
|
||||
├── node_modules // 项目依赖
|
||||
│ └── @inula
|
||||
│ └── inula-cli
|
||||
│ ├── lib
|
||||
├── mock // mock目录
|
||||
│ └── mock.ts
|
||||
├── src // 项目源码目录
|
||||
│ ├── pages
|
||||
│ │ ├── index.less
|
||||
│ │ └── index.tsx
|
||||
│ ├── utils
|
||||
│ │ └── index.ts
|
||||
│ ├── services
|
||||
│ │ └── api.ts
|
||||
│ ├── entry.(ts|tsx)
|
||||
├── .env // 项目环境变量文件
|
||||
├── plugin.ts // 插件文件
|
||||
├── .inula.ts|js // inula-cli配置文件
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 三、环境变量
|
||||
|
||||
inula-cli支持开发者使用环境变量来定义一些配置。
|
||||
|
||||
### 环境变量配置方法
|
||||
|
||||
inula-cli默认将项目根路径下的.env文件视为环境变量配置文件。.env文件应遵循properties文件格式。
|
||||
|
||||
如果项目在本地开发时需要做一些自定义配置,支持同时配置.local.env文件,相同名称的环境变量会以.local.env中的生效。
|
||||
|
||||
|
||||
|
||||
## 四、配置文件
|
||||
|
||||
inula-cli支持用户通过项目根目录下的.inula.ts或者.inula.js文件进行自定义配置。当两个文件同时存在时,会优先选取.inula.ts作为项目配置。如果您对配置文件还需要额外定制,可以在环境变量中添加RUNNING_MODE字段,RUNNING_MODE允许额外再定制一份配置文件,例如RUNNING_MODE=cloud,inula-cli会寻找名称为.inula.cloud.ts/js的配置文件。
|
||||
|
||||
### 配置文件定义
|
||||
|
||||
在配置文件中,您需要默认导出一个配置,以下为一个简单的配置文件示例:
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
enableMock: true,
|
||||
buildConfig:[
|
||||
{
|
||||
path: './webpack.web.js',
|
||||
name: 'web'
|
||||
},
|
||||
{
|
||||
path: './webpack.server.js',
|
||||
name: 'server'
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
对于TypeScript类型,我们也提供了类型定义以供开发时自动补全:
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
import { defineConfig } from "inula-cli"
|
||||
|
||||
export default defineConfig({
|
||||
enableMock: true,
|
||||
buildConfig:[
|
||||
{
|
||||
path: "./webpack.web.js",
|
||||
name: "web"
|
||||
},
|
||||
{
|
||||
path: "./webpack.server.js",
|
||||
name: "server"
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 配置文件参数
|
||||
|
||||
| 序号 | 参数 | 描述 | 类型 |
|
||||
| ---- | ------------- | ---------------------- | ------ |
|
||||
| 1、 | enableMock | 是否开启本地mock能力 | bool |
|
||||
| 2、 | mockPath | 本地Mock文件目录 | string |
|
||||
| 3、 | compileMode | 构建方式 | string |
|
||||
| 4、 | buildConfig | 生产构建配置 | array |
|
||||
| 5、 | devBuildConfg | 本地构建配置 | object |
|
||||
| 6、 | plugins | 需要加载的插件路径列表 | array |
|
||||
| 7、 | remoteProxy | 远端静态接口代理配置 | object |
|
||||
|
||||
|
||||
|
||||
## 五、插件
|
||||
|
||||
inula-cli的所有功能都围绕插件展开,插件可以很方便地让用户集成各式各样的功能,用户可以通过配置自由搭配组合各式各样的插件能力。
|
||||
|
||||
### 插件执行流程
|
||||
|
||||
所有插件的触发都从用户执行命令开始。当用户执行命令后,inula-cli会加载用户的配置文件.获取到插件列表后,会依次加载内部插件和用户自定义插件,通过inula-cli提供的api对象,向inula-cli注册命令以及hook。最后根据用户的命令选择最终执行的方法。
|
||||
|
||||
|
||||
### 使用插件
|
||||
|
||||
#### 使用内置插件
|
||||
|
||||
内置插件在inula-cli运行时会自动加载,用户可以直接调用这些内置命令,当前支持的内置插件功能如下:
|
||||
|
||||
| 序号 | 插件功能 | 触发命令 |
|
||||
| ---- | :----------------------- | ------------------- |
|
||||
| 1、 | 本地开发构建 | inula-cli dev |
|
||||
| 2、 | 生产构建 | inula-cli build |
|
||||
| 3、 | 接口mock能力 | inula-cli dev |
|
||||
| 4、 | 远端服务器页面热更新能力 | inula-cli dev |
|
||||
| 5、 | 远端服务器页面代理能力 | inula-cli proxy |
|
||||
| 6、 | 显示版本 | inula-cli version |
|
||||
| 7、 | 显示帮助 | inula-cli help |
|
||||
|
||||
#### 使用其他开发者发布的插件
|
||||
|
||||
inula-cli支持用户集成已发布在npm仓库的插件,用户可以按需安装并运行这些插件。
|
||||
|
||||
安装可以通过npm安装,这里以插件@inula/add为例:
|
||||
|
||||
```
|
||||
npm i --save-dev @inula/add
|
||||
```
|
||||
|
||||
如果需要运行插件,需要在配置文件中配置对应的插件路径
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
...
|
||||
plugins:["@inula/add"]
|
||||
}
|
||||
```
|
||||
|
||||
如果需要使用node_modules中的插件,可以只填写插件模块名,inula-cli会自动在node_modules里寻找模块并导入该模块。
|
||||
|
||||
### 开发自定义插件
|
||||
|
||||
#### inula-cli添加命令
|
||||
|
||||
我们可以通过api的registerCommand为inula-cli添加自定义命令,示例如下:
|
||||
|
||||
1、编写命令插件文件,这里我们自定义了一个conf命令用于展示当前项目的配置信息。
|
||||
|
||||
```
|
||||
// conf.ts
|
||||
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: "conf",
|
||||
description: "show user config",
|
||||
initalState: api.userConfig,
|
||||
fn: async function (args: any, state: any) {
|
||||
console.log("current user config is: ", state);
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
2、在配置文件中加入对插件的引用
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
plugins: ["./conf"]
|
||||
}
|
||||
```
|
||||
|
||||
3、在项目根目录下执行inula-cli conf即可触发插件运行。
|
||||
|
||||
```
|
||||
> inula-cli conf
|
||||
current user config is: {
|
||||
plugins: [ './conf', './showConf' ],
|
||||
}
|
||||
```
|
||||
|
||||
#### 为插件添加hook
|
||||
|
||||
inula-cli提供了hook机制可以让开发者在执行命令时实现事件监听和触发能力,
|
||||
|
||||
1、使用插件注册hook
|
||||
|
||||
```
|
||||
// modifyConfig.ts
|
||||
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerHook({
|
||||
name: "modifyConfig",
|
||||
fn: () => {
|
||||
api.userConfig.buildConfig = {
|
||||
path: "./webpack.config.js"
|
||||
};
|
||||
api.compileMode = "webpack"
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
2、在插件中触发hook
|
||||
|
||||
```
|
||||
// conf.ts
|
||||
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: "conf",
|
||||
description: "show user config",
|
||||
initalState: api.userConfig,
|
||||
fn: async function (args: any, state: any) {
|
||||
api.applyHook("modifyConfig");
|
||||
console.log("current user config is: ", state);
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
3、在配置文件中加入插件
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
plugins: ["./conf", "./modifyConfig"]
|
||||
}
|
||||
```
|
||||
|
||||
4、触发命令
|
||||
|
||||
```
|
||||
> inula-cli conf
|
||||
current user config is: {
|
||||
plugins: [ './conf', './showConf' ],
|
||||
buildConfig: {path: './webpack.config.js'},
|
||||
compileMode: 'webpack'
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 插件属性
|
||||
|
||||
| 序号 | 属性名称 | 描述 |
|
||||
| ---- | -------------- | ------------------------------------------------ |
|
||||
| 1、 | compileMode | 构建方式 |
|
||||
| 2、 | userConfig | 用户配置信息 |
|
||||
| 3、 | buildConfig | 用户生产构建配置 |
|
||||
| 4、 | devBuildConfig | 用户本地开发构建配置 |
|
||||
| 5、 | packageJson | 用户package.json信息 |
|
||||
| 6、 | logger | 日志模块,支持debug、info、warn、error四个级别, |
|
||||
|
||||
|
||||
|
||||
### 插件核心API
|
||||
|
||||
1、registerCommand
|
||||
|
||||
registerCommand方法允许用户自定义inula-cli的执行命令,
|
||||
|
||||
```
|
||||
api.registerCommand({
|
||||
name: string,
|
||||
description?: string,
|
||||
initialState?: any,
|
||||
fn: (args: yargsParser.Arguments, state: any): void;
|
||||
})
|
||||
```
|
||||
|
||||
- name:命令名称。
|
||||
- description:命令描述,用于help命令作展示。
|
||||
- initialState:命令初始属性。
|
||||
- fn:命令回调函数,参数args为调用inula-cli命令时传入的参数,已默认去除掉命令本身,结构同yargs-parser。state为执行时的命令属性。
|
||||
|
||||
使用示例:
|
||||
|
||||
```
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: "conf",
|
||||
description: "show user config",
|
||||
initialState: api.userConfig,
|
||||
fn: async function (args: any, state: any) {
|
||||
console.log("current user config is: ", state);
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
2、registerHook
|
||||
|
||||
registerHook用于向inula-cli注册hook事件
|
||||
|
||||
```
|
||||
api.registerHook({
|
||||
name: string,
|
||||
fn: (value?: any):void;
|
||||
})
|
||||
```
|
||||
|
||||
- name: 事件名称
|
||||
- fn:事件回调函数,参数为value,由触发事件回调时提供。
|
||||
|
||||
使用示例:
|
||||
|
||||
```
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerHook({
|
||||
name: "modifyConfig",
|
||||
fn: () => {
|
||||
api.userConfig.buildConfig = {
|
||||
path: "./webpack.config.js"
|
||||
};
|
||||
api.compileMode = "webpack"
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
3、applyHook
|
||||
|
||||
applyHook用于触发事件回调
|
||||
|
||||
```
|
||||
applyHook(name: string, value?: any})
|
||||
```
|
||||
|
||||
- name:待触发的事件名称
|
||||
- value:事件参数。
|
||||
|
||||
使用示例:
|
||||
|
||||
```
|
||||
import { API } from "inula-cli";
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: "conf",
|
||||
description: "show user config",
|
||||
initalState: api.userConfig,
|
||||
fn: async function (args: any, state: any) {
|
||||
api.applyHook("modifyConfig");
|
||||
console.log("current user config is: ", state);
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、构建能力
|
||||
|
||||
### 生产构建
|
||||
|
||||
inula-cli默认集成生产构建能力,用户可以通过在.inula.ts中配置buildConfig字段启用功能。配置示例如下:
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
// 使用webpack构建
|
||||
export default {
|
||||
compileMode: 'webpack',
|
||||
buildConfig:[
|
||||
{
|
||||
path: './webpack.web.js',
|
||||
name: 'web'
|
||||
},
|
||||
{
|
||||
path: './webpack.server.js',
|
||||
name: 'server'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 使用vite构建
|
||||
export default {
|
||||
compileMode: 'vite',
|
||||
buildConfig:[
|
||||
{
|
||||
path: './vite.config.js',
|
||||
name: 'config'
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
生产构建支持传入多个配置文件路径,使用webpack构建还支持配置文件以函数方式导出,inula-cli会将配置中的env和args作为参数传递到函数中执行以获取最后的构建配置。
|
||||
|
||||
```
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = function (env, argv) {
|
||||
return {
|
||||
entry: './src/' + argv['output'] + '.js',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
devtool: env.production ? 'source-map' : 'eval'
|
||||
}
|
||||
}
|
||||
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
compileMode: 'webpack',
|
||||
buildConfig:[
|
||||
{
|
||||
path: './webpack.config.js',
|
||||
name: 'config',
|
||||
args: {
|
||||
output: 'page',
|
||||
},
|
||||
env: {
|
||||
production: true
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 本地构建
|
||||
|
||||
inula-cli默认也支持项目本地构建,用户可以通过在.inula.ts中配置devBuildConfig字段启用功能。配置示例如下:
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
// 使用webpack构建
|
||||
export default: {
|
||||
compileMode: "webpack"
|
||||
devBuildConfig: {
|
||||
path: "./webpack.dev.js",
|
||||
name: "dev",
|
||||
env: {
|
||||
development: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用vite构建
|
||||
export default: {
|
||||
compileMode: "vite"
|
||||
devBuildConfig: {
|
||||
path: "./vite.dev.js",
|
||||
name: "dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
本地构建只允许用于传入一份配置文件路径,如果使用webpack进行构建,同样支持配置文件导出函数。
|
||||
|
||||
|
||||
|
||||
## 六、Mock
|
||||
|
||||
Mock功能广泛应用于前端开发中,Mock能力可以模拟出后端服务的各种响应情况,比如成功、失败、超时等情况,以及不同的数据返回,从而帮助我们更好地进行前端开发和测试。 Mock功能可以使得前端开发人员可以独立进行开发和测试,不会受到后端开发的进度影响,从而提高开发效率。
|
||||
|
||||
### 如何使用Mock
|
||||
|
||||
inula-cli已经将Mock功能作为内置插件能力,当您无论使用webpack还是vite构建项目时都可以自动启用。如果您不想使用Mock功能,可以自行在配置文件中关闭。
|
||||
|
||||
inula-cli已经将Mock功能作为内置插件能力,当您无论使用webpack还是vite构建项目时都可以自动启用。如果您不想使用Mock功能,可以自行在配置文件中关闭。该目录下所有文件视为mock文件。例如:
|
||||
|
||||
### Mock目录
|
||||
|
||||
inula-cli自动将项目根路径里/Mock目录下所有文件视为mock文件。例如:
|
||||
|
||||
```
|
||||
├── mock
|
||||
├── todos.ts
|
||||
├── items.ts
|
||||
└── users.ts
|
||||
└── src
|
||||
└── pages
|
||||
└── index.tsx
|
||||
```
|
||||
|
||||
如果您想修改Mock目录位置,可以在配置文件中修改mockPath。如果不配置该参数,默认使用"./mock"。
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
export default {
|
||||
...
|
||||
enableMock: true,
|
||||
mockPath:"./mock",
|
||||
}
|
||||
```
|
||||
|
||||
### Mock文件
|
||||
|
||||
由于实现方式不同,webpack和vite构建的项目在mock文件的格式也略有不同,这里将分开说明。
|
||||
|
||||
#### webpack
|
||||
|
||||
Mock文件需要默认导出一个对象,key为"请求方式 接口名",值为接口实现。示例如下:
|
||||
|
||||
```
|
||||
export default {
|
||||
"GET /api/user": (req, res) => {
|
||||
res.status(200).json("admin")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果想要一次mock多个接口,可以在导出对象中设置多个key,例如:
|
||||
|
||||
```
|
||||
export default {
|
||||
"GET /api/user": (req, res) => {
|
||||
res.status(200).json("admin");
|
||||
},
|
||||
"GET /api/user/id/admin ": (req, res) => {
|
||||
res.status(200).json(1);
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
关于接口实现中的req和res的API,可以参考express文档获取更多的信息。
|
||||
|
||||
#### Vite
|
||||
|
||||
Mock文件默认导出一个数组,数组每一个成员示例如下:
|
||||
|
||||
```
|
||||
export default [
|
||||
{
|
||||
url: '/api/get',
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
name: 'vben',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/post',
|
||||
method: 'post',
|
||||
timeout: 2000,
|
||||
response: {
|
||||
code: 0,
|
||||
data: {
|
||||
name: 'vben',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/text',
|
||||
method: 'post',
|
||||
rawResponse: async (req, res) => {
|
||||
let reqbody = ''
|
||||
await new Promise((resolve) => {
|
||||
req.on('data', (chunk) => {
|
||||
reqbody += chunk
|
||||
})
|
||||
req.on('end', () => resolve(undefined))
|
||||
})
|
||||
res.setHeader('Content-Type', 'text/plain')
|
||||
res.statusCode = 200
|
||||
res.end(`hello, ${reqbody}`)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
## 七、**远端页面热更新能力**
|
||||
|
||||
远端页面热更新能力是指在不修改远端服务器文件的前提下,当开发者在本地调试、修改项目代码时,可以实时在远端页面生效,并且能够与远端其他接口正常完成交互,其实现原理如下:
|
||||
|
||||
|
||||
|
||||
用户在启动dev调试后,devServer会根据matcher中的判断逻辑决定该接口从本地项目获取还是从远端服务器获取,同时本地项目会根据用户修改代码实时编译,这样可以使得devServer拥有热更新能力。
|
||||
|
||||
### 使用方法
|
||||
|
||||
在框架配置文件中,开发者需要配置远端服务器地址以及编写自定义的matcher函数提供给框架:
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
const matcher = (pathname, request) => {
|
||||
// 这里我们假设以"/user"开头的接口由本地项目提供,其余接口从远端获取
|
||||
if (!pathname.startsWith("/user")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
...
|
||||
devBuildConfig: {
|
||||
devProxy: {
|
||||
target: "https://xx.xx.xx.xx:xxxx",
|
||||
matcher: matcher,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
配置完成后,直接运行inula-cli dev,即可进行热调试。
|
||||
|
||||
## 八、**远端静态接口代理**能力
|
||||
|
||||
远端代理也是开发测试过程中常用能力之一,尤其是在开发大型项目时,需要修改其中的一个或多个接口时,可以使用远端代理将需要修改的客户端接口重定向到本地修改后的文件,快速进行问题分析和修改验证。
|
||||
|
||||
|
||||
|
||||
### 使用方法
|
||||
|
||||
用户可以在.inula.ts中配置remoteProxy字段开启远端静态接口代理能力,完成配置后,使用后执行inula-cli proxy启动该功能。
|
||||
|
||||
```
|
||||
// .inula.ts
|
||||
|
||||
export default {
|
||||
remoteProxy: {
|
||||
target: "https://xx.xx.xx.xx:xxxx",
|
||||
localPort: 3001,
|
||||
localStatic: [
|
||||
{
|
||||
url: "/api/page.js",
|
||||
local: "./dist/page.js"
|
||||
},
|
||||
{
|
||||
url: "api/page.css",
|
||||
local: "./dist/page.css"
|
||||
}
|
||||
]
|
||||
forwardingURL: ["/"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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 run from '../lib/cli/cli.js';
|
||||
|
||||
run();
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "inula-cli",
|
||||
"version": "0.0.4",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"bin": {
|
||||
"inula-cli": "bin/start.js"
|
||||
},
|
||||
"files":[
|
||||
"bin",
|
||||
"lib",
|
||||
"template",
|
||||
"package.json",
|
||||
"tsconfig.json",
|
||||
"README.md"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MulanPSL2",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/body-parser": "^1.19.2",
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/node": "^18.16.1",
|
||||
"@types/resolve": "^1.20.2",
|
||||
"@types/webpack": "^5.28.1",
|
||||
"@types/yargs-parser": "^21.0.0",
|
||||
"@types/http-proxy-middleware": "^1.0.0",
|
||||
"@types/jest": "^29.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chalk": "^4.0.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"crequire": "^1.8.1",
|
||||
"deepmerge": "^4.3.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"esbuild": "^0.18.17",
|
||||
"express": "^4.18.2",
|
||||
"glob": "^10.3.3",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"inquirer": "^9.2.7",
|
||||
"install": "^0.13.0",
|
||||
"jest": "^29.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"resolve": "^1.22.3",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"vite": "4.4.2",
|
||||
"webpack": "^5.0.0",
|
||||
"webpack-dev-server": "^4.13.3",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"types": "lib/types/types.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/types.d.ts",
|
||||
"import": "./lib/types/types.js",
|
||||
"require": "./lib/types/types.js"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 WebpackDevServer from 'webpack-dev-server';
|
||||
import { createServer } from 'vite';
|
||||
import { API } from '../../../types/types';
|
||||
import setupProxy from '../../../utils/setupProxy.js';
|
||||
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: 'dev',
|
||||
description: 'build application for development',
|
||||
initialState: api.devBuildConfig,
|
||||
fn: async function (args: any, state: any) {
|
||||
api.applyHook({ name: 'beforeDevConfig' });
|
||||
switch (api.compileMode) {
|
||||
case 'webpack':
|
||||
if (state) {
|
||||
api.applyHook({ name: 'beforeDevCompile', config: state });
|
||||
const compiler = webpack(state);
|
||||
|
||||
const devServerOptions: WebpackDevServer.Configuration = {
|
||||
client: {
|
||||
overlay: false,
|
||||
},
|
||||
host: 'localhost',
|
||||
port: '8888',
|
||||
open: true,
|
||||
historyApiFallback: true,
|
||||
};
|
||||
|
||||
if (api.userConfig.devBuildConfig.devProxy) {
|
||||
devServerOptions.onBeforeSetupMiddleware = (devServer: WebpackDevServer) => {
|
||||
setupProxy(devServer.app, api);
|
||||
}
|
||||
}
|
||||
|
||||
api.applyHook({
|
||||
name: 'beforeStartDevServer',
|
||||
config: { compiler: compiler, devServerOptions: devServerOptions },
|
||||
});
|
||||
const server = new WebpackDevServer(compiler, devServerOptions);
|
||||
server.startCallback((err: any) => {
|
||||
api.applyHook({ name: 'afterStartDevServer' });
|
||||
});
|
||||
} else {
|
||||
api.logger.error('Can\'t find config');
|
||||
}
|
||||
break;
|
||||
case 'vite':
|
||||
if (state) {
|
||||
await createServer(state)
|
||||
.then(server => {
|
||||
return server.listen();
|
||||
})
|
||||
.then(server => {
|
||||
server.printUrls();
|
||||
});
|
||||
} else {
|
||||
api.logger.error('Can\'t find config');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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 { API } from '../../../types/types';
|
||||
import yargsParser from 'yargs-parser';
|
||||
import inquirer from 'inquirer';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { copyFile } from '../../../utils/util.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: 'generate',
|
||||
description: 'generate template',
|
||||
fn: async (args: yargsParser.Arguments) => {
|
||||
if (args._[0] === 'g') {
|
||||
args._.shift();
|
||||
}
|
||||
if (args._.length === 0) {
|
||||
api.logger.warn("Can't find any generate options.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args._[0]) {
|
||||
case 'jest':
|
||||
args._.shift();
|
||||
const isESM = api.packageJson['type'] === 'module';
|
||||
await generateJest(args, api.cwd, isESM);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const generateJest = async (args: yargsParser.Arguments, cwd: string, isESM: boolean) => {
|
||||
let isTs: boolean = false;
|
||||
if (args['ts']) {
|
||||
isTs = true;
|
||||
} else {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
name: 'useTs',
|
||||
message: 'Do you want to use TypeScript',
|
||||
type: 'confirm',
|
||||
},
|
||||
]);
|
||||
isTs = answers['useTs'];
|
||||
}
|
||||
|
||||
if (checkJestConfigExist(cwd)) {
|
||||
console.log('The jest config is exist.');
|
||||
return;
|
||||
}
|
||||
|
||||
const testRootPath = path.join(cwd, 'test');
|
||||
|
||||
if (!fs.existsSync(testRootPath)) {
|
||||
fs.mkdirSync(testRootPath);
|
||||
}
|
||||
|
||||
let templateDir = path.resolve(__dirname, '../../../../template/test');
|
||||
|
||||
// 如果是TS, 拷贝ts
|
||||
if (isTs) {
|
||||
templateDir = path.join(templateDir, 'ts');
|
||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
||||
}
|
||||
|
||||
// 拷贝mjs
|
||||
if (!isTs && isESM) {
|
||||
templateDir = path.join(templateDir, 'mjs');
|
||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
||||
}
|
||||
|
||||
// 拷贝cjs
|
||||
if (!isTs && !isESM) {
|
||||
templateDir = path.join(templateDir, 'cjs');
|
||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
||||
}
|
||||
};
|
||||
|
||||
function checkJestConfigExist(cwd: string): boolean {
|
||||
const items = fs.readdirSync(cwd);
|
||||
for (const item of items) {
|
||||
const itemPath = path.resolve(cwd, item);
|
||||
const states = fs.statSync(itemPath);
|
||||
if (states.isFile() && item.startsWith('jest.config')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function copyTestTemplate(cwd: string, testRootPath: string, templateDir: string) {
|
||||
const items = fs.readdirSync(templateDir);
|
||||
for (const item of items) {
|
||||
const itemPath = path.resolve(templateDir, item);
|
||||
if (item.startsWith('jest.config')) {
|
||||
copyFile(path.join(cwd, item), itemPath);
|
||||
} else {
|
||||
copyFile(path.join(testRootPath, item), itemPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 chalk from 'chalk';
|
||||
import lodash from 'lodash';
|
||||
|
||||
function getDescriptions(commands: any) {
|
||||
return Object.keys(commands)
|
||||
.filter(name => typeof commands[name] !== 'string')
|
||||
.map(name => {
|
||||
return getDescription(commands[name]);
|
||||
});
|
||||
}
|
||||
|
||||
function getDescription(command: any) {
|
||||
return ` ${chalk.green(lodash.padEnd(command.name, 10))}${command.description || ''}`;
|
||||
}
|
||||
|
||||
function padLeft(str: string) {
|
||||
return str
|
||||
.split('\n')
|
||||
.map((line: string) => ` ${line}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export default (api: any) => {
|
||||
api.registerCommand({
|
||||
name: 'help',
|
||||
description: 'show command helps',
|
||||
|
||||
fn: (args: any, config: any) => {
|
||||
console.log(`
|
||||
Usage: inula-cli <command> [options]
|
||||
|
||||
${getDescriptions(api.commands).join('\n')}
|
||||
`);
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { createRequire } from 'module';
|
||||
import mockServer from '../../../utils/mockServer.js';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
export default (api: any) => {
|
||||
api.registerHook({
|
||||
name: 'beforeStartDevServer',
|
||||
fn: async (state: any) => {
|
||||
const { compiler, devServerOptions } = state;
|
||||
devServerOptions.setupMiddlewares = (middlewares: any, devServer: { app: any }) => {
|
||||
mockServer(devServer.app);
|
||||
return middlewares;
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 express from 'express';
|
||||
import { createRequire } from 'module';
|
||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
export default (api: any) => {
|
||||
api.registerCommand({
|
||||
name: 'proxy',
|
||||
description: 'remote proxy',
|
||||
initialState: api.userConfig.remoteProxy,
|
||||
fn: async function (args: any, state: any) {
|
||||
if (!state) {
|
||||
api.logger.error(`Invalid proxy config!`);
|
||||
return;
|
||||
}
|
||||
const app = express();
|
||||
const proxyConfig = state;
|
||||
const staticList = proxyConfig.localStatic;
|
||||
staticList.forEach(function (value: { url: any; local: string }) {
|
||||
app.use(value.url, express.static(value.local));
|
||||
});
|
||||
const remoteProxy = createProxyMiddleware(proxyConfig.fowardingURL, {
|
||||
target: proxyConfig.target,
|
||||
secure: false,
|
||||
autoRewrite: true,
|
||||
protocolRewrite: 'http',
|
||||
ws: true,
|
||||
hostRewrite: '',
|
||||
preserveHeaderKeyCase: true,
|
||||
proxyTimeout: 5 * 60 * 60 * 1000,
|
||||
timeout: 5 * 60 * 60 * 1000,
|
||||
onError: handleProxyError,
|
||||
});
|
||||
function handleProxyError(err: any) {
|
||||
api.logger.error('Local proxy error. Error is ', err);
|
||||
}
|
||||
app.use(remoteProxy);
|
||||
|
||||
app.listen(proxyConfig.localPort, () => {
|
||||
api.logger.info(`Start proxy client on http://localhost:${proxyConfig.localPort}`);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { API } from '../../../types/types';
|
||||
import jest from 'jest';
|
||||
import yargsParser from 'yargs-parser';
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: 'jest',
|
||||
description: 'run jest test',
|
||||
fn: async (args: yargsParser.Arguments, config: any) => {
|
||||
await jest.run();
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { API } from '../../../types/types';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
interface PackageJson {
|
||||
version: string;
|
||||
}
|
||||
|
||||
const pkgPath = path.resolve(__dirname, '../../../../package.json');
|
||||
|
||||
// 读取 package.json 文件
|
||||
const packageJson = fs.readFileSync(pkgPath, 'utf8');
|
||||
|
||||
// 解析 JSON 格式的数据
|
||||
const packageData: PackageJson = JSON.parse(packageJson);
|
||||
|
||||
// 获取版本号
|
||||
const version = packageData.version;
|
||||
|
||||
export default (api: API) => {
|
||||
api.registerCommand({
|
||||
name: 'version',
|
||||
description: 'show inula-cli version',
|
||||
fn: () => {
|
||||
api.logger.info(`Inula-cli version is ${version}.`);
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 chalk from 'chalk';
|
||||
import yargsParser from 'yargs-parser';
|
||||
import Hub from '../core/Hub.js';
|
||||
import initializeEnv from '../utils/initializeEnv.js';
|
||||
import { Logger, LogLevel } from '../utils/logger.js';
|
||||
|
||||
export default async function run() {
|
||||
const args: yargsParser.Arguments = yargsParser(process.argv.slice(2));
|
||||
const alias: Record<string, string> = {
|
||||
h: 'help',
|
||||
v: 'version',
|
||||
g: 'generate',
|
||||
};
|
||||
let command: string | number | undefined = args._[0];
|
||||
|
||||
if (!command) {
|
||||
if (args['v'] || args['version']) {
|
||||
command = 'v';
|
||||
}
|
||||
if (args['h'] || args['help']) {
|
||||
command = 'h';
|
||||
}
|
||||
}
|
||||
|
||||
const aliasCommand: string | undefined = alias[command];
|
||||
|
||||
if (aliasCommand) {
|
||||
command = aliasCommand;
|
||||
}
|
||||
|
||||
initializeEnv();
|
||||
|
||||
if (command === 'version' || command === 'help') {
|
||||
process.env.INNER_COMMAND = "true"
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case 'build':
|
||||
process.env.NODE_ENV = 'production';
|
||||
break;
|
||||
case 'dev':
|
||||
process.env.NODE_ENV = 'development';
|
||||
break;
|
||||
default:
|
||||
process.env.NODE_ENV = 'development';
|
||||
break;
|
||||
}
|
||||
|
||||
let enableDebug: boolean = false;
|
||||
|
||||
if (process.env.DEBUG === "true") {
|
||||
enableDebug = true;
|
||||
}
|
||||
|
||||
const logger: Logger = new Logger(enableDebug ? LogLevel.DEBUG : LogLevel.INFO);
|
||||
|
||||
try {
|
||||
new Hub({
|
||||
logger: logger,
|
||||
}).run({
|
||||
command,
|
||||
args,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
logger.error(chalk.red(err.message));
|
||||
if (err.stack) {
|
||||
logger.error(err.stack);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 { existsSync } from 'fs';
|
||||
import { extname, join } from 'path';
|
||||
import { parseRequireDeps, cleanRequireCache } from '../utils/util.js';
|
||||
import deepmerge from 'deepmerge';
|
||||
import { loadModule } from '../utils/loadModule.js';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
import { UserConfig } from '../types/types.js';
|
||||
|
||||
interface ConfigOpts {
|
||||
cwd: string;
|
||||
isLocal?: boolean;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_FILES = ['.inula.ts', '.inula.js'];
|
||||
|
||||
export default class Config {
|
||||
cwd: string;
|
||||
isLocal: boolean;
|
||||
configFile?: string | null;
|
||||
logger: Logger;
|
||||
|
||||
constructor(opts: ConfigOpts) {
|
||||
this.cwd = opts.cwd || process.cwd();
|
||||
this.isLocal = opts.isLocal || process.env.NODE_ENV === 'development';
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
async getUserConfig(): Promise<UserConfig> {
|
||||
const configFile: string | null = this.getConfigFile();
|
||||
if (configFile === null) {
|
||||
this.logger.debug(`Can't find .inula.ts or .inula.js in ${this.cwd}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
this.configFile = configFile;
|
||||
|
||||
if (configFile) {
|
||||
let envConfigFile: string | undefined = undefined;
|
||||
if (process.env.RUNNING_MODE) {
|
||||
envConfigFile = this.addModePath(configFile, process.env.RUNNING_MODE);
|
||||
}
|
||||
// 配置文件的来源
|
||||
// 1、默认的configFile 如.inula.ts
|
||||
// 2、带环境变量的configFile 如.inula.cloud.ts
|
||||
// 3、dev模式 包含local 如.inula.local.ts
|
||||
const files: string[] = [configFile];
|
||||
if (envConfigFile && existsSync(envConfigFile)) {
|
||||
files.push(envConfigFile);
|
||||
}
|
||||
|
||||
if (this.isLocal) {
|
||||
const localConfigFile = this.addModePath(configFile, 'local');
|
||||
if (existsSync(localConfigFile)) {
|
||||
files.push(localConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(`Find user config files ${files}`);
|
||||
|
||||
// 依次加载配置文件中的依赖并刷新require中的缓存
|
||||
const requireDeps = files.reduce((deps: string[], file) => {
|
||||
deps = deps.concat(parseRequireDeps(file));
|
||||
return deps;
|
||||
}, []);
|
||||
requireDeps.forEach(cleanRequireCache);
|
||||
|
||||
const configs = await this.requireConfigs(files);
|
||||
return this.mergeConfig(...configs);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
getConfigFile(): string | null {
|
||||
const configFileList: string[] = DEFAULT_CONFIG_FILES.map(f => join(this.cwd, f));
|
||||
for (let configFile of configFileList) {
|
||||
if (existsSync(configFile)) {
|
||||
return configFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
addModePath(file: string, mode: string) {
|
||||
const ext = extname(file);
|
||||
return file.replace(new RegExp(`${ext}$`), `.${mode}${ext}`);
|
||||
}
|
||||
|
||||
async requireConfigs(configFiles: string[]) {
|
||||
const configs: UserConfig[] = [];
|
||||
for (const file in configFiles) {
|
||||
const content: UserConfig | undefined = await loadModule<UserConfig>(configFiles[file]);
|
||||
if (content) {
|
||||
configs.push(content);
|
||||
}
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
mergeConfig(...configs: UserConfig[]) {
|
||||
let ret: UserConfig = {};
|
||||
for (const config of configs) {
|
||||
ret = deepmerge<UserConfig>(ret, config);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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 { join, isAbsolute } from 'path';
|
||||
import Config from '../config/Config.js';
|
||||
import { BuildConfig, DevBuildConfig, DevProxy, ICommand, UserConfig } from '../types/types.js';
|
||||
import { ServiceStage } from '../enum/enum.js';
|
||||
import Plugin from '../plugin/Plugin.js';
|
||||
import { appendFile, existsSync } from 'fs';
|
||||
import { createRequire } from 'module';
|
||||
import { Logger, LogLevel } from '../utils/logger.js';
|
||||
import { loadModule } from '../utils/loadModule.js';
|
||||
import readDirectory from '../utils/readDirectory.js';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import yargsParser from 'yargs-parser';
|
||||
import { PackageJSON } from 'resolve';
|
||||
import { loadPkg } from '../utils/loadPkg.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
interface HubOpts {
|
||||
cwd?: string;
|
||||
logger?: Logger;
|
||||
}
|
||||
|
||||
export default class Hub {
|
||||
args: any;
|
||||
cwd: string;
|
||||
env: string | undefined;
|
||||
configManager: Config;
|
||||
userConfig: UserConfig = {};
|
||||
packageJson: PackageJSON;
|
||||
stage: ServiceStage = ServiceStage.uninitialized;
|
||||
buildConfig: {name:string, config: object}[] = [];
|
||||
pluginManager: Plugin;
|
||||
buildConfigPath: BuildConfig[] = [];
|
||||
devBuildConfig: object = {};
|
||||
compileMode: string = '';
|
||||
builtInPlugins: string[] = [];
|
||||
pluginPaths: string[] = [];
|
||||
devProxy: DevProxy | null = null;
|
||||
logger: Logger;
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
constructor(opts: HubOpts) {
|
||||
this.setStage(ServiceStage.constructor);
|
||||
this.cwd = opts.cwd || process.cwd();
|
||||
this.env = process.env.NODE_ENV;
|
||||
|
||||
if (!opts.logger) {
|
||||
this.logger = new Logger(LogLevel.INFO);
|
||||
} else {
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
this.packageJson = loadPkg(path.join(this.cwd, './package.json'));
|
||||
|
||||
this.configManager = new Config({
|
||||
cwd: this.cwd,
|
||||
isLocal: this.env === 'development',
|
||||
logger: this.logger,
|
||||
});
|
||||
|
||||
this.pluginManager = new Plugin({
|
||||
cwd: this.cwd,
|
||||
hub: this,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
setStage(stage: ServiceStage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.setStage(ServiceStage.init);
|
||||
|
||||
// 获取用户配置
|
||||
this.userConfig = await this.configManager.getUserConfig();
|
||||
|
||||
// 设置编译模式
|
||||
this.setCompileMode()
|
||||
|
||||
// 获取编译配置
|
||||
await this.analyzeBuildConfig();
|
||||
|
||||
this.setStage(ServiceStage.initPlugins);
|
||||
this.builtInPlugins = this.getBuiltInPlugins();
|
||||
await this.pluginManager.register(this.builtInPlugins, this.userConfig.plugins);
|
||||
|
||||
this.setStage(ServiceStage.initHooks);
|
||||
this.pluginManager.initHook();
|
||||
}
|
||||
|
||||
getBuiltInPlugins(): string[] {
|
||||
return readDirectory(path.resolve(__dirname, '../builtInPlugins'));
|
||||
}
|
||||
|
||||
async run({ command, args }: { command: string | number; args: yargsParser.Arguments }) {
|
||||
args._ = args._ || [];
|
||||
if (args._[0] === command) {
|
||||
args._.shift();
|
||||
}
|
||||
|
||||
this.args = args;
|
||||
|
||||
await this.init();
|
||||
|
||||
this.setStage(ServiceStage.run);
|
||||
return this.runCommand({ command, args });
|
||||
}
|
||||
|
||||
async runCommand({ command, args }: { command: string | number; args: yargsParser.Arguments }) {
|
||||
this.logger.debug(`run command ${command}`);
|
||||
|
||||
const commands =
|
||||
typeof this.pluginManager.commands[command] === 'string'
|
||||
? this.pluginManager.commands[this.pluginManager.commands[command] as string]
|
||||
: this.pluginManager.commands[command];
|
||||
|
||||
if (commands === undefined) {
|
||||
this.logger.error(`Invalid command ${command}`)
|
||||
return
|
||||
}
|
||||
const { fn } = commands as ICommand;
|
||||
|
||||
return fn(args, this.pluginManager.store[command]);
|
||||
}
|
||||
|
||||
setCompileMode() {
|
||||
this.compileMode = this.userConfig.compileMode || 'webpack';
|
||||
this.logger.debug(`current compile mode is ${this.compileMode}`);
|
||||
}
|
||||
|
||||
async analyzeBuildConfig() {
|
||||
if (this.userConfig.devBuildConfig) {
|
||||
let { name, path, env } = this.userConfig.devBuildConfig;
|
||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
||||
if (!existsSync(path)) {
|
||||
this.logger.warn(`Cant't find dev build config. Path is ${path}`);
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`Find dev build config. Path is ${path}`);
|
||||
let bc = await loadModule<object | Function>(path);
|
||||
if (bc == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let finalBc = {};
|
||||
if (typeof bc === 'function') {
|
||||
finalBc = bc(env)
|
||||
this.devBuildConfig = finalBc;
|
||||
return;
|
||||
}
|
||||
this.devBuildConfig = bc;
|
||||
|
||||
if (this.userConfig.devBuildConfig.devProxy) {
|
||||
this.devProxy = this.userConfig.devBuildConfig.devProxy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!this.userConfig.buildConfig) {
|
||||
switch (this.compileMode) {
|
||||
case 'webpack':
|
||||
this.buildConfigPath.push({name:'default', path:'./webpack.config.js'})
|
||||
break;
|
||||
case 'vite':
|
||||
this.buildConfigPath.push({name:'default', path:'./vite.config.js'})
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(`Unknown compile mode ${this.compileMode}`);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.userConfig.buildConfig.forEach((userBuildConfig) => {
|
||||
if (typeof userBuildConfig === 'object') {
|
||||
this.buildConfigPath.push(userBuildConfig);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.buildConfigPath.forEach(async (config) => {
|
||||
let {name, path} = config;
|
||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
||||
if (!existsSync(path)) {
|
||||
this.logger.debug(`Cant't find build config. Path is ${path}`);
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`Find build config. Path is ${path}`);
|
||||
let bc = await loadModule<object | Function >(path);
|
||||
if (bc == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let finalBc = {};
|
||||
if (typeof bc === 'function') {
|
||||
finalBc = bc(config.env)
|
||||
this.buildConfig.push({name: name, config: finalBc});
|
||||
return;
|
||||
}
|
||||
this.buildConfig.push({name: name, config: bc});
|
||||
})
|
||||
}
|
||||
|
||||
getConfigName(name: string): string {
|
||||
name = name.replace('webpack.', '');
|
||||
name = name.replace('.js', '');
|
||||
name = name.replace('.ts', '');
|
||||
return name
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export enum PluginType {
|
||||
preset = 'preset',
|
||||
plugin = 'plugin',
|
||||
}
|
||||
|
||||
export enum ServiceStage {
|
||||
uninitialized,
|
||||
constructor,
|
||||
init,
|
||||
initPlugins,
|
||||
initHooks,
|
||||
pluginReady,
|
||||
getConfig,
|
||||
run,
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 resolve from 'resolve';
|
||||
import chalk from 'chalk';
|
||||
import PluginAPI, { IOpts } from './PluginAPI.js';
|
||||
import { IHook, ICommand } from '../types/types.js';
|
||||
import Hub from '../core/Hub';
|
||||
import { loadModule } from '../utils/loadModule.js';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
|
||||
interface pluginManagerOpts {
|
||||
cwd: string;
|
||||
hub: Hub;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
interface PluginObj {
|
||||
path: string;
|
||||
api: PluginAPI;
|
||||
}
|
||||
|
||||
export interface IPlugin {
|
||||
id: string;
|
||||
key: string;
|
||||
path: string;
|
||||
apply: Function;
|
||||
}
|
||||
|
||||
export default class Plugin {
|
||||
cwd: string;
|
||||
builtInPlugins: string[] = [];
|
||||
userPlugins: string[] = [];
|
||||
commands: {
|
||||
[name: string]: ICommand | string;
|
||||
} = {};
|
||||
hooksByPluginPath: {
|
||||
[id: string]: IHook[];
|
||||
} = {};
|
||||
hooks: {
|
||||
[key: string]: IHook[];
|
||||
} = {};
|
||||
store: {
|
||||
[key: string]: any;
|
||||
} = {};
|
||||
hub: Hub;
|
||||
logger: Logger;
|
||||
registerFunction: Function[] = [];
|
||||
// 解决调用this[props]时ts提示属性未知
|
||||
[key: string]: any;
|
||||
|
||||
constructor(opts: pluginManagerOpts) {
|
||||
this.cwd = opts.cwd || process.cwd();
|
||||
this.hub = opts.hub;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
getPluginPaths(builtInPlugins: string[], userPlugins: string[] | undefined): string[] {
|
||||
const paths: string[] = [];
|
||||
paths.push(...builtInPlugins);
|
||||
if (userPlugins) {
|
||||
paths.push(...userPlugins);
|
||||
}
|
||||
|
||||
// 获取所有插件文件的绝对路径
|
||||
const absPaths: string[] = paths.map(path => {
|
||||
return resolve.sync(path, {
|
||||
basedir: this.cwd,
|
||||
extensions: ['.js', '.ts'],
|
||||
});
|
||||
});
|
||||
|
||||
return absPaths;
|
||||
}
|
||||
|
||||
setStore(name: string, initialValue: any) {
|
||||
const store = this.store;
|
||||
if (this.store[name]) {
|
||||
return;
|
||||
}
|
||||
store[name] = initialValue;
|
||||
}
|
||||
|
||||
async register(builtInPlugins: string[], userPlugins: string[] | undefined) {
|
||||
const paths = this.getPluginPaths(builtInPlugins, userPlugins);
|
||||
this.hub.pluginPaths = paths;
|
||||
|
||||
const objs: PluginObj[] = paths.map(path => {
|
||||
const api: PluginAPI = this.createPluginAPI({
|
||||
path,
|
||||
manager: this,
|
||||
logger: this.logger,
|
||||
});
|
||||
return {
|
||||
path,
|
||||
api,
|
||||
};
|
||||
});
|
||||
|
||||
for (const obj of objs) {
|
||||
const module: Function | undefined = await loadModule(obj.path);
|
||||
if (module) {
|
||||
try {
|
||||
module(obj.api);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
this.logger.error(chalk.red(err.message));
|
||||
if (err.stack) {
|
||||
this.logger.error(err.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo 给API换个名字
|
||||
createPluginAPI(opts: IOpts): PluginAPI {
|
||||
const pluginAPI = new PluginAPI(opts);
|
||||
|
||||
// 为PluginAPI添加代理
|
||||
// 除了PluginAPI自有的方法之外,为开发者提供更丰富的api
|
||||
return new Proxy(pluginAPI, {
|
||||
get: (target: PluginAPI, prop: string) => {
|
||||
if (['userConfig', 'devBuildConfig', 'buildConfig', 'compileMode', 'packageJson', 'cwd'].includes(prop)) {
|
||||
return typeof this.hub[prop] === 'function'
|
||||
? this.hub[prop].bind(this.hub)
|
||||
: this.hub[prop];
|
||||
}
|
||||
|
||||
if (['setStore', 'logger', 'commands'].includes(prop)) {
|
||||
return typeof this[prop] === 'function'
|
||||
? this[prop].bind(this)
|
||||
: this[prop];
|
||||
}
|
||||
|
||||
return target[prop];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
initHook() {
|
||||
Object.keys(this.hooksByPluginPath).forEach(path => {
|
||||
const hooks = this.hooksByPluginPath[path];
|
||||
hooks.forEach(hook => {
|
||||
const { name } = hook;
|
||||
hook.pluginId = path;
|
||||
if (!this.hooks[name]) {
|
||||
this.hooks[name] = [];
|
||||
}
|
||||
this.hooks[name].push(hook);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 Plugin from './Plugin.js';
|
||||
import { IHook, ICommand } from '../types/types.js';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
|
||||
export interface IOpts {
|
||||
path: string;
|
||||
manager: Plugin;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export default class PluginAPI {
|
||||
path: string;
|
||||
manager: Plugin;
|
||||
logger: Logger;
|
||||
[key: string]: any;
|
||||
|
||||
constructor(opts: IOpts) {
|
||||
this.path = opts.path;
|
||||
this.manager = opts.manager;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
register(hook: IHook) {
|
||||
if (!this.manager.hooksByPluginPath[this.path]) {
|
||||
this.manager.hooksByPluginPath[this.path] = [];
|
||||
}
|
||||
|
||||
this.manager.hooksByPluginPath[this.path].push(hook);
|
||||
}
|
||||
|
||||
registerCommand(command: ICommand) {
|
||||
const { name } = command;
|
||||
this.manager.commands[name] = command;
|
||||
if (command.initialState) {
|
||||
this.manager.setStore(name, command.initialState);
|
||||
}
|
||||
}
|
||||
|
||||
registerHook(hook: IHook) {
|
||||
this.register(hook);
|
||||
}
|
||||
|
||||
registerMethod(fn: Function) {
|
||||
this.manager.registerFunction.push(fn);
|
||||
}
|
||||
|
||||
async applyHook(name: string, args?: any ) {
|
||||
const hooks: IHook[] = this.manager.hooks[name] || [];
|
||||
let config: any = undefined;
|
||||
for (const hook of hooks) {
|
||||
if (this.manager.store[name]) {
|
||||
config = this.manager.store[name];
|
||||
}
|
||||
if (hook.fn) {
|
||||
await hook.fn(args, config);
|
||||
}
|
||||
}
|
||||
return this.manager.store[name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
module.exports = sum;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum = require('./sum');
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export default sum;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum from './sum';
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).(js?(x)|ts?(x))$'],
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum from './sum';
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export default sum;
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 { PackageJSON } from 'resolve';
|
||||
import yargsParser from 'yargs-parser';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
import type * as http from 'http';
|
||||
import type * as express from 'express';
|
||||
|
||||
|
||||
interface Request extends express.Request {
|
||||
}
|
||||
interface Response extends express.Response {
|
||||
}
|
||||
|
||||
export interface IDep {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
export interface IPackage {
|
||||
name?: string;
|
||||
dependencies?: IDep;
|
||||
devDependencies?: IDep;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface IPlugin {
|
||||
id: string;
|
||||
key: string;
|
||||
path: string;
|
||||
apply: Function;
|
||||
|
||||
config?: IPluginConfig;
|
||||
isPreset?: boolean;
|
||||
}
|
||||
|
||||
export interface IPluginConfig {
|
||||
default?: any;
|
||||
onChange?: string | Function;
|
||||
}
|
||||
|
||||
export interface IHook {
|
||||
// 触发事件名称
|
||||
name: string;
|
||||
fn?: {
|
||||
(state: any, config: any): void;
|
||||
};
|
||||
pluginId?: string;
|
||||
}
|
||||
|
||||
export interface ICommand {
|
||||
name: string;
|
||||
description?: string;
|
||||
details?: string;
|
||||
initialState?: any;
|
||||
fn: {
|
||||
(args: yargsParser.Arguments, config: any): void;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IConfig {
|
||||
plugins?: string[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface applyHookConfig<T = any> {
|
||||
name: string;
|
||||
config?: T;
|
||||
}
|
||||
|
||||
export interface API {
|
||||
cwd: string;
|
||||
logger: Logger;
|
||||
userConfig: IConfig;
|
||||
buildConfig: any;
|
||||
devBuildConfig: any;
|
||||
compileMode: string;
|
||||
commands: string[];
|
||||
packageJson: PackageJSON;
|
||||
|
||||
registerCommand: {
|
||||
(command: ICommand): void;
|
||||
};
|
||||
registerHook: {
|
||||
(hook: IHook): void;
|
||||
};
|
||||
registerMethod: {
|
||||
(method: Function): void;
|
||||
}
|
||||
applyHook: {
|
||||
(opts: applyHookConfig): void;
|
||||
};
|
||||
setStore: {
|
||||
(name: string, initialState: any): void;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RemoteProxy {
|
||||
target: string;
|
||||
localPort?: number;
|
||||
localStatic?: StaticFileMatcher[];
|
||||
fowardingURL?: string[];
|
||||
}
|
||||
|
||||
export interface StaticFileMatcher {
|
||||
url: string;
|
||||
local: string;
|
||||
}
|
||||
|
||||
export interface UserConfig {
|
||||
mock?: MockConfig;
|
||||
proxy?: RemoteProxy;
|
||||
plugins?: string[];
|
||||
compileMode?: string;
|
||||
buildConfig?: BuildConfig[];
|
||||
devBuildConfig?: DevBuildConfig;
|
||||
}
|
||||
|
||||
export interface MockConfig {
|
||||
enableMock?: boolean;
|
||||
mockPath?: string;
|
||||
}
|
||||
|
||||
export interface DevBuildConfig {
|
||||
name: string;
|
||||
path: string;
|
||||
args?: object;
|
||||
env?: object;
|
||||
devProxy?: DevProxy;
|
||||
}
|
||||
|
||||
export interface DevProxy {
|
||||
target: string;
|
||||
matcher: ((pathname: string, req: Request) => boolean);
|
||||
onProxyRes: (proxyRes: http.IncomingMessage, req: Request, res: Response) => void;
|
||||
}
|
||||
|
||||
export interface BuildConfig {
|
||||
name: string;
|
||||
path: string;
|
||||
args?: object;
|
||||
env?: object;
|
||||
}
|
||||
|
||||
export type ExportUserConfig = UserConfig | Promise<UserConfig>;
|
||||
|
||||
export function defineConfig(config: ExportUserConfig): ExportUserConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
export interface Arguments {
|
||||
_: Array<string | number>;
|
||||
'--'?: Array<string | number>;
|
||||
[argName: string]: any;
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 path from 'path';
|
||||
import fs from 'fs';
|
||||
import { build as esbuild, Plugin } from 'esbuild';
|
||||
|
||||
const buildConfig = async (fileName: string, format: 'esm' | 'cjs' = 'esm'): Promise<string> => {
|
||||
// 外部依赖不构建参与构建,减少执行时间
|
||||
const pluginExternalDeps: Plugin = {
|
||||
name: 'plugin-external-deps',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /.*/ }, args => {
|
||||
const id = args.path;
|
||||
if (id[0] !== '.' && !path.isAbsolute(id)) {
|
||||
return {
|
||||
external: true,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// 将文件中的路径改成确定路径,避免执行时调用错误
|
||||
const pluginReplaceImport: Plugin = {
|
||||
name: 'plugin-replace-import-meta',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.[jt]s$/ }, args => {
|
||||
const contents: string = fs.readFileSync(args.path, 'utf8');
|
||||
|
||||
// 替换import路径
|
||||
contents.replace(/\bimport\.meta\.url\b/g, JSON.stringify(`file://${args.path}`));
|
||||
|
||||
// 替换当前目录路径
|
||||
contents.replace(/\b__dirname\b/g, JSON.stringify(path.dirname(args.path)));
|
||||
|
||||
// 替换当前文件路径
|
||||
contents.replace(/\b__filename\b/g, JSON.stringify(args.path));
|
||||
|
||||
return {
|
||||
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
|
||||
contents: contents
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const buildEntry = [fileName];
|
||||
const result = await esbuild({
|
||||
entryPoints: buildEntry,
|
||||
outdir: 'build',
|
||||
write: false,
|
||||
platform: 'node',
|
||||
bundle: true,
|
||||
format,
|
||||
metafile: true,
|
||||
plugins: [pluginExternalDeps, pluginReplaceImport],
|
||||
});
|
||||
const { text } = result.outputFiles[0];
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
export default buildConfig;
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default async function dynamicImport(filePath: string) {
|
||||
let importPath = filePath;
|
||||
|
||||
importPath = 'file:///' + importPath;
|
||||
return await import(importPath);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { join } from 'path';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { parse } from 'dotenv';
|
||||
|
||||
export default function initializeEnv(): void {
|
||||
const envPath: string = join(process.cwd(), '.env');
|
||||
const localEnvPath: string = join(process.cwd(), '.local.env');
|
||||
loadEnv(envPath);
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
loadEnv(localEnvPath);
|
||||
}
|
||||
}
|
||||
|
||||
function loadEnv(envPath: string): void {
|
||||
if (existsSync(envPath)) {
|
||||
const parsed = parse(readFileSync(envPath, 'utf-8')) || {};
|
||||
Object.keys(parsed).forEach(key => {
|
||||
process.env[key] = parsed[key];
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 { pathToFileURL } from 'url';
|
||||
import { join, isAbsolute } from 'path';
|
||||
import fs from 'fs';
|
||||
import buildConfig from './build.js';
|
||||
import { createRequire } from 'module';
|
||||
import dynamicImport from './dynamicImport.js';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
export async function loadModule<T>(filePath: string): Promise<T | undefined> {
|
||||
filePath = isAbsolute(filePath) ? filePath : join(process.cwd(), filePath);
|
||||
|
||||
const isTsFile: boolean = filePath.endsWith('ts');
|
||||
const isJsFile: boolean = filePath.endsWith('js');
|
||||
|
||||
|
||||
let content: T | undefined;
|
||||
|
||||
// js文件,可以直接通过import引用
|
||||
if (isJsFile) {
|
||||
content = (await dynamicImport(filePath))?.default;
|
||||
}
|
||||
|
||||
// 如果是ts文件,需要先转为js文件,再读取
|
||||
if (isTsFile) {
|
||||
const code = await buildConfig(filePath, 'esm');
|
||||
content = await getTypescriptModule(code, filePath);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
async function getTypescriptModule(code: string, filePath: string, isEsm = true) {
|
||||
const tempFile = `${filePath}.${isEsm ? 'm' : 'c'}js`;
|
||||
let content = null;
|
||||
|
||||
// todo 臨時文件管理
|
||||
fs.writeFileSync(tempFile, code);
|
||||
|
||||
delete require.cache[require.resolve(tempFile)];
|
||||
|
||||
try {
|
||||
const raw = isEsm ? await dynamicImport(tempFile) : require(tempFile);
|
||||
content = raw?.default ?? raw;
|
||||
} catch (err: unknown) {
|
||||
fs.unlinkSync(tempFile);
|
||||
if (err instanceof Error) {
|
||||
err.message = err.message.replace(tempFile, filePath);
|
||||
err.stack = err.stack?.replace(tempFile, filePath);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// todo 刪除失敗加日誌
|
||||
fs.unlinkSync(tempFile);
|
||||
|
||||
return content;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 fs from 'fs';
|
||||
import { PackageJSON } from 'resolve';
|
||||
|
||||
export const loadPkg = (path: string): PackageJSON => {
|
||||
const packageJson = fs.readFileSync(path, 'utf8');
|
||||
const packageData: PackageJSON = JSON.parse(packageJson);
|
||||
return packageData;
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export enum LogLevel {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3,
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
private readonly level: LogLevel;
|
||||
|
||||
constructor(level?: LogLevel) {
|
||||
if (level !== undefined) {
|
||||
this.level = level;
|
||||
} else {
|
||||
this.level = LogLevel.INFO;
|
||||
}
|
||||
}
|
||||
|
||||
debug(message: string): void {
|
||||
if (this.level <= LogLevel.DEBUG) {
|
||||
console.debug(`[DEBUG] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
info(message: string): void {
|
||||
if (this.level <= LogLevel.INFO) {
|
||||
console.info(`[INFO] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: string): void {
|
||||
if (this.level <= LogLevel.WARN) {
|
||||
console.warn(`[WARN] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
error(message: string, error?: Error): void {
|
||||
if (this.level <= LogLevel.ERROR) {
|
||||
console.error(`[ERROR] ${message}`, error || '');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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 chokidar from 'chokidar';
|
||||
import bodyParser from 'body-parser';
|
||||
import {globSync} from 'glob';
|
||||
import { join } from 'path';
|
||||
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const mockDir = join(process.cwd(), 'mock');
|
||||
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
|
||||
|
||||
const jsonParser = bodyParser.json();
|
||||
const urlencodedParser = bodyParser.urlencoded({
|
||||
extended: true,
|
||||
});
|
||||
|
||||
interface Mock {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 读取 mock 文件夹下的 js 文件
|
||||
function getMocksFile() {
|
||||
const mockFiles = globSync('**/*.js', {
|
||||
cwd: mockDir,
|
||||
});
|
||||
let ret = mockFiles.reduce((mocks: any, mockFile: string) => {
|
||||
if (!mockFile.startsWith('_')) {
|
||||
mocks = {
|
||||
...mocks,
|
||||
...require(join(mockDir, mockFile)),
|
||||
};
|
||||
console.log('mockFile', require(join(mockDir, mockFile)));
|
||||
}
|
||||
|
||||
return mocks;
|
||||
}, {});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function generateRoutes(app: any) {
|
||||
let mockStartIndex = app._router.stack.length,
|
||||
mocks: Mock = {};
|
||||
|
||||
try {
|
||||
mocks = getMocksFile();
|
||||
} catch (error) {
|
||||
console.error('Generate mock routes error', error);
|
||||
}
|
||||
|
||||
for (const mockItem in mocks) {
|
||||
if (Object.prototype.hasOwnProperty.call(mocks, mockItem)) {
|
||||
try {
|
||||
const trimMockItemArr = mockItem
|
||||
.replace(/(^\s*)|(\s*$)/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.split(' ');
|
||||
|
||||
const respond = mocks[mockItem];
|
||||
|
||||
let mockType = 'get',
|
||||
mockUrl;
|
||||
if (trimMockItemArr.length === 1) {
|
||||
mockUrl = trimMockItemArr[0];
|
||||
} else {
|
||||
[mockType, mockUrl] = trimMockItemArr;
|
||||
}
|
||||
|
||||
const mockTypeLowerCase = mockType.toLowerCase();
|
||||
|
||||
if (!HTTP_METHODS.includes(mockTypeLowerCase)) {
|
||||
throw new Error(`Invalid HTTP request method ${mockType} for path ${mockUrl}`);
|
||||
}
|
||||
|
||||
app[mockTypeLowerCase](
|
||||
mockUrl,
|
||||
[jsonParser, urlencodedParser],
|
||||
respond instanceof Function
|
||||
? respond
|
||||
: (_req: any, res: { send: (arg0: any) => void }) => {
|
||||
res.send(respond);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
mockRoutesLength: app._router.stack.length - mockStartIndex,
|
||||
mockStartIndex: mockStartIndex,
|
||||
};
|
||||
}
|
||||
|
||||
// 清除 mock 文件下的 require 缓存
|
||||
function cleanRequireCache() {
|
||||
Object.keys(require.cache).forEach(key => {
|
||||
if (key.includes(mockDir)) {
|
||||
delete require.cache[require.resolve(key)];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default (app: { _router: { stack: any[] } }) => {
|
||||
const mockRoutes = generateRoutes(app);
|
||||
let { mockRoutesLength } = mockRoutes;
|
||||
let { mockStartIndex } = mockRoutes;
|
||||
|
||||
// 监听 mock 文件夹下文件变化
|
||||
chokidar
|
||||
.watch(mockDir, {
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event: string, _path: any) => {
|
||||
if (event === 'change' || event === 'add') {
|
||||
try {
|
||||
// 删除中间件映射
|
||||
app._router.stack.splice(mockStartIndex, mockRoutesLength);
|
||||
|
||||
cleanRequireCache();
|
||||
const mockRoutes = generateRoutes(app);
|
||||
|
||||
mockRoutesLength = mockRoutes.mockRoutesLength;
|
||||
mockStartIndex = mockRoutes.mockStartIndex;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export default function readDirectory(directoryPath: string): string[] {
|
||||
const filesArray: string[] = [];
|
||||
const traverseDirectory = (directoryPath: string) => {
|
||||
const files = fs.readdirSync(directoryPath);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(directoryPath, file);
|
||||
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
// 如果是目录,则递归读取该目录下的所有文件
|
||||
traverseDirectory(filePath);
|
||||
} else {
|
||||
if (filePath.startsWith('.')) {
|
||||
continue;
|
||||
}
|
||||
// 如果是文件,则将其全路径添加到数组中
|
||||
if (filePath.endsWith('.js')) {
|
||||
filesArray.push(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
traverseDirectory(directoryPath);
|
||||
return filesArray;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { createProxyMiddleware } from 'http-proxy-middleware';
|
||||
import { API } from '../types/types';
|
||||
|
||||
export default (app: any, api: API) => {
|
||||
const { devProxy } = api.userConfig.devBuildConfig;
|
||||
app.use(createProxyMiddleware(devProxy.matcher, {
|
||||
target: devProxy.target,
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
ws: false,
|
||||
onProxyRes: devProxy.onProxyRes
|
||||
}));
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 { dirname } from 'path';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import resolve from 'resolve';
|
||||
// @ts-ignore
|
||||
import crequire from 'crequire'
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
function parse(filePath: string): string[] {
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
return (crequire(content) as any[])
|
||||
.map<string>(o => o.path)
|
||||
.filter(path => path.charAt(0) === '.')
|
||||
.map(path =>
|
||||
resolve.sync(path, {
|
||||
basedir: dirname(filePath),
|
||||
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function parseRequireDeps(filePath: string): string[] {
|
||||
const paths: string[] = [filePath];
|
||||
const ret: string[] = [filePath];
|
||||
|
||||
while (paths.length) {
|
||||
const extraPaths = parse(paths.shift()!).filter(path => !ret.includes(path));
|
||||
if (extraPaths.length) {
|
||||
paths.push(...extraPaths);
|
||||
ret.push(...extraPaths);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export const isWindows = typeof process !== 'undefined' && process.platform === 'win32';
|
||||
|
||||
export function cleanRequireCache(cacheKey: string): void {
|
||||
const cachePath = isWindows ? cacheKey.replace(/\//g, '\\') : cacheKey;
|
||||
if (require.cache[cachePath]) {
|
||||
const cacheParent = (require.cache[cachePath] as any).parent;
|
||||
let i = cacheParent?.children.length || 0;
|
||||
while (i--) {
|
||||
if (cacheParent!.children[i].id === cachePath) {
|
||||
cacheParent!.children.splice(i, 1);
|
||||
}
|
||||
}
|
||||
delete require.cache[cachePath];
|
||||
}
|
||||
}
|
||||
|
||||
export function copyFile(targetPath: string, sourcePath: string): void {
|
||||
try {
|
||||
const fileContent = readFileSync(sourcePath);
|
||||
writeFileSync(targetPath, fileContent);
|
||||
} catch (error) {
|
||||
console.error('Copy file failed.', error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
module.exports = sum;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum = require('./sum');
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export default sum;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum from './sum';
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
||||
|
||||
// 匹配的测试文件模式
|
||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).(js?(x)|ts?(x))$'],
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 sum from './sum';
|
||||
|
||||
describe('sum', () => {
|
||||
it('should add two numbers', () => {
|
||||
expect(sum(1, 2)).toEqual(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function sum(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export default sum;
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "esnext",
|
||||
"sourceMap": false,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "**/*.spec.ts", "./src/template/**/*"],
|
||||
"ts-node": {
|
||||
"esm": true,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
root: true,
|
||||
|
||||
plugins: ['jest', 'no-for-of-loops', 'no-function-declare-after-return', 'react', '@typescript-eslint'],
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 8,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
modules: true,
|
||||
experimentalObjectRestSpread: true,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
jest: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
semi: ['warn', 'always'],
|
||||
quotes: ['warn', 'single'],
|
||||
'accessor-pairs': 'off',
|
||||
'brace-style': ['error', '1tbs'],
|
||||
'func-style': ['warn', 'declaration', { allowArrowFunctions: true }],
|
||||
'max-lines-per-function': 'off',
|
||||
'object-curly-newline': 'off',
|
||||
// 尾随逗号
|
||||
'comma-dangle': ['error', 'only-multiline'],
|
||||
|
||||
'no-constant-condition': 'off',
|
||||
'no-for-of-loops/no-for-of-loops': 'error',
|
||||
'no-function-declare-after-return/no-function-declare-after-return': 'error',
|
||||
},
|
||||
globals: {
|
||||
isDev: true,
|
||||
isTest: true,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['scripts/tests/**/*.js'],
|
||||
globals: {
|
||||
container: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
printWidth: 120, // 一行120字符数,如果超过会进行换行
|
||||
tabWidth: 2, // tab等2个空格
|
||||
useTabs: false, // 用空格缩进行
|
||||
semi: true, // 行尾使用分号
|
||||
singleQuote: true, // 字符串使用单引号
|
||||
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
|
||||
jsxSingleQuote: false, // 在JSX中使用双引号
|
||||
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
|
||||
bracketSpacing: true, // 对象的括号间增加空格
|
||||
bracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
|
||||
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
|
||||
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
|
||||
endOfLine: 'lf', // 仅限换行(\n)
|
||||
};
|
|
@ -0,0 +1,727 @@
|
|||
# Inula-Intl
|
||||
|
||||
`Inula-intl`是`inula`提供的生态组件,主要提供了国际化功能,涵盖了基本的国际化组件和钩子函数,便于用户构建具备国际化能力的前端界面。
|
||||
|
||||
在`Inula-intl`中使用国际化时,无论是组件或者Hooks,其目的就是获取当前应用程序的国际化实例,该实例提供了处理多语言文本、日期、时间等功能。
|
||||
|
||||
```tsx
|
||||
import { IntlProvider, useIntl } from 'inula-intl';
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
greeting: 'Hello, {name}!',
|
||||
today: 'Today is {date}',
|
||||
amount: 'Amount: {value, number}',
|
||||
},
|
||||
fr: {
|
||||
greeting: 'Bonjour, {name} !',
|
||||
today: "Aujourd'hui, c'est le {date}",
|
||||
amount: 'Montant : {value, number}',
|
||||
},
|
||||
};
|
||||
|
||||
function App({ locale }) {
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages[locale]}>
|
||||
<Component />
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
|
||||
function Component() {
|
||||
// 创建缓存
|
||||
const cache = createIntlCache();
|
||||
|
||||
// 获取国际化对象
|
||||
const intl = createIntl({ locale, messages }, cache);
|
||||
|
||||
// 日期国际化
|
||||
const formattedDate = intl.formatDate(new Date(), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
// 数字格式化
|
||||
const formattedAmount = intl.formatNumber(12345.67);
|
||||
|
||||
const greeting = intl.formatMessage({ id: 'greeting' }, { name: 'Alice' });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{greeting}</p>
|
||||
<p>{intl.formatMessage({ id: 'today' }, { date: formattedDate })}</p>
|
||||
<p>{intl.formatMessage({ id: 'amount' }, { value: formattedAmount })}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
在这个示例中,创建了一个名为`<App>`的组件,该组件使用 `<IntlProvider>` 来提供国际化的上下文。然后,在 `<Component>`组件中使用 `useIntl` 钩子来获取国际化的功能。`useIntl` 钩子的返回对象中包含了国际化方法,如 `formatDate`、`formatNumber` 和 `formatMessage`。`<Component>` 组件演示了如何使用这些方法来格式化日期、数字和翻译文本消息
|
||||
|
||||
## 类
|
||||
|
||||
### I18n类
|
||||
|
||||
**功能介绍**
|
||||
|
||||
如果脱离了`<RawIntlProvider>`或者`<IntlProvider>`,`inula-intl`提供了一个可加载的信息类`I18n`,这要求您必须加载所需的配置信息,创建出`i18n`对象,用于完成国际化的相关功能。
|
||||
|
||||
**类定义**
|
||||
|
||||
I18n类继承了EventDispatcher,用于监听国际化资源变化,当`loacle`或者`messages`变化时,会重新加载资源。
|
||||
|
||||
```tsx
|
||||
class I18n extends EventDispatcher (i18nProps: I18nProps){
|
||||
constructor(props: I18nProps);
|
||||
}
|
||||
|
||||
interface I18nProps {
|
||||
locale?: Locale;
|
||||
locales?: Locales;
|
||||
messages?: AllMessages;
|
||||
localeConfig?: AllLocaleConfig;
|
||||
useMemorize?: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
```
|
||||
|
||||
- `locale`:表示本地语言
|
||||
- `locales`:表示可加载多个语言,如:['en','zh']
|
||||
- `localeConfig`:用于加载本地语言的规则,如:复数规则
|
||||
- `messages`:用于加载`message`信息
|
||||
- `error`: 可选参数,用于处理资源信息无法加载的错误提示
|
||||
- `useMemorize`:可选参数,用于记忆存储开关键
|
||||
|
||||
**示例**
|
||||
|
||||
以动态加载方式创建国际化对象,以使用国际化功能。
|
||||
|
||||
```tsx
|
||||
import {I18n} from 'inula-intl';
|
||||
|
||||
function App() => {
|
||||
const messages = {
|
||||
'greeting': 'Hello, {name}',
|
||||
|
||||
};
|
||||
const i18n = new I18n({
|
||||
locale: 'fr',
|
||||
messages: messages,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
{i18n.formatMessage('Hello',{name: 'Fred'})}
|
||||
{i18n.formatDate(new Date(), {year: 'numeric',month: 'numeric',day: 'numeric'})}
|
||||
{i18n.numberFormat(1000, {style: 'currency', currency: 'USD'})}
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上述示例展示了,若脱离组件,直接通过实例化一个`I18n`类来实现国际化。
|
||||
|
||||
> 注意,使用者须构造传参,以确保`I18n`类正确实例化。
|
||||
|
||||
### DateTimeFormatter类
|
||||
|
||||
**功能介绍**
|
||||
DateTimeFormatter 类主要提供了时间格式化方法,开发者可以基于类直接进行格式化而不需要获取`i18n`实例。
|
||||
|
||||
**类定义**
|
||||
|
||||
```tsx
|
||||
class DateTimeFormatter {
|
||||
constructor(locales: any, formatOptions?: Intl.DateTimeFormatOptions, useMemorize?: boolean);
|
||||
dateTimeFormat(value: DatePool, formatOptions: FormatOptions): string;
|
||||
}
|
||||
```
|
||||
|
||||
`DateTimeFormatter`类实例化参数:
|
||||
|
||||
- `locales`:必须参数 国际化语言
|
||||
- `formatOptions`:可选参数,日期格式化选项,默认采用浏览器日期格式
|
||||
- `useMemorize`:可选参数,记忆存储开关键,默认开启存储功能。
|
||||
|
||||
`dateTimeFormat`传入两个参数:
|
||||
|
||||
- `value` :其是需要格式化的数据,有两种类型,分别为`Data` 和 `string`。
|
||||
- `formatOptions`:是自定义的格式化类型,如果不传入,默认为浏览器格式。
|
||||
|
||||
> 常用的日期格式化选项包括:
|
||||
> localeMatcher:指定日期格式的匹配方式,可以是 'lookup'(默认,从库中查找匹配)或 'best fit'(根据浏览器的语言环境选择)。
|
||||
> weekday:指定要显示的星期几信息的格式。可以是 'narrow'、'short'、'long'。
|
||||
> year、month、day:指定年、月、日的显示格式。可以是 'numeric'、'2-digit'、'narrow'、'short'、'long'。
|
||||
> hour、minute、second:指定小时、分钟、秒的显示格式。同样可以是不同的选项。
|
||||
> timeZoneName:是否显示时区名。
|
||||
> hour12:是否使用12小时制,默认为 true。
|
||||
>
|
||||
> ```jsx
|
||||
> // 示例
|
||||
> const options = {year: 'numeric',month: 'numeric',day: 'numeric'}
|
||||
> ```
|
||||
|
||||
**示例**
|
||||
|
||||
```jsx
|
||||
import DateTimeFormatter form 'inula-intl'
|
||||
|
||||
const date = new DateTimeFormatter('en', {year: 'numeric',month: 'numeric',day: 'numeric'}, true)
|
||||
|
||||
date.dateTimeFormat(new Date());
|
||||
```
|
||||
|
||||
在上述示例中,通过`DateTimeFormatter`类实例化出`data`实例,可以对传入的参数`new Date()`进行时间格式化,按照给定的时间格式` {year: 'numeric',month: 'numeric',day: 'numeric'}`。
|
||||
|
||||
### NumberFormatter类
|
||||
|
||||
**功能介绍**
|
||||
`NumberFormatter`类主要提供了数字格式化方法,开发者可以基于类直接进行格式化而不需要获取`i18n`实例。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
class NumberFormatter {
|
||||
constructor(locales: Locales, formatOption?: Intl.NumberFormatOptions, useMemorize?: boolean);
|
||||
numberFormat(value: number, formatOption?: Intl.NumberFormatOptions): string;
|
||||
}
|
||||
```
|
||||
|
||||
`NumberFormatter`类实例化参数:
|
||||
|
||||
- `locales`:必须参数 国际化语言
|
||||
- `formatOptions`:可选参数,数字格式化选项,默认采用浏览器数字格式
|
||||
- `useMemorize`:可选参数,记忆存储开关键,默认开启存储功能。
|
||||
|
||||
`numberFormat`传入两个参数:
|
||||
|
||||
- `value` :其是需要格式化的值,类型为`number`
|
||||
- `formatOptions`:是自定义的格式化类型,如果不传入,默认为浏览器格式
|
||||
|
||||
> 常用的格式化选项包括:
|
||||
> style:指定数字的格式样式。可以是 'decimal'(默认,普通数字格式)、'currency'(货币格式)或 'percent'(百分比格式)。
|
||||
> currency:用于货币格式的货币代码。
|
||||
> minimumFractionDigits:最小的小数位数。
|
||||
> maximumFractionDigits:最大的小数位数。
|
||||
> minimumIntegerDigits:最小的整数位数。
|
||||
> useGrouping:是否使用千分位分隔符,默认为 true。
|
||||
>
|
||||
> ```jsx
|
||||
> // 示例
|
||||
> const options = {style: 'currency', currency: 'USD'}
|
||||
> ```
|
||||
|
||||
**示例**
|
||||
|
||||
通过实例化`NumberFormatter`类来直接实现数字国际化,如
|
||||
|
||||
```jsx
|
||||
import NumberFormatter form 'inula-intl'
|
||||
|
||||
const number = new NumberFormatter('en',{style: 'currency', currency: 'USD'}, true)
|
||||
|
||||
number.numberFormat(1000);
|
||||
```
|
||||
|
||||
在上述示例中,通过`NumberFormatter`类实例化出`number`实例,可以对传入的参数`1000`进行时间格式化,按照给定的时间格式`{style: 'currency', currency: 'USD'}`。
|
||||
|
||||
## 组件式API
|
||||
|
||||
### IntlProvider
|
||||
|
||||
**功能介绍**
|
||||
|
||||
用于为应用程序提供国际化的格式化功能,管理程序中的语言文本信息和本地化资源信息。
|
||||
|
||||
**接口定义**
|
||||
|
||||
该组件用于创建国际化上下文,通常在使用时用此组件包裹应用根组件,以便完成国际化的功能。
|
||||
|
||||
`IntlProvider`是函数式组件,如下是该组件可设置的属性内容:
|
||||
|
||||
```tsx
|
||||
interface I18nProviderProps {
|
||||
i18n?: I18n;
|
||||
locale?: Locale;
|
||||
messages?: AllMessages;
|
||||
defaultLocale?: string;
|
||||
RenderOnLocaleChange?: boolean;
|
||||
children?: any;
|
||||
uesMemorize?: boolean;
|
||||
}
|
||||
|
||||
type AllMessages = Record<string, string> | Record<Locale, Messages>;
|
||||
type CompiledMessage = string | CompiledMessagePart[];
|
||||
type CompiledMessagePart = string | Array<string | Array<string | (string | undefined)> | Record<string, unknown>>;
|
||||
```
|
||||
|
||||
- `i18n`:实现国际化的统一接口类,在`IntlProvider`中可以传入i18n对象;
|
||||
- `locale`:本地语言
|
||||
- `messages`:国际化消息文本
|
||||
- `defaultLocale`:默认语言
|
||||
- `RenderOnLocaleChange`:语言渲染开关键
|
||||
- `children`:子组件
|
||||
- `uesMemorize`:记忆存储开关键
|
||||
|
||||
**示例**
|
||||
|
||||
通过ES6语法`import`,可导入`<IntlProvider>`组件
|
||||
|
||||
```tsx
|
||||
import {IntlProvider} from 'inula-intl';
|
||||
|
||||
const App = () => {
|
||||
const locale = 'en';
|
||||
const message = {
|
||||
'greeting': 'hello,world!'
|
||||
};
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messgae}>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上述示例中,`<IntlProvider>` 主要是传递国际化所需要的本地数据和配置信息给其他组件使用,因此一般使用是包裹着子组件。`<IntlProvider>{children}</IntlProvider>`。通过将`lcoale`以及`message`值传给子组件。
|
||||
|
||||
### RawIntlProvider
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`<RawIntlProvider>` 区别于`<IntlProvider>` 是未经封装`i18n`对象,,可以说`<RawIntlProvider>`是`<IntlProvider>`的底层实现,其是由`inula.createContext`生成,使用时需要结合钩子函数`createIntl`来使用。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```ts
|
||||
interface RawIntlProviderProps {
|
||||
value: I18n;
|
||||
children?: any;
|
||||
}
|
||||
```
|
||||
|
||||
- `value`:接受一个 `I18n `对象,该对象包含了国际化的配置和翻译信息。你可以使用[createIntl](###createIntl)函数来创建`I18n`对象,也可以通过`I18n`类实例化对象。
|
||||
- `children`:可选属性,表示要包裹在`<RawIntlProvider>`内部的子组件
|
||||
|
||||
**示例**
|
||||
|
||||
```tsx
|
||||
import {RawIntlProvider} from 'inula-intl';
|
||||
|
||||
const locale = 'en';
|
||||
const messages = {
|
||||
'greeting': 'hello,world!'
|
||||
};
|
||||
|
||||
const intl = createIntl({
|
||||
locale: locale,
|
||||
messages: messages
|
||||
},
|
||||
cache
|
||||
);
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<RawIntlProvider value={intl}>
|
||||
{children}
|
||||
</RawIntlProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上述代码首先用于`createIntl()`创建了一个名为intl的变量,其次使用 `<RawIntlProvider> `组件中使用 intl 变量作为 value 属性的值。`<RawIntlProvider>` 是 inula-intl 库提供的一个上下文组件,它将 intl 对象提供给其子组件,以便在整个组件树中可以访问到国际化相关的函数和数据。
|
||||
|
||||
### FormattedMessage
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`<FormattedMessage>` 组件主要用于格式化多语言信息,其可以处理日期、时间、数字格式化以及翻译字符串等任务。`<FormattedMessage>`组件的作用是将消息字符串从源语言翻译成目标语言,并根据需要进行格式化。该组件通常与`<IntlProvider>`组件一起使用,后者负责提供翻译功能和本地化信息。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
interface FormattedMessageProps {
|
||||
id: string;
|
||||
defaultMessage?: string | object;
|
||||
defaultValues?: Record<string, unknown>;
|
||||
values?: object;
|
||||
tagName?: string;
|
||||
children?(nodes: any[]): any;
|
||||
comment?: string;
|
||||
message?: string;
|
||||
context?: string;
|
||||
formatOptions?: FormatOptions;
|
||||
useMemorize?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
- `id`:必须参数,消息的唯一标识符,用于在消息资源文件中查找对应的翻译文本。;
|
||||
- `defaultValues`:可选参数,默认value值{}。
|
||||
- `defaultMessage`:可选参数,当找不到对应的翻译文本时,使用默认消息作为备选。
|
||||
- `values`:表示一个对象,包含要插入到消息中的动态值。
|
||||
- `tagName`:表示一个需要渲染的HTML标签名,如`div`或者`p`标签。如果不指定,则默认为`<>`。
|
||||
- `children`:可选参数,表示用于自定义渲染的消息内容
|
||||
- `comment`:可选参数,表示对消息选项的注释或说明。
|
||||
- `message`:可选参数,表示消息的内容。
|
||||
- `context`:可选参数,表示消息的上下文或背景信息。
|
||||
- `formatOptions`:可选参数,用来指定消息的格式选项,例如日期格式、货币符号等。
|
||||
- `useMemorize`:可选参数,表示是否使用记忆功能,即是否将消息缓存起来以供后续使用。
|
||||
|
||||
```tsx
|
||||
interface FormatOptions {
|
||||
dateTimeFormat?: Intl.DateTimeFormatOptions;
|
||||
numberFormat?: Intl.NumberFormatOptions;
|
||||
plurals?: Intl.PluralRulesOptions;
|
||||
}
|
||||
```
|
||||
|
||||
- `dateTimeFormat`:可选参数,它是一个可传递给`Intl.DateTimeFormat`构造函数的对象,用于指定日期和时间格式化的选项。可以使用这些选项来定义日期的显示格式、时区等。
|
||||
- `numberFormat`:可选参数,它是一个可传递给`Intl.NumberFormat`构造函数的对象,用于指定数字格式化的选项。可以使用这些选项来定义数字的显示格式、小数位数、货币符号等。
|
||||
- `plurals`:可选参数,它是一个可传递给`Intl.PluralRules`构造函数的对象,用于指定复数形式的选项。可以使用这些选项来根据不同的语言规则确定单数、复数等形式。
|
||||
|
||||
> ```tsx
|
||||
> const pluralRulesOptions = {
|
||||
> localeMatcher: 'best fit', // 匹配方式,可以是 'lookup' 或 'best fit'
|
||||
> type: 'cardinal', // 复数类型,可以是 'cardinal'(默认)或 'ordinal'
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> - `localeMatcher`:表示匹配方式,有两个选项:'lookup':从提供的 locale 列表中精确匹配合适的规则。'best fit':根据浏览器环境或提供的 locale 选择最佳匹配规则。
|
||||
> - `type`:表示复数类型,有两个选项:'cardinal':用于基数(一、两、三等)复数形 式。'ordinal':用于序数(第一、第二、第三等)复数形式。
|
||||
|
||||
**示例**
|
||||
|
||||
```tsx
|
||||
import { FormattedMessage } from 'inula-intl';
|
||||
|
||||
const App = () => {
|
||||
const name = 'John Doe';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="greeting"
|
||||
defaultMessage="Hello, {name}!"
|
||||
values={{ name }}
|
||||
/>
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
在上面的示例中,使用了`<FormattedMessage>`组件来显示一个国际化的问候语。为了使FormattedMessage正常工作,你需要在应用程序中正确配置并加载翻译文件,以便根据当前的语言环境提供正确的翻译文本。
|
||||
|
||||
## 函数式API
|
||||
|
||||
### useIntl()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`useIntl` hook 主要是函数式组件使用,其提供了一种简单的方式来获取当前应用程序的国际化相关信息。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```jsx
|
||||
const intl = useIntl();
|
||||
```
|
||||
|
||||
- `intl`:表示国际的相关信息。
|
||||
|
||||
**示例**
|
||||
|
||||
使用`useIntl` hook,可以主要访问以下功能
|
||||
|
||||
- 格式化消息(Format Messages): 使用`formatMessage`函数可以格式化消息字符串。你可以定义一系列消息,在不同语言环境下进行翻译,并且根据当前语言环境选择正确的翻译版本。例如:
|
||||
|
||||
```jsx
|
||||
const intl = useIntl();
|
||||
const message = intl.formatMessage({ id: 'greeting' });
|
||||
```
|
||||
|
||||
- 格式化日期和时间(Format Dates and Times): 你可以使用formatDate和formatTime函数来格式化日期和时间。这些函数会根据当前语言环境和所设置的格式选项返回相应的格式化结果。例如:
|
||||
|
||||
```jsx
|
||||
const intl = useIntl();
|
||||
const formattedDate = intl.formatDate(new Date(), { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
```
|
||||
|
||||
- 格式化数字(Format Numbers): 你可以使用formatNumber函数来格式化数字。这个函数可以根据当前语言环境和所设置的格式选项返回相应的格式化结果。例如:
|
||||
|
||||
```jsx
|
||||
const intl = useIntl();
|
||||
const formattedNumber = intl.formatNumber(1000, { style: 'currency', currency: 'USD' });
|
||||
```
|
||||
|
||||
### createIntl()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
createI18n hook函数,用于创建国际化i8n实例,以进行相关的国际化功能。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
const intl = createI18n(config: I18nProviderProps, cache?: I18nCache);
|
||||
|
||||
interface I18nProviderProps {
|
||||
i18n?: I18n;
|
||||
locale?: Locale;
|
||||
messages?: AllMessages;
|
||||
defaultLocale?: string;
|
||||
RenderOnLocaleChange?: boolean;
|
||||
children?: any;
|
||||
uesMemorize?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
`createIntl`接受两个参数:
|
||||
|
||||
- `config`:,其类型为I18nProviderProps,
|
||||
- `cache`: 自定义缓存。其返回一个国际对象的缓存对象,如果不传入,则默认为true,这是为了优化性能,其主要通过[creatIntlCache](### "creatIntlCache")创建。
|
||||
|
||||
**示例**
|
||||
|
||||
```tsx
|
||||
import {createIntl, createIntlCache, RawIntlProvider} from 'inula-intl'
|
||||
|
||||
const cache = createIntlCache()
|
||||
const props = {
|
||||
locale: 'en',
|
||||
messages: {
|
||||
greeting : "hello, world",
|
||||
}
|
||||
}
|
||||
|
||||
const Component = (props) => {
|
||||
// 受渲染时机影响,createIntl方式需控制时序,否则慢一拍
|
||||
const intl = createIntl({ ...props }, cache);
|
||||
const msg = intl.formatMessage({ id: 'greeting' });
|
||||
|
||||
return (
|
||||
<IntlProvider>
|
||||
<div className="card">
|
||||
<h2>createIntl-Demo</h2>
|
||||
<pre>{msg}</pre>
|
||||
</div>
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上述示例展示了如何使用`createIntl`来创建一个国际化对象,并且以这个对象来进行国际化功能操作,即调用`intl.formatMessage`。
|
||||
|
||||
### creatIntlCache()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`creatIntlCache` 接口是创建供全局使用的国际化缓存示例,其主要保证国际化的性能,是基于内存的缓存接口。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
cosnt cache = creatI18nCache(): I18nCache;
|
||||
|
||||
// I18n国际化缓存接口
|
||||
interface I18nCache {
|
||||
dateTimeFormat: Record<string, Intl.DateTimeFormat>;
|
||||
numberFormat: Record<string, Intl.NumberFormat>;
|
||||
plurals: Record<string, Intl.PluralRules>;
|
||||
messages: Record<string, IntlMessageFormat>;
|
||||
select: Record<string, object>;
|
||||
octothorpe: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
- `cache` : 表示国际化的存储。
|
||||
|
||||
**示例**
|
||||
|
||||
`creatIntlCache`一般作为 `createIntl(config, cache?)`的第二个参数使用
|
||||
|
||||
```jsx
|
||||
const cache = createIntlCache();
|
||||
const intl = createIntl(config, cache);
|
||||
```
|
||||
|
||||
在通过createIntl初始化国际化对象时,如需要缓存,则可以通过createIntlCache进行传入,当然,如果不传入也可以,inula-intl会默认为您开启存储模式,来存储你的国际化资源,以便优化程序性能。
|
||||
|
||||
### formatDate()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`formatDate`函数API主要用于国际化中的日期格式化,其可以根据传入参数解析出日期,formatDate底层是基于DateTimeFormatter实现。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```jsx
|
||||
formatDate(value: string | Date, formatOptions?: Intl.DateTimeFormatOptions): string
|
||||
```
|
||||
|
||||
- value:需要格式化的日期,可以是字符串也可以日期。
|
||||
- formatOptions:
|
||||
|
||||
**示例**
|
||||
|
||||
使用国际化对象intl实现
|
||||
|
||||
```tsx
|
||||
import {useIntl} form 'inula-intl'
|
||||
|
||||
const {intl} = useIntl();
|
||||
|
||||
function App() => {
|
||||
return(
|
||||
<IntlProvider>
|
||||
{intl.formatDate(new Date(), {year: 'numeric',month: 'numeric',day: 'numeric'})}
|
||||
</IntlProvider>;
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
> 如果在实例化`DateTimeFormatter`对象和调用内部函数`dateTimeFormat`时同时传入`formatOptions`参数,则函数传入的`formatOptions`参数的优先级高,提供灵活性和定制化选项。
|
||||
|
||||
### formatNumber()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`formatNumber`函数API是`inula-intl`提供的数字格式化功能接口,主要用于国际化中的数字格式化,其可以根据传入参数解析出数字,及货币等格式。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
formatNumber(value: number, formatOption?: Intl.NumberFormatOptions): string;
|
||||
```
|
||||
|
||||
**示例**
|
||||
|
||||
使用国际化对象intl实现
|
||||
|
||||
```jsx
|
||||
import {useIntl} form 'inula-intl'
|
||||
|
||||
const {intl} = useIntl();
|
||||
|
||||
function App() => {
|
||||
return(
|
||||
<IntlProvider>
|
||||
{intl.numberFormat(1000, {style: 'currency', currency: 'USD'})}
|
||||
</IntlProvider>;
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
> 如果在实例化`NumberFormatter`对象和调用内部函数`dateTimeFormat`时同时传入`formatOptions`参数,则函数传入的`formatOptions`参数的优先级高,提供灵活性和定制化选项。
|
||||
|
||||
### formatMessage()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`formatMessage` 是`inula-intl`提供的消息格式化功能接口,其主要针对较复杂的信息在运行时进行格式化。
|
||||
|
||||
> 简单消息和复杂消息的区别是一个不过多涉及复杂的语境或者文化差异,另一种是需要涉及更多文化和语言背景。具体在其余参考中可详细了解。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```tsx
|
||||
formatMessage(id: MessageDescriptor | string, values?: Object | undefined, { message, context, formatOptions, useMemorize }?: MessageOptions): string;
|
||||
|
||||
interface MessageDescriptor extends MessageOptions {
|
||||
id: string;
|
||||
defaultMessage?: string | object;
|
||||
defaultValues?: Record<string, unknown>;
|
||||
}
|
||||
interface MessageOptions {
|
||||
comment?: string;
|
||||
message?: string;
|
||||
context?: string;
|
||||
formatOptions?: FormatOptions;
|
||||
useMemorize?: boolean;
|
||||
}
|
||||
|
||||
interface FormatOptions {
|
||||
dateTimeFormat?: Intl.DateTimeFormatOptions;
|
||||
numberFormat?: Intl.NumberFormatOptions;
|
||||
plurals?: Intl.PluralRulesOptions;
|
||||
}
|
||||
```
|
||||
|
||||
- `formatMessage`接口传递了三个参数:
|
||||
- `id`: id是唯一标识符,其可以是`MessageDescriptor`对象,也可以是string类型
|
||||
- `value`:复杂消息中,格式化需要的变量值
|
||||
- `MessageOptions`:消息格式化选项,其包含了消息的描述,默认消息,默认值,记忆存储开关键等信息。
|
||||
|
||||
**示例**
|
||||
|
||||
`formatMessage`是一个类内部函数,其可以通过实例国际化对象来使用
|
||||
|
||||
```jsx
|
||||
const messages = { id: "Je m'appelle {name}"};
|
||||
|
||||
intl.formatMessage("id", { name: 'Fred' });
|
||||
```
|
||||
|
||||
**参考**
|
||||
|
||||
复杂消息和简单消息的示例
|
||||
|
||||
简单消息不需要过多的文化差异以及复杂的语境
|
||||
|
||||
```jsx
|
||||
Hey {name}!
|
||||
```
|
||||
|
||||
复杂消息状态下,根据复杂语言环境以及文化,如plural复数规则均有所不同。下述代码示例为英语时,其复数规则`value === 1 ? 'one' : 'other';`
|
||||
|
||||
```jsx
|
||||
// 复杂消息
|
||||
{value, plural, one {{value} Book} other {# Books}};
|
||||
{value, selectordinal, one {#st Book} two {#nd Book}};
|
||||
```
|
||||
|
||||
### injectIntl()
|
||||
|
||||
**功能介绍**
|
||||
|
||||
`injectIntl`用于实现国际化的高阶组件(Higher-Order Component,HOC),将国际化功能注入到组件中,使组件能够使用国际化功能,injectintl高阶组件主要使用于类组件,用于将intl对象注入到类组件的props中
|
||||
|
||||
当使用`injectIntl`将一个组件包装起来时,被包装的组件会通过`props`接收一个提供了国际化相关函数的对象intl,。它包含了格式化消息、日期、时间等的方法,还可以获取当前语言环境、可用的语言列表等信息。
|
||||
|
||||
**接口定义**
|
||||
|
||||
```jsx
|
||||
const WrappedComponent = injectIntl(Component, options);
|
||||
```
|
||||
|
||||
- `Component`:表示需要国际化的组件
|
||||
- `options`:可选参数,表示国际化的选项。
|
||||
|
||||
**代码示例**
|
||||
|
||||
```tsx
|
||||
import {injectIntl} from 'inula-intl';
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props as any;
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>injectIntl-Demo</h2>
|
||||
<pre>{intl.formatMessage({ id: 'greeting' })}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default injectIntl(App);
|
||||
```
|
||||
|
||||
上述示例采用`injectIntl`高阶组件将国际对象注入,从注入props中获取到国际化对象,并用获取的国际化对象`intl`格式化一个消息。
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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: {
|
||||
browsers: ['> 1%', 'last 2 versions', 'not ie <= 8'],
|
||||
node: 'current',
|
||||
},
|
||||
useBuiltIns: 'usage',
|
||||
corejs: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
[
|
||||
"@babel/preset-react",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"importSource": "openinula"
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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, { useState } from 'openinula';
|
||||
import { IntlProvider } from "../index";
|
||||
import zh from "./locale/zh";
|
||||
import en from "./locale/en";
|
||||
import Example1 from "./components/Example1";
|
||||
import Example2 from "./components/Example2";
|
||||
import Example3 from "./components/Example3";
|
||||
import Example4 from "./components/Example4";
|
||||
import Example5 from "./components/Example5";
|
||||
import Example6 from "./components/Example6";
|
||||
|
||||
const App = () => {
|
||||
const [locale, setLocale] = useState('zh');
|
||||
const handleChange = () => {
|
||||
locale === 'zh' ? setLocale('en') : setLocale('zh');
|
||||
};
|
||||
const message = locale === 'zh' ? zh : en
|
||||
|
||||
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={locale === 'zh' ? zh : en}>
|
||||
<header>Inula-Intl API Test Demo</header>
|
||||
|
||||
<div className='container'>
|
||||
<Example1/>
|
||||
<Example2/>
|
||||
<Example3/>
|
||||
</div>
|
||||
<div className='container'>
|
||||
<Example4 locale={locale} messages={message}/>
|
||||
<Example5/>
|
||||
<Example6 locale={{ locale }} messages={message}/>
|
||||
</div>
|
||||
<div className='button'>
|
||||
<button onClick={handleChange}>切换语言</button>
|
||||
</div>
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { useIntl } from "../../index";
|
||||
|
||||
const Example1 = () => {
|
||||
const { i18n } = useIntl();
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>useIntl方式测试Demo</h2>
|
||||
<pre>{i18n.formatMessage({ id: 'text1' })}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Example1;
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from "../../index";
|
||||
|
||||
const Example2= () => {
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>FormattedMessage方式测试Demo</h2>
|
||||
<pre>
|
||||
<FormattedMessage id='text2'/>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Example2;
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from "../../index";
|
||||
|
||||
const Example3 = (props) => {
|
||||
const { locale, setLocale } = props;
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>FormattedMessage方式测试Demo</h2>
|
||||
<pre>
|
||||
<button className="testButton" onClick={() => {
|
||||
setLocale(locale === 'zh' ? 'en' : 'zh')
|
||||
}}>
|
||||
<FormattedMessage id={'button'}/>
|
||||
</button>
|
||||
<br/>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Example3;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { createIntl } from "../../index";
|
||||
|
||||
const Example4 = (props) => {
|
||||
// 受渲染时机影响,createIntl方式需控制时序,否则慢一拍
|
||||
const intl = createIntl({ ...props });
|
||||
const msg = intl.formatMessage({ id: 'text3' });
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>createIntl方式测试Demo</h2>
|
||||
<pre>{msg}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Example4;
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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, { Component } from 'openinula';
|
||||
import { injectIntl } from '../../index';
|
||||
|
||||
class Example5 extends Component<any, any, any> {
|
||||
public constructor(props: any, context) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props as any;
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>injectIntl方式测试Demo</h2>
|
||||
<pre>{intl.formatMessage({ id: 'text4' })}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(Example5);
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { createIntl, createIntlCache, RawIntlProvider } from "../../index";
|
||||
import Example6Child from "./Example6Child";
|
||||
|
||||
const Example6 = (props: any) => {
|
||||
|
||||
const { locale, messages } = props;
|
||||
|
||||
const cache = createIntlCache();
|
||||
let i18n = createIntl(
|
||||
{ locale: locale, messages: messages },
|
||||
cache
|
||||
);
|
||||
|
||||
return (
|
||||
<RawIntlProvider value={i18n}>
|
||||
<Example6Child/>
|
||||
</RawIntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default Example6;
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { useIntl } from "../../index";
|
||||
|
||||
const Example6Child = (props: any) => {
|
||||
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>RawIntlProvider方式测试Demo</h2>
|
||||
<pre>{formatMessage({ id: 'text4' })}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Example6Child;
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Inula-Intl API Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f8f8f8;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
color: #2c3e50;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px #aaa;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 0 15px #aaa;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.card pre {
|
||||
background-color: #f0f0f0;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.testButton {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 Inula from 'openinula';
|
||||
import App from './App'
|
||||
|
||||
function render() {
|
||||
Inula.render(
|
||||
<>
|
||||
<App/>
|
||||
</>,
|
||||
document.querySelector('#root') as any
|
||||
)
|
||||
}
|
||||
render();
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default {
|
||||
button: 'Welcome to the Inula-Intl component!',
|
||||
text1: 'Welcome to the Inula-Intl component!',
|
||||
text2: 'Welcome to the Inula-Intl component!',
|
||||
text3: 'Welcome to the Inula-Intl component!',
|
||||
text4: 'Welcome to the Inula-Intl component!',
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export default {
|
||||
button: '欢迎使用国际化组件!',
|
||||
text1: '欢迎使用国际化组件!',
|
||||
text2: '欢迎使用国际化组件!',
|
||||
text3: '欢迎使用国际化组件!',
|
||||
text4: '欢迎使用国际化组件!',
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 DateTimeFormatter from './src/format/fomatters/DateTimeFormatter';
|
||||
import NumberFormatter from './src/format/fomatters/NumberFormatter';
|
||||
import I18n from './src/core/I18n';
|
||||
import createI18nCache from './src/format/cache/cache';
|
||||
import FormattedMessage from './src/core/components/FormattedMessage';
|
||||
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';
|
||||
// 函数API
|
||||
export {
|
||||
I18n,
|
||||
createI18nCache as createIntlCache,
|
||||
createI18n as createIntl,
|
||||
DateTimeFormatter,
|
||||
NumberFormatter,
|
||||
useI18n as useIntl,
|
||||
};
|
||||
|
||||
// 组件
|
||||
export {
|
||||
FormattedMessage,
|
||||
I18nContext,
|
||||
I18nProvider as IntlProvider,
|
||||
injectIntl as injectIntl,
|
||||
InjectProvider as RawIntlProvider,
|
||||
};
|
||||
|
||||
export default {
|
||||
I18n,
|
||||
createIntlCache: createI18nCache,
|
||||
createIntl: createI18n,
|
||||
DateTimeFormatter,
|
||||
NumberFormatter,
|
||||
useIntl: useI18n,
|
||||
FormattedMessage,
|
||||
I18nContext,
|
||||
IntlProvider: I18nProvider,
|
||||
injectIntl: injectIntl,
|
||||
RawIntlProvider: InjectProvider,
|
||||
}
|
||||
|
||||
// 用于定义文本
|
||||
export function defineMessages<K extends keyof any, T = MessageDescriptor, U = Record<K, T>>(msgs: U): U {
|
||||
return msgs;
|
||||
}
|
||||
|
||||
export function defineMessage<T>(msg: T): T {
|
||||
return msg;
|
||||
}
|
||||
|
||||
export interface InjectedIntlProps {
|
||||
intl: InjectedIntl;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue