├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
└── pre-commit
├── .npmignore
├── .prettierrc
├── CONTRIBUTING(zh).md
├── CONTRIBUTING.md
├── LICENSE
├── README(zh).md
├── README.md
├── __test__
├── array-utils.test.js
├── date-utils.test.js
├── object-utils.test.js
├── performance-utils.test.js
├── priority-queue.test.js
├── reg-rules.test.js
├── string-utils.test.js
└── tree-utils.test.js
├── package.json
├── resource
└── logo.png
├── rollup.config.js
├── src
├── array-utils.ts
├── date-utils.ts
├── index.ts
├── object-utils.ts
├── performance-utils.ts
├── priority-queue.ts
├── reg-rules.ts
├── string-utils.ts
└── tree-utils.ts
├── tsconfig.json
├── typedoc.json
└── utils
├── find-export-files.js
└── get-library-name.js
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: MainDeploy
2 | on:
3 | push:
4 | branches:
5 | - main
6 | - dev
7 | pull_request:
8 | branches:
9 | - main
10 | - dev
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | with:
18 | fetch-depth: 0
19 | - name: Build And Test Project
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 18.x
23 | - run: npm i
24 | - run: npm run build
25 | - run: npm run test
26 |
27 | - if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && success() }}
28 | name: Move File
29 | run: mv ./dist/* ./
30 |
31 | - if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && success() }}
32 | name: Publish NPM
33 | uses: JS-DevTools/npm-publish@v3
34 | with:
35 | token: ${{ secrets.NPM_TOKEN }}
36 |
37 | - if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && success() }}
38 | name: Generate Doc
39 | uses: actions/setup-node@v3
40 | with:
41 | node-version: 18.x
42 | - run: npm run doc
43 |
44 | - if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && success() }}
45 | name: Publish HTML to GitHub Pages
46 | uses: peaceiris/actions-gh-pages@v3
47 | with:
48 | github_token: ${{ secrets.GIT_TOKEN }}
49 | publish_dir: ./docs
50 | enable_jekyll: false
51 |
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | package-lock.json
4 | docs
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npm run build
2 | npm run test
3 | npm run format
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /*
2 | !cjs/
3 | !umd/
4 | !/*.js
5 | !/*.ts
6 | *.config.js
7 | !README.md
8 | !LICENSE
9 | !package.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 150,
3 | "tabWidth": 2,
4 | "useTabs": true,
5 | "semi": true,
6 | "singleQuote": true,
7 | "quoteProps": "as-needed",
8 | "jsxSingleQuote": false,
9 | "trailingComma": "es5",
10 | "bracketSpacing": true,
11 | "jsxBracketSameLine": false,
12 | "bracketSameLine": false,
13 | "arrowParens": "always",
14 | "rangeStart": 0,
15 | "requirePragma": false,
16 | "insertPragma": false,
17 | "proseWrap": "preserve",
18 | "htmlWhitespaceSensitivity": "css",
19 | "vueIndentScriptAndStyle": false,
20 | "endOfLine": "lf"
21 | }
22 |
--------------------------------------------------------------------------------
/CONTRIBUTING(zh).md:
--------------------------------------------------------------------------------
1 | # 贡献指南
2 |
3 | ## 安装和设置
4 |
5 | 在开始贡献之前,请确保您的本地环境已经设置好并满足以下要求:
6 |
7 | - node版本14.x以上
8 | - 所需依赖项和库的版本正确安装
9 |
10 | ## 贡献流程
11 |
12 | 1. Fork 本仓库的main分支到您的 GitHub 账号。
13 | 3. 创建一个新的分支,用于您的贡献,分支命名建议使用描述性的名称。
14 | 4. 在分支上进行您的修改和工作。
15 | 4. 如果新增内容,需要在src/index.ts文件中导入您新增的内容,并且在\__test__目录下编写您新增内容的测试用例
16 | 5. 提交您的更改。请确保您的提交消息清晰、简洁,并且符合代码提交指南(参考下文)。
17 | 6. 向原仓库的main分支提交 Pull Request。
18 | 7. 等待代码审核和反馈。根据需要进行修改和调整。
19 | 8. 一旦您的贡献通过审核,将被合并到main分支。
20 |
21 | ## 版本控制和分支管理
22 |
23 | - main分支用于稳定的发布版本。
24 | - gh-pages分支用于项目文档的发布。
25 |
26 | ## 提交要求
27 |
28 | - 请遵循项目的代码风格指南和命名约定。
29 | - 请遵循项目的注释规范,以便其他人理解和维护您的代码。
30 | - 确保您的更改不会影响现有的功能,并通过相关测试用例。
31 |
32 | ## 社区行为准则
33 |
34 | 我们期待所有参与者能够遵循我们的社区行为准则。请确保在所有交流和互动中保持友善、尊重和合作的态度。对于任何形式的不当行为,请及时报告给项目维护者。
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | ## Installation and Setup
4 |
5 | Before you start contributing, please make sure your local environment is properly set up and meets the following requirements:
6 |
7 | - Node version 14.x or above
8 | - Required dependencies and libraries installed with the correct versions
9 |
10 | ## Contribution Process
11 |
12 | 1. Fork the main branch of this repository to your GitHub account.
13 | 2. Create a new branch for your contributions. It is recommended to use a descriptive name for the branch.
14 | 3. Make your modifications and work on the branch.
15 | 4. If you add new content, import your additions in the src/index.ts file and write test cases for your additions in the _*test*_ directory.
16 | 5. Commit your changes. Make sure your commit message is clear, concise, and follows the code submission guidelines (refer to the guidelines below).
17 | 6. Submit a Pull Request to the main branch of the original repository.
18 | 7. Wait for code review and feedback. Make any necessary modifications and adjustments as required.
19 | 8. Once your contributions are approved, they will be merged into the main branch.
20 |
21 | ## Version Control and Branch Management
22 |
23 | - The main branch is used for stable releases.
24 | - The gh-pages branch is used for publishing project documentation.
25 |
26 | ## Submission Requirements
27 |
28 | - Please follow the project's code style guide and naming conventions.
29 | - Please follow the project's commenting guidelines to ensure that others can understand and maintain your code.
30 | - Ensure that your changes do not affect existing functionality and pass the relevant test cases.
31 |
32 | ## Community Code of Conduct
33 |
34 | We expect all participants to adhere to our community code of conduct. Please maintain a friendly, respectful, and collaborative attitude in all communication and interactions. Report any instances of inappropriate behavior promptly to the project maintainers.
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Wang JunLiang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README(zh).md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | 被别人需要是一件很幸福的事情,因此有了BootsJS这个强大的JS工具库。
4 |
5 | [EN](https://github.com/JunLiangWangX/BootsJS/blob/main/README.md) | [中文](https://github.com/JunLiangWangX/BootsJS/blob/main/README(zh).md)
6 |
7 | ## 安装
8 |
9 | **npm:**
10 |
11 | ```
12 | npm install boots-js
13 | ```
14 |
15 | **yarn:**
16 |
17 | ```
18 | yarn add boots-js
19 | ```
20 |
21 | **cdn:**
22 |
23 | ```
24 | https://unpkg.com/boots-js@latest/umd/index.js
25 | ```
26 |
27 | ## 使用
28 |
29 | ### CommonJS
30 |
31 | ```javascript
32 | // 全局导入
33 | const BootsJS=require('boots-js/cjs');
34 | BootsJS.ObjectUtils.type(123);
35 | // 按需导入
36 | const ObjectUtils = require('boots-js/cjs/object-utils');
37 | ObjectUtils.type(123);
38 | ```
39 |
40 | ### Browser
41 |
42 | ```html
43 | // 全局导入
44 |
45 |
46 | // 按需导入
47 |
48 |
49 | ```
50 |
51 | ### ES6 Module
52 |
53 | ```js
54 | // 全局导入
55 | import BootsJS from 'boots-js'
56 | BootsJS.ObjectUtils.type(123);
57 | // 按需导入
58 | import ObjectUtils from 'boots-js/object-utils'
59 | ObjectUtils.type(123);
60 | ```
61 |
62 | ## 所有特性
63 | [点击查看](https://junliangwangx.github.io/BootsJS/)
64 |
65 | ## 参与贡献
66 | [点击查看](https://github.com/JunLiangWangX/BootsJS/blob/main/CONTRIBUTING(zh).md)
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | Being needed by others is a very happy thing, so there is BootsJS, a powerful JS tool library.
4 |
5 | [EN](https://github.com/JunLiangWangX/BootsJS/blob/main/README.md) | [中文](https://github.com/JunLiangWangX/BootsJS/blob/main/README(zh).md)
6 |
7 | ## Installation
8 |
9 | **npm:**
10 |
11 | ```
12 | npm install boots-js
13 | ```
14 |
15 | **yarn:**
16 |
17 | ```
18 | yarn add boots-js
19 | ```
20 |
21 | **cdn:**
22 |
23 | ```
24 | https://unpkg.com/boots-js@latest/umd/index.js
25 | ```
26 |
27 | ## Usage
28 |
29 | ### CommonJS
30 |
31 | ```javascript
32 | // Global Import
33 | const BootsJS=require('boots-js/cjs');
34 | BootsJS.ObjectUtils.type(123);
35 | // Import on Demand
36 | const ObjectUtils = require('boots-js/cjs/object-utils');
37 | ObjectUtils.type(123);
38 | ```
39 |
40 | ### Browser
41 |
42 | ```html
43 | // Global Import
44 |
45 |
46 | // Import on Demand
47 |
48 |
49 | ```
50 |
51 | ### ES6 Module
52 |
53 | ```js
54 | // Global Import
55 | import BootsJS from 'boots-js'
56 | BootsJS.ObjectUtils.type(123);
57 | // Import on Demand
58 | import ObjectUtils from 'boots-js/object-utils'
59 | ObjectUtils.type(123);
60 | ```
61 |
62 | ## All Features
63 |
64 | [Click to view](https://junliangwangx.github.io/BootsJS/)
65 |
66 | ## Contribute
67 |
68 | [Click to view](https://github.com/JunLiangWangX/BootsJS/blob/main/CONTRIBUTING.md)
--------------------------------------------------------------------------------
/__test__/array-utils.test.js:
--------------------------------------------------------------------------------
1 | const ArrayUtils = require('../dist/cjs/array-utils');
2 | const BootsJS = require('../dist/cjs');
3 |
4 |
5 | test('test ArrayUtils class', () => {
6 | const test1={a:'1'},test2={a:'1'},
7 | arr1=[test1,test2,test1],
8 | arr2=[1,2,3,1,4]
9 | expect(ArrayUtils.removeDuplicates(arr1)).toStrictEqual([test1,test2]);
10 | expect(ArrayUtils.removeDuplicates(arr1,true)).toStrictEqual([test1]);
11 | expect(BootsJS.ArrayUtils.removeDuplicates(arr2)).toStrictEqual([1,2,3,4]);
12 | })
--------------------------------------------------------------------------------
/__test__/date-utils.test.js:
--------------------------------------------------------------------------------
1 | const DateUtils = require('../dist/cjs/date-utils');
2 | const BootsJS = require('../dist/cjs');
3 |
4 | test('test DateUtils class', () => {
5 | expect(DateUtils.dateFormater('Mon Feb 26 2024', 'YYYY-MM-DD')).toBe('2024-02-26');
6 | expect(DateUtils.dateFormater('2024/2/26', 'YYYY-MM-DD')).toBe('2024-02-26');
7 | //expect(DateUtils.dateFormater(1708917102083, 'YYYY-MM-DD HH:mm:ss')).toBe('2024-02-26 11:11:42');
8 | expect(DateUtils.dateFormater('2024/2/26 11:11:42', 'YYYY/MM/DD/HH/mm/ss')).toBe('2024/02/26/11/11/42');
9 | //expect(DateUtils.dateFormater(new Date(1708917102083), 'YYYY/MM/DD/HH/mm/ss')).toBe('2024/02/26/11/11/42');
10 | expect(DateUtils.dateFormater('Mon Feb 26 2024', 'YYYY-MM-DD')).toBe('2024-02-26');
11 | expect(DateUtils.dateFormater('2024/2/26', 'YYYY-MM-DD')).toBe('2024-02-26');
12 | //expect(DateUtils.dateFormater(1708917102083, 'YYYY-MM-DD HH:mm:ss')).toBe('2024-02-26 11:11:42');
13 | expect(DateUtils.dateFormater('2024/2/26 11:11:42', 'YYYY/MM/DD/HH/mm/ss')).toBe('2024/02/26/11/11/42');
14 | //expect(DateUtils.dateFormater(new Date(1708917102083), 'YYYY/MM/DD/HH/mm/ss')).toBe('2024/02/26/11/11/42');
15 |
16 | expect(DateUtils.isLeapYear(2040)).toBe(true);
17 | expect(DateUtils.isLeapYear(2000)).toBe(true);
18 | expect(DateUtils.isLeapYear(2020)).toBe(true);
19 | expect(DateUtils.isLeapYear(2024)).toBe(true);
20 | expect(DateUtils.isLeapYear(2019)).toBe(false);
21 | expect(DateUtils.isLeapYear(2025)).toBe(false);
22 | expect(DateUtils.isLeapYear(2027)).toBe(false);
23 | expect(DateUtils.isLeapYear(1997)).toBe(false);
24 |
25 | expect(DateUtils.getDaysInMonth(2024, 2)).toBe(29);
26 | expect(DateUtils.getDaysInMonth(2024, 7)).toBe(31);
27 | expect(DateUtils.getDaysInMonth(2025, 2)).toBe(28);
28 | expect(DateUtils.getDaysInMonth(2025, 8)).toBe(31);
29 |
30 | expect(DateUtils.getDateDiff('2024/1/26', '2025/1/26', DateUtils.DateUnitEnum.day)).toBe(366);
31 | expect(DateUtils.getDateDiff('2024/1/26', '2025/1/26', DateUtils.DateUnitEnum.month)).toBe(12);
32 | expect(DateUtils.getDateDiff('2025/6/19', '2025/9/18', DateUtils.DateUnitEnum.month)).toBe(2);
33 | expect(DateUtils.getDateDiff('2025/6/19', '2025/9/18', DateUtils.DateUnitEnum.year)).toBe(0);
34 | expect(DateUtils.getDateDiff('2025/6/19', '2025/9/18', DateUtils.DateUnitEnum.all)).toEqual({
35 | years: 0,
36 | months: 2,
37 | days: 30,
38 | hours: 0,
39 | minutes: 0,
40 | seconds: 0,
41 | });
42 | expect(DateUtils.getDateDiff('2025/8/18 23:00', '2025/8/19 21:00', DateUtils.DateUnitEnum.hour)).toBe(22);
43 | expect(DateUtils.getDateDiff('2025/8/18 23:19', '2025/8/19 21:00', DateUtils.DateUnitEnum.hour)).toBe(21);
44 | expect(DateUtils.getDateDiff('2025/8/18 23:19', '2025/8/19 21:00', DateUtils.DateUnitEnum.minute)).toBe(1301);
45 | expect(DateUtils.getDateDiff('2024/3/25', '2030/2/1', DateUtils.DateUnitEnum.year)).toBe(5);
46 | expect(DateUtils.getDateDiff('2024/2/2', '2030/1/1', DateUtils.DateUnitEnum.month)).toBe(70);
47 | expect(DateUtils.getDateDiff('2024/2/12', '2030/3/10', DateUtils.DateUnitEnum.day)).toBe(2218);
48 | expect(DateUtils.getDateDiff('2024/2/12', '2030/3/10', DateUtils.DateUnitEnum.all)).toEqual({
49 | years: 6,
50 | months: 0,
51 | days: 26,
52 | hours: 0,
53 | minutes: 0,
54 | seconds: 0,
55 | });
56 |
57 | expect(BootsJS.DateUtils.dateCalculator('2024/2/12', { years: 1 }).toISOString()).toBe(new Date('2025/2/12').toISOString());
58 |
59 | expect(BootsJS.DateUtils.convertTimeZone(
60 | 1711611931754,
61 | BootsJS.DateUtils.TimeZoneOffsetEnum['UTC+08:00'],
62 | BootsJS.DateUtils.TimeZoneOffsetEnum['UTC+09:00']).valueOf()).toBe(1711615531754);
63 | expect(BootsJS.DateUtils.convertTimeZone(
64 | 1711611931754,
65 | BootsJS.DateUtils.TimeZoneOffsetEnum['UTC+08:00'],
66 | BootsJS.DateUtils.TimeZoneOffsetEnum['UTC-06:00']).valueOf()).toBe(1711561531754);
67 | expect(BootsJS.DateUtils.convertTimeZone(
68 | 1711611931754,
69 | BootsJS.DateUtils.TimeZoneOffsetEnum['UTC+08:00'],
70 | BootsJS. DateUtils.TimeZoneOffsetEnum['UTC±00:00']).valueOf()).toBe(1711583131754);
71 |
72 |
73 |
74 | });
75 |
--------------------------------------------------------------------------------
/__test__/object-utils.test.js:
--------------------------------------------------------------------------------
1 | const ObjectUtils = require('../dist/cjs/object-utils');
2 | const BootsJS = require('../dist/cjs/index');
3 |
4 |
5 | test('test ObjectUtils class', async () => {
6 |
7 | expect(BootsJS.ObjectUtils.type(123)).toBe('Number');
8 | expect(BootsJS.ObjectUtils.type(true)).toBe('Boolean');
9 | expect(BootsJS.ObjectUtils.type('String')).toBe('String');
10 | expect(BootsJS.ObjectUtils.type(new Map())).toBe('Map');
11 | expect(BootsJS.ObjectUtils.type(new Array())).toBe('Array');
12 | expect(BootsJS.ObjectUtils.type(new ArrayBuffer())).toBe('ArrayBuffer');
13 |
14 |
15 | const obj1={Number:1},obj2={Boolean:2},obj3={obj:obj1,String:'123'},
16 | obj4={obj1:obj3,obj2:obj2,Null:null,Undefined:undefined,Symbol:Symbol('X'),BigInt:BigInt(123)}
17 | map=new Map(),fun=function(){console.info(test)}
18 | map.set(1,obj1)
19 | map.set(obj1,obj2)
20 | map.set('test',obj2)
21 | const testObj={
22 | Int8Array:new Int8Array([1,2,3,4,5]),
23 | Int16Array:new Int16Array([11,22,33,44,55]),
24 | Int32Array:new Int32Array([111,222,333,444,555]),
25 | Uint8Array:new Uint8Array([1111,2222,3333,4444,5555]),
26 | Uint16Array:new Uint16Array([11111,22222,3323,4444,4455]),
27 | Uint32Array:new Uint32Array([21311,2231,3123,4434,51215]),
28 | Float32Array:new Float32Array([11,22,33,44,55]),
29 | Float64Array:new Float64Array([11,22,33,44,55]),
30 | BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
31 | BigUint64Array:new BigUint64Array([BigInt(123),BigInt(123),BigInt(123)]),
32 | Date:new Date(),
33 | RegExp:/1234/,
34 | Array:new Array(...[obj1,obj2,obj3,obj4]),
35 | Set:new Set([obj1,obj2,obj3,obj4]),
36 | Map:map,
37 | Object:obj4,
38 | ArrayBuffer:new ArrayBuffer(10),
39 | DataView:new DataView(new ArrayBuffer(10)),
40 | Function:fun
41 | }
42 | testObj.Object.testObj=testObj
43 | const copyObj=testObj
44 | let deepCopyObj=ObjectUtils.deepClone(testObj)
45 |
46 | expect(ObjectUtils.argToStrKey(deepCopyObj)===ObjectUtils.argToStrKey(testObj)).toBe(true);
47 | expect(copyObj.Int8Array===testObj.Int8Array).toBe(true);
48 | expect(deepCopyObj.Int8Array===testObj.Int8Array).toBe(false);
49 | expect(copyObj.Date===testObj.Date).toBe(true);
50 | expect(deepCopyObj.Date===testObj.Date).toBe(false);
51 | expect(copyObj.RegExp===testObj.RegExp).toBe(true);
52 | expect(deepCopyObj.RegExp===testObj.RegExp).toBe(false);
53 | expect(copyObj.Array===testObj.Array).toBe(true);
54 | expect(deepCopyObj.Array===testObj.Array).toBe(false);
55 | expect(copyObj.Array[3].obj1===testObj.Array[3].obj1).toBe(true);
56 | expect(deepCopyObj.Array[3].obj1===testObj.Array[3].obj1).toBe(false);
57 | expect(copyObj.Set===testObj.Set).toBe(true);
58 | expect(deepCopyObj.Set===testObj.Set).toBe(false);
59 | expect(copyObj.Map===testObj.Map).toBe(true);
60 | expect(deepCopyObj.Map===testObj.Map).toBe(false);
61 | expect(copyObj.Map.get(1)===testObj.Map.get(1)).toBe(true);
62 | expect(deepCopyObj.Map.get(1)===testObj.Map.get(1)).toBe(false);
63 | expect(copyObj.Object.obj1.obj===testObj.Object.obj1.obj).toBe(true);
64 | expect(deepCopyObj.Object.obj1.obj===testObj.Object.obj1.obj).toBe(false);
65 | expect(copyObj.ArrayBuffer===testObj.ArrayBuffer).toBe(true);
66 | expect(deepCopyObj.ArrayBuffer===testObj.ArrayBuffer).toBe(false);
67 | expect(copyObj.DataView===testObj.DataView).toBe(true);
68 | expect(deepCopyObj.DataView===testObj.DataView).toBe(false);
69 | expect(copyObj.Function===testObj.Function).toBe(true);
70 | expect(deepCopyObj.Function===testObj.Function).toBe(true);
71 |
72 | const map2=new Map()
73 | map2.set('test',obj2)
74 | map2.set(obj1,obj2)
75 | map2.set(1,obj1)
76 | const date=new Date()
77 | const testObj2={
78 | BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
79 | Float64Array:new Float64Array([11,22,33,44,55]),
80 | Uint32Array:new Uint32Array([21311,2231,3123,4434,51215]),
81 | Uint16Array:new Uint16Array([11111,22222,3323,4444,4455]),
82 | Int16Array:new Int16Array([11,22,33,44,55]),
83 | Int8Array:new Int8Array([1,2,3,4,5]),
84 | Uint8Array:new Uint8Array([1,2,3,4,5]),
85 | Int32Array:new Int32Array([111,222,333,444,555]),
86 | Float32Array:new Float32Array([11,22,33,44,55]),
87 | BigUint64Array:new BigUint64Array([BigInt(123),BigInt(123),BigInt(123)]),
88 | RegExp:/1234/,
89 | Array:new Array(...[obj1,obj2,obj3,obj4]),
90 | Set:new Set([obj1,obj2,obj3,obj4]),
91 | Object:obj4,
92 | Map:map2,
93 | Date:date,
94 | ArrayBuffer:new ArrayBuffer(10),
95 | DataView:new DataView(new ArrayBuffer(10)),
96 | }
97 | const testObj3={
98 | Int8Array:new Int8Array([1,2,3,4,5]),
99 | RegExp:/1234/,
100 | Uint32Array:new Uint32Array([21311,2231,3123,4434,51215]),
101 | Int16Array:new Int16Array([11,22,33,44,55]),
102 | Array:new Array(...[obj1,obj2,obj3,obj4]),
103 | Uint16Array:new Uint16Array([11111,22222,3323,4444,4455]),
104 | Uint8Array:new Uint8Array([1,2,3,4,5]),
105 | Set:new Set([obj4,obj3,obj2,obj1]),
106 | Int32Array:new Int32Array([111,222,333,444,555]),
107 | Object:obj4,
108 | Float64Array:new Float64Array([11,22,33,44,55]),
109 | Map:map,
110 | BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
111 | Date:date,
112 | ArrayBuffer:new ArrayBuffer(10),
113 | BigUint64Array:new BigUint64Array([BigInt(123),BigInt(123),BigInt(123)]),
114 | DataView:new DataView(new ArrayBuffer(10)),
115 | Float32Array:new Float32Array([11,22,33,44,55]),
116 | }
117 | let testObj4=ObjectUtils.deepClone(testObj2)
118 | testObj4.Array=new Array(...[obj2,obj1,obj3,obj4])
119 | let testObj5=ObjectUtils.deepClone(testObj2)
120 | testObj5.Object.obj1.String='12344'
121 | expect(ObjectUtils.isEqual(testObj2,testObj3)).toBe(true);
122 | expect(ObjectUtils.isEqual(testObj2,ObjectUtils.deepClone(testObj2))).toBe(true);
123 | expect(ObjectUtils.isEqual(testObj2,testObj4)).toBe(false);
124 | expect(ObjectUtils.isEqual(testObj2,testObj5)).toBe(false);
125 |
126 |
127 | expect(ObjectUtils.argToStrKey(testObj2)===ObjectUtils.argToStrKey(testObj3)).toBe(true);
128 | expect(ObjectUtils.argToStrKey(testObj2)===ObjectUtils.argToStrKey(testObj4)).toBe(false);
129 | expect(ObjectUtils.argToStrKey(testObj2)===ObjectUtils.argToStrKey(testObj5)).toBe(false);
130 |
131 |
132 | expect(ObjectUtils.deepMerge({a:1},{b:1})).toEqual({
133 | a:1,
134 | b:1
135 | })
136 | expect(ObjectUtils.deepMerge({a:{
137 | a:1,
138 | b:1
139 | }},{a:{
140 | c:1,
141 | d:1
142 | }})).toEqual({
143 | a:{
144 | a:1,
145 | b:1,
146 | c:1,
147 | d:1
148 | }
149 | })
150 | expect(ObjectUtils.deepMerge({
151 | a:'1',
152 | arr1:[1,23],
153 | b:{
154 | ba:2,
155 | bb:false,
156 | bc:[1,2,{a:1}]
157 | }
158 | },{
159 | arr1:[1,23],
160 | b:{
161 | bc:[1,2,{a:1},3,4],
162 | bd:{a:1}
163 | },
164 | c:false,
165 | },ObjectUtils.ArrayMergeModeEnum.CompareMerge)).toEqual({
166 | a:'1',
167 | arr1:[1,23],
168 | b:{
169 | ba:2,
170 | bb:false,
171 | bc:[1,2,{a:1},{a:1},3,4],
172 | bd:{a:1}
173 | },
174 | c:false,
175 | })
176 |
177 | expect(ObjectUtils.deepMerge([1,2,3],[4,5,6])).toEqual([1,2,3])
178 | expect(ObjectUtils.deepMerge([1,2,3],[3,4],ObjectUtils.ArrayMergeModeEnum.IncrementalMerge)).toEqual([1,2,3,3,4])
179 | expect(ObjectUtils.deepMerge([1,2,3],[3,4],ObjectUtils.ArrayMergeModeEnum.CompareMerge)).toEqual([1,2,3,4])
180 |
181 | });
182 |
--------------------------------------------------------------------------------
/__test__/performance-utils.test.js:
--------------------------------------------------------------------------------
1 | const PerformanceUtils = require('../dist/cjs/performance-utils');
2 | const BootsJS = require('../dist/cjs');
3 |
4 |
5 | test('test PerformanceUtils class', () => {
6 | let a = 0
7 | function add(nv) {
8 | a += nv
9 | return a
10 | }
11 | const debounceAdd = PerformanceUtils.debounce(add, 500, true);
12 | debounceAdd(1); debounceAdd(1); debounceAdd(1);
13 | expect(a).toBe(1);
14 | const debounceAdd2 = BootsJS.PerformanceUtils.debounce(add, 500, true);
15 | debounceAdd2(1); debounceAdd2(1); debounceAdd2(1);
16 | expect(a).toBe(2);
17 |
18 | let num = 0
19 | function addNum(nv) {
20 | num += nv
21 | }
22 | const throttleAdd = PerformanceUtils.throttle(addNum, 500);
23 | throttleAdd(1); throttleAdd(1); throttleAdd(1);
24 | expect(num).toBe(1);
25 | const throttleAdd2 = BootsJS.PerformanceUtils.throttle(addNum, 500);
26 | throttleAdd2(1); throttleAdd2(1); throttleAdd2(1);
27 | expect(num).toBe(2);
28 |
29 | let count=0
30 | function addCount(nv){
31 | count+=nv;
32 | return count
33 | }
34 | const memoizeAdd=PerformanceUtils.memoize(addCount,{
35 | expirationTime:2000
36 | })
37 | memoizeAdd(1);memoizeAdd(1);memoizeAdd(1);
38 | expect(count).toBe(1)
39 | memoizeAdd(2);memoizeAdd(2);memoizeAdd(2);
40 | expect(count).toBe(3)
41 | });
--------------------------------------------------------------------------------
/__test__/priority-queue.test.js:
--------------------------------------------------------------------------------
1 | const PriorityQueue = require('../dist/cjs/priority-queue');
2 | const BootsJS=require('../dist/cjs');
3 |
4 |
5 | test('test PriorityQueue class', () => {
6 | let priorityQueue = new PriorityQueue();
7 | priorityQueue.enqueue('1', 1)
8 | priorityQueue.enqueue('1', 5)
9 | priorityQueue.enqueue('1', 9)
10 | expect(priorityQueue.length).toBe(3);
11 | expect(priorityQueue.top.priority).toBe(9);
12 | expect(priorityQueue.dequeue())
13 | expect(priorityQueue.top.priority).toBe(5)
14 |
15 | let ascPriorityQueue = new BootsJS.PriorityQueue(true);
16 | ascPriorityQueue.enqueue('1', 1)
17 | ascPriorityQueue.enqueue('1', 5)
18 | ascPriorityQueue.enqueue('1', 9)
19 | expect(ascPriorityQueue.length).toBe(3);
20 | expect(ascPriorityQueue.top.priority).toBe(1);
21 | expect(ascPriorityQueue.dequeue())
22 | expect(ascPriorityQueue.top.priority).toBe(5)
23 |
24 | });
--------------------------------------------------------------------------------
/__test__/reg-rules.test.js:
--------------------------------------------------------------------------------
1 | const RegRules = require('../dist/cjs/reg-rules');
2 | const BootsJS=require('../dist/cjs');
3 |
4 |
5 | test('test RegRules class', () => {
6 | expect(RegRules.chinesePhoneNumberRule.test('18523127384')).toBe(true);
7 | expect(RegRules.digitsAndLettersRule.test('134Afsv')).toBe(true);
8 | expect(RegRules.digitsRule.test('18523127384')).toBe(true);
9 | expect(RegRules.emailRule.test('wjl@gmail.com')).toBe(true);
10 | expect(RegRules.imageRule.test('test.png')).toBe(true);
11 | expect(RegRules.videoRule.test('test.mp4')).toBe(true);
12 | expect(RegRules.audioRule.test('test.mp3')).toBe(true);
13 | expect(RegRules.uppercaseLettersRule.test('ABC')).toBe(true);
14 | expect(RegRules.lowercaseLettersRule.test('abc')).toBe(true);
15 | expect(RegRules.chineseIDCardRule.test('52052219830823283x')).toBe(true);
16 | expect(RegRules.IPAddressRule.test('192.168.0.1')).toBe(true);
17 |
18 | expect(BootsJS.RegRules.chinesePhoneNumberRule.test('28523127384')).toBe(false);
19 | expect(BootsJS.RegRules.digitsAndLettersRule.test('134!Afsv')).toBe(false);
20 | expect(BootsJS.RegRules.digitsRule.test('18523s127384')).toBe(false);
21 | expect(BootsJS.RegRules.emailRule.test('wjlssgmail.com')).toBe(false);
22 | expect(BootsJS.RegRules.imageRule.test('test.mp4')).toBe(false);
23 | expect(BootsJS.RegRules.videoRule.test('test.mp3')).toBe(false);
24 | expect(BootsJS.RegRules.audioRule.test('test.png')).toBe(false);
25 | expect(BootsJS.RegRules.uppercaseLettersRule.test('AaBC')).toBe(false);
26 | expect(BootsJS.RegRules.lowercaseLettersRule.test('Aabc')).toBe(false);
27 | expect(BootsJS.RegRules.chineseIDCardRule.test('52052291830823283x')).toBe(false);
28 | expect(BootsJS.RegRules.IPAddressRule.test('1292.168.0.1')).toBe(false);
29 |
30 | });
--------------------------------------------------------------------------------
/__test__/string-utils.test.js:
--------------------------------------------------------------------------------
1 | const StringUtils = require('../dist/cjs/string-utils');
2 | const BootsJS=require('../dist/cjs/index');
3 |
4 |
5 | test('test StringUtils class', () => {
6 | expect(StringUtils.dashNameToUpperCamelCaseName('string-utils')).toBe('StringUtils');
7 | expect(StringUtils.camelCaseNameToDashName('StringUtils')).toBe('string-utils');
8 | expect(StringUtils.trim(' StringUtils',StringUtils.TrimOptions.leading)).toBe('StringUtils');
9 | expect(StringUtils.trim('StringUtils ',StringUtils.TrimOptions.trailing)).toBe('StringUtils');
10 | expect(BootsJS.StringUtils.trim(' StringUtils ',StringUtils.TrimOptions.side)).toBe('StringUtils');
11 | expect(BootsJS.StringUtils.trim(' String Utils ')).toBe('StringUtils');
12 | });
--------------------------------------------------------------------------------
/__test__/tree-utils.test.js:
--------------------------------------------------------------------------------
1 | const TreeUtils = require('../dist/cjs/tree-utils');
2 | const BootsJS = require('../dist/cjs/index');
3 |
4 |
5 | test('test TreeUtils class', () => {
6 | const tree = {
7 | name: '中国',
8 | code: '0',
9 | childList: [
10 | {
11 | name: '重庆',
12 | code: '01',
13 | },
14 | {
15 | name: '四川',
16 | code: '02',
17 | },
18 | {
19 | name: '广东',
20 | code: '03',
21 | },
22 | ]
23 | }
24 | let arr = BootsJS.TreeUtils.tree2Array([tree], 'childList', {
25 | isGenerateLevel: true,
26 | generateLevelAttributeName: 'level',
27 | isGenerateParentID: true,
28 | generateParentIDAttributeName: 'parentCode',
29 | nodeIDAttributeName: 'code',
30 | deleteAttributeList: ['childList']
31 | })
32 | expect(arr).toEqual(
33 | [
34 | { name: '中国', code: '0', level: 0 },
35 | { name: '重庆', code: '01', level: 1, parentCode: '0' },
36 | { name: '四川', code: '02', level: 1, parentCode: '0' },
37 | { name: '广东', code: '03', level: 1, parentCode: '0' },
38 | ]);
39 | let genTree = TreeUtils.array2Tree(arr, 'code', 'parentCode', 'childList', (node) => {
40 | return !('parentCode' in node)
41 | })
42 | expect(genTree).toEqual(
43 | [
44 | {
45 | name: '中国',
46 | code: '0',
47 | level: 0,
48 | childList: [
49 | { name: '重庆', code: '01', level: 1, parentCode: '0', childList: [] },
50 | { name: '四川', code: '02', level: 1, parentCode: '0', childList: [] },
51 | { name: '广东', code: '03', level: 1, parentCode: '0', childList: [] }
52 | ]
53 | }
54 | ]);
55 |
56 | const newtree = {
57 | name: '中国',
58 | code: '0',
59 | childList: [
60 | {
61 | name: '重庆',
62 | code: '01',
63 | },
64 | {
65 | name: '四川',
66 | code: '02',
67 | },
68 | {
69 | name: '广东',
70 | code: '03',
71 | },
72 | ]
73 | }
74 | let childList = BootsJS.TreeUtils.getChildList([newtree], 'code', '0', 'childList')
75 |
76 | expect(childList).toEqual(
77 | [
78 | { name: '重庆', code: '01' },
79 | { name: '四川', code: '02' },
80 | { name: '广东', code: '03' },
81 | ]);
82 |
83 |
84 | let filterList = TreeUtils.filter(genTree, 'childList', (obj) => {
85 | return obj.parentCode === '0'
86 | })
87 | expect(filterList).toEqual(
88 | [
89 | { name: '重庆', code: '01', level: 1, parentCode: '0', childList: [] },
90 | { name: '四川', code: '02', level: 1, parentCode: '0', childList: [] },
91 | { name: '广东', code: '03', level: 1, parentCode: '0', childList: [] },
92 | ]);
93 |
94 | let path = TreeUtils.findPath(genTree, 'code', '03', 'childList')
95 | expect(path).toEqual(
96 | [
97 | {
98 | name: '中国',
99 | code: '0',
100 | level: 0,
101 | childList: [
102 | { name: '重庆', code: '01', level: 1, parentCode: '0', childList: [] },
103 | { name: '四川', code: '02', level: 1, parentCode: '0', childList: [] },
104 | { name: '广东', code: '03', level: 1, parentCode: '0', childList: [] }
105 | ]
106 | },
107 | { name: '广东', code: '03', level: 1, parentCode: '0', childList: [] }
108 | ]);
109 | })
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boots-js",
3 | "version": "1.3.0",
4 | "description": "BootsJS is a library dedicated to extending the capabilities of native JavaScript, aiming to address common data structures, methods, and frequently used algorithms that are not natively supported in JavaScript.",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "npx rollup -c",
8 | "format": "prettier --write src/",
9 | "test": "npx jest",
10 | "doc": "npx typedoc",
11 | "prepare": "husky"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/JunLiangWangX/BootsJs.git"
16 | },
17 | "keywords": [
18 | "boots-js",
19 | "javascript",
20 | "native",
21 | "library",
22 | "lib",
23 | "node",
24 | "browser"
25 | ],
26 | "author": "JunLiangWang",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/JunLiangWangX/BootsJs/issues"
30 | },
31 | "homepage": "https://junliangwangx.github.io/BootsJS/",
32 | "browserslist": [
33 | "> 1%",
34 | "last 2 versions"
35 | ],
36 | "devDependencies": {
37 | "@babel/plugin-transform-runtime": "^7.24.7",
38 | "@babel/preset-env": "^7.25.3",
39 | "@babel/preset-typescript": "^7.24.7",
40 | "@rollup/plugin-babel": "^6.0.4",
41 | "@rollup/plugin-commonjs": "^26.0.1",
42 | "@rollup/plugin-node-resolve": "^15.2.3",
43 | "@rollup/plugin-terser": "^0.4.4",
44 | "@rollup/plugin-typescript": "^11.1.6",
45 | "husky": "^9.0.11",
46 | "jest": "^29.7.0",
47 | "prettier": "^3.3.3",
48 | "rollup": "^4.20.0",
49 | "tslib": "^2.6.3",
50 | "typedoc": "^0.25.8"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/resource/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunLiangWangX/BootsJS/3cfbe30823deef8a5e5386e1a470297158caacd8/resource/logo.png
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const typescript = require('@rollup/plugin-typescript');
3 | const babel = require('@rollup/plugin-babel');
4 | const { nodeResolve } = require('@rollup/plugin-node-resolve');
5 | const commonjs= require ('@rollup/plugin-commonjs');
6 | const terser =require ('@rollup/plugin-terser');
7 | const findExportFiles = require('./utils/find-export-files.js');
8 | const getLibraryName = require('./utils/get-library-name.js');
9 |
10 | const entryFiles = findExportFiles(__dirname, './src');
11 | const entryNames = Object.keys(entryFiles).reduce((acc, key) => {
12 | acc[key] = getLibraryName(entryFiles[key]);
13 | return acc;
14 | }, {});
15 |
16 | const baseConfig = {
17 | plugins: [
18 | nodeResolve({
19 | extensions: ['.tsx', '.ts', '.js'],
20 | }),
21 | commonjs(),
22 | babel({
23 | babelHelpers: 'runtime',
24 | exclude: 'node_modules/**',
25 | extensions: ['.tsx', '.ts', '.js'],
26 | presets: [
27 | '@babel/preset-env',
28 | '@babel/preset-typescript'
29 | ],
30 | plugins: ['@babel/plugin-transform-runtime']
31 | }),
32 | terser()
33 | ]
34 | };
35 |
36 | const esmConfig = {
37 | input: entryFiles,
38 | output: {
39 | dir: path.resolve(__dirname, 'dist'),
40 | format: 'es',
41 | entryFileNames: '[name].js',
42 | },
43 | ...baseConfig,
44 | plugins: [
45 | ...baseConfig.plugins,
46 | typescript({
47 | tsconfig: './tsconfig.json',
48 | compilerOptions: {
49 | declaration: true,
50 | outDir: path.resolve(__dirname, 'dist')
51 | }
52 | })
53 | ]
54 | };
55 |
56 | const cjsConfig = {
57 | input: entryFiles,
58 | output: {
59 | dir: path.resolve(__dirname, './dist/cjs/'),
60 | format: 'cjs',
61 | entryFileNames: '[name].js',
62 | },
63 | ...baseConfig,
64 | plugins: [
65 | ...baseConfig.plugins,
66 | typescript({
67 | tsconfig: './tsconfig.json',
68 | compilerOptions: {
69 | declaration: false,
70 | outDir: path.resolve(__dirname, './dist/cjs/')
71 | }
72 | })
73 | ]
74 | };
75 |
76 | const umdConfig = Object.keys(entryFiles).map(entry => ({
77 | input: entryFiles[entry],
78 | output: {
79 | file: path.resolve(__dirname, `./dist/umd/${entry}.js`),
80 | format: 'umd',
81 | name: entryNames[entry]
82 | },
83 | ...baseConfig,
84 | plugins: [
85 | ...baseConfig.plugins,
86 | typescript({
87 | tsconfig: './tsconfig.json',
88 | compilerOptions: {
89 | declaration: false,
90 | outDir: path.resolve(__dirname, './dist/umd/')
91 | }
92 | })
93 | ]
94 | }));
95 |
96 | module.exports = [esmConfig, cjsConfig, ...umdConfig];
97 |
--------------------------------------------------------------------------------
/src/array-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for working with array.(一些处理数组的工具)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.ArrayUtils.removeDuplicates([1,2,3,1]);
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const ArrayUtils = require('boots-js/cjs/array-utils'); // CommandJS
12 | * import ArrayUtils from 'boots-js/array-utils' // Es6 Module
13 | * ArrayUtils.removeDuplicates([1,2,3,1]);
14 | * ```
15 | * @module
16 | */
17 | import { argToStrKey } from './object-utils';
18 |
19 | /**
20 | * Remove duplicate values from array.(数组去除重复值)
21 | * @param {Array} arr Given an array.(给定数组)
22 | * @param {boolean} isCompareValue Whether to perform value comparison for elements of reference type.(对于引用类型的元素是否进行值比对)
23 | * @example
24 | * const ArrayUtils = require('boots-js/cjs/array-utils'); // CommandJS
25 | * import ArrayUtils from 'boots-js/array-utils' // Es6 Module
26 | *
27 | * const test1={a:'1'},test2={a:'1'},
28 | * arr1=[test1,test2,test1],
29 | * arr2=[1,2,3,1,4];
30 | * ArrayUtils.removeDuplicates(arr1) // [{a:'1'},{a:'1'}]
31 | * ArrayUtils.removeDuplicates(arr1,true) // [{a:'1'}]
32 | * ArrayUtils.removeDuplicates(arr2) //[1,2,3,4];
33 | */
34 | export function removeDuplicates(arr: Array, isCompareValue: boolean = false): Array {
35 | if (!Array.isArray(arr)) {
36 | console.warn(`${arr} not an Array!`);
37 | return arr;
38 | }
39 | const uniSet = new Set(),
40 | result: Array = [];
41 | arr.forEach((val) => {
42 | if (isCompareValue) {
43 | const key = argToStrKey(val);
44 | if (!uniSet.has(key)) {
45 | result.push(val);
46 | uniSet.add(key);
47 | }
48 | } else {
49 | if (!uniSet.has(val)) {
50 | result.push(val);
51 | uniSet.add(val);
52 | }
53 | }
54 | });
55 | return result;
56 | }
57 |
58 | /**
59 | * @ignore
60 | */
61 | export default {
62 | removeDuplicates,
63 | };
64 |
--------------------------------------------------------------------------------
/src/date-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for working with date and time.(一些处理日期时间的工具)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.DateUtils.dateFormater(new Date(),'YYYY-MM-DD HH:mm:ss');
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
12 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
13 | * DateUtils.dateFormater(new Date(),'YYYY-MM-DD HH:mm:ss');
14 | * ```
15 | * @module
16 | */
17 | /**
18 | * @enum Date unit enum.(日期单位枚举)
19 | * - `all`: Returns the time difference in years, months, days, hours, minutes, and seconds. (返回年、月、日、小时、分钟和秒的时间差)
20 | * - `year`: Returns the time difference in years. (返回年的时间差)
21 | * - `month`: Returns the time difference in months. (返回月的时间差)
22 | * - `day`: Returns the time difference in days. (返回天的时间差)
23 | * - `hour`: Returns the time difference in hours. (返回小时的时间差)
24 | * - `minute`: Returns the time difference in minutes. (返回分钟的时间差)
25 | * - `second`: Returns the time difference in seconds. (返回秒的时间差)
26 | */
27 | export enum DateUnitEnum {
28 | /**
29 | * Returns the time difference in years, months, days, hours, minutes, and seconds. (返回年、月、日、小时、分钟和秒的时间差)
30 | */
31 | all,
32 | /**
33 | * Returns the time difference in years. (返回年的时间差)
34 | */
35 | year,
36 | /**
37 | * Returns the time difference in months. (返回月的时间差)
38 | */
39 | month,
40 | /**
41 | * Returns the time difference in days. (返回天的时间差)
42 | */
43 | day,
44 | /**
45 | * Returns the time difference in hours. (返回小时的时间差)
46 | */
47 | hour,
48 | /**
49 | * Returns the time difference in minutes. (返回分钟的时间差)
50 | */
51 | minute,
52 | /**
53 | * Returns the time difference in seconds. (返回秒的时间差)
54 | */
55 | second,
56 | }
57 | /**
58 | * @enum Time zone offset enum.(时区偏移量枚举)
59 | */
60 | export enum TimeZoneOffsetEnum {
61 | 'UTC-12:00' = 720,
62 | 'UTC-11:00' = 660,
63 | 'UTC-10:00' = 600,
64 | 'UTC-09:00' = 540,
65 | 'UTC-08:00' = 480,
66 | 'UTC-07:00' = 420,
67 | 'UTC-06:00' = 360,
68 | 'UTC-05:00' = 300,
69 | 'UTC-04:00' = 240,
70 | 'UTC-03:30' = 210,
71 | 'UTC-03:00' = 180,
72 | 'UTC-02:00' = 120,
73 | 'UTC-01:00' = 60,
74 | 'UTC±00:00' = 0,
75 | 'UTC+01:00' = -60,
76 | 'UTC+02:00' = -120,
77 | 'UTC+03:00' = -180,
78 | 'UTC+03:30' = -210,
79 | 'UTC+04:00' = -240,
80 | 'UTC+04:30' = -270,
81 | 'UTC+05:00' = -300,
82 | 'UTC+05:30' = -330,
83 | 'UTC+05:45' = -345,
84 | 'UTC+06:00' = -360,
85 | 'UTC+06:30' = -390,
86 | 'UTC+07:00' = -420,
87 | 'UTC+08:00' = -480,
88 | 'UTC+08:45' = -525,
89 | 'UTC+09:00' = -540,
90 | 'UTC+09:30' = -570,
91 | 'UTC+10:00' = -600,
92 | 'UTC+10:30' = -630,
93 | 'UTC+11:00' = -660,
94 | 'UTC+12:00' = -720,
95 | }
96 | /**
97 | * DateTime Object(日期时间对象)
98 | */
99 | interface DateTime {
100 | years: number;
101 | months: number;
102 | days: number;
103 | hours: number;
104 | minutes: number;
105 | seconds: number;
106 | }
107 | /**
108 | * Format date and time.(格式化日期与时间)
109 | * @param {string|number|Date} date Specify date and time, support timestamp/date character/Date object, default is current date.(指定日期时间,支持时间戳/日期字符/Date对象,默认为当前日期)
110 | * @param {string} formater Specify the date and time format, default is YYYY-MM-DD HH:mm:ss.(指定日期和时间的格式,默认为YYYY-MM-DD HH:mm:ss)
111 | * @example
112 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
113 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
114 | *
115 | * DateUtils.dateFormater('Mon Feb 26 2024', 'YYYY-MM-DD') //2024-02-26
116 | * DateUtils.dateFormater('2024/2/26', 'YYYY-MM-DD') //2024-02-26
117 | * DateUtils.dateFormater(1708917102083, 'YYYY-MM-DD HH:mm:ss') //'2024-02-26 11:11:42'
118 | * DateUtils.dateFormater('2024/2/26 11:11:42', 'YYYY/MM/DD/HH/mm/ss') //'2024/02/26/11/11/42';
119 | */
120 | export function dateFormater(date: string | number | Date = new Date(), formater: string = 'YYYY-MM-DD HH:mm:ss'): String {
121 | let tempDate = date ? new Date(date) : new Date(),
122 | year = tempDate.getFullYear(),
123 | month = tempDate.getMonth() + 1,
124 | day = tempDate.getDate(),
125 | hour = tempDate.getHours(),
126 | minute = tempDate.getMinutes(),
127 | second = tempDate.getSeconds();
128 |
129 | return formater
130 | .replace(/YYYY/g, year.toString())
131 | .replace(/YY/g, year.toString().substr(2, 2))
132 | .replace(/MM/g, (month < 10 ? '0' : '') + month)
133 | .replace(/DD/g, (day < 10 ? '0' : '') + day)
134 | .replace(/HH/g, (hour < 10 ? '0' : '') + hour)
135 | .replace(/mm/g, (minute < 10 ? '0' : '') + minute)
136 | .replace(/ss/g, (second < 10 ? '0' : '') + second);
137 | }
138 | /**
139 | * Determine whether a given date is a leap year.(给定年份判断是否闰年)
140 | * @param {number} year Specify the year.(指定年份)
141 | * @example
142 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
143 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
144 | *
145 | * DateUtils.isLeapYear(2040) //true
146 | * DateUtils.isLeapYear(2019) //false
147 | */
148 | export function isLeapYear(year: number): boolean {
149 | return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
150 | }
151 | /**
152 | * Given a date, returns the total number of days in the month.(给定日期返回当月总天数)
153 | * @param {number} year Specify the year.(指定年份)
154 | * @param {number} month Specify the month, starting from 1.(指定月份,从1月开始)
155 | * @example
156 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
157 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
158 | *
159 | * DateUtils.getDaysInMonth(2024, 2) //29
160 | * DateUtils.getDaysInMonth(2025, 2) //28
161 | * DateUtils.getDaysInMonth(2025, 8) //31
162 | */
163 | export function getDaysInMonth(year: number, month: number): number {
164 | if (month < 1 || month > 12) throw new Error('month out of range');
165 | const daysPerMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
166 | if (month === 2 && isLeapYear(year)) return 29;
167 | return daysPerMonth[month];
168 | }
169 | /**
170 | * Calculate the time between two dates.(计算两日期的相隔时间)
171 | * @param {string|number|Date} startDate Specify start date, support timestamp/date character/Date object, default is current date.(指定开始日期,支持时间戳/日期字符/Date对象,默认为当前日期)
172 | * @param {string|number|Date} endDate Specify end date, support timestamp/date character/Date object, default is current date.(指定结束日期,支持时间戳/日期字符/Date对象,默认为当前日期)
173 | * @param {DateUnit} unit Specify the calculation unit, default is days. (指定计算单位,默认为天)
174 | * - `dateUnitEnum.all`: Returns the time difference in years, months, days, hours, minutes, and seconds. (返回年、月、日、小时、分钟和秒的时间差)
175 | * - `dateUnitEnum.year`: Returns the time difference in years. (返回年的时间差)
176 | * - `dateUnitEnum.month`: Returns the time difference in months. (返回月的时间差)
177 | * - `dateUnitEnum.day`: Returns the time difference in days. (返回天的时间差)
178 | * - `dateUnitEnum.hour`: Returns the time difference in hours. (返回小时的时间差)
179 | * - `dateUnitEnum.minute`: Returns the time difference in minutes. (返回分钟的时间差)
180 | * - `dateUnitEnum.second`: Returns the time difference in seconds. (返回秒的时间差)
181 | * @example
182 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
183 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
184 | *
185 | * DateUtils.getDateDiff('2024/1/26', '2025/1/26', DateUtils.dateUnitEnum.day) //366
186 | * DateUtils.getDateDiff('2024/1/26', '2025/1/26', DateUtils.dateUnitEnum.month) //12
187 | * DateUtils.getDateDiff('2025/6/19', '2025/9/18', DateUtils.dateUnitEnum.year) //0
188 | * DateUtils.getDateDiff('2025/6/19', '2025/9/18', DateUtils.dateUnitEnum.all) //{years: 0, months: 2,days: 30,hours: 0,minutes: 0,seconds: 0}
189 | */
190 | export function getDateDiff(
191 | startDate: string | number | Date = new Date(),
192 | endDate: string | number | Date = new Date(),
193 | unit: DateUnitEnum = DateUnitEnum.day
194 | ): number | DateTime {
195 | const start = startDate ? new Date(startDate) : new Date(),
196 | end = endDate ? new Date(endDate) : new Date();
197 | if (end.getTime() < start.getTime()) throw new Error('The start date should be less than the end date');
198 | const diffSecond = end.getSeconds() - start.getSeconds(),
199 | diffMinute = end.getMinutes() - start.getMinutes() - (diffSecond < 0 ? 1 : 0),
200 | diffHour = end.getHours() - start.getHours() - (diffMinute < 0 ? 1 : 0),
201 | diffDay = end.getDate() - start.getDate() - (diffHour < 0 ? 1 : 0),
202 | diffMonth = end.getMonth() - start.getMonth() - (diffDay < 0 ? 1 : 0),
203 | diffYear = end.getFullYear() - start.getFullYear() - (diffMonth < 0 ? 1 : 0);
204 | switch (unit) {
205 | case DateUnitEnum.all:
206 | let endMonth = end.getMonth(),
207 | endYear = end.getFullYear();
208 | if (endMonth == 0) {
209 | endMonth = 12;
210 | endYear -= endYear;
211 | }
212 | return {
213 | years: diffYear,
214 | months: diffMonth < 0 ? diffMonth + 12 : diffMonth,
215 | days: diffDay < 0 ? diffDay + getDaysInMonth(endYear, endMonth) : diffDay,
216 | hours: diffHour < 0 ? diffHour + 24 : diffHour,
217 | minutes: diffMinute < 0 ? diffMinute + 60 : diffMinute,
218 | seconds: diffSecond < 0 ? diffSecond + 60 : diffSecond,
219 | };
220 | case DateUnitEnum.year:
221 | return diffYear;
222 | case DateUnitEnum.month:
223 | return (diffMonth < 0 ? diffMonth + 12 : diffMonth) + diffYear * 12;
224 | case DateUnitEnum.day:
225 | return Math.floor((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
226 | case DateUnitEnum.hour:
227 | return Math.floor((end.getTime() - start.getTime()) / (1000 * 60 * 60));
228 | case DateUnitEnum.minute:
229 | return Math.floor((end.getTime() - start.getTime()) / (1000 * 60));
230 | case DateUnitEnum.second:
231 | return Math.floor((end.getTime() - start.getTime()) / 1000);
232 | default:
233 | throw new Error('Please enter unit as the correct enumeration type');
234 | }
235 | }
236 | /**
237 | * Date Add/Subtract Calculator(日期加/减计算器)
238 | * @param {string|number|Date} startDate Specify start date, support timestamp/date character/Date object, default is current date.(指定开始日期,支持时间戳/日期字符/Date对象,默认为当前日期)
239 | * @param {DateTime} options An object specifying the amount of years, months, days, hours, minutes, and seconds to add/subtract from the start date. (包含要添加/减去的年、月、日、小时、分钟和秒数的对象)
240 | * - years: Number of years to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的年数。正数表示添加,负数表示减去)
241 | * - months: Number of months to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的月数。正数表示添加,负数表示减去)
242 | * - days: Number of days to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的天数。正数表示添加,负数表示减去)
243 | * - hours: Number of hours to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的小时数。正数表示添加,负数表示减去)
244 | * - minutes: Number of minutes to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的分钟数。正数表示添加,负数表示减去)
245 | * - seconds: Number of seconds to add/subtract. Positive number for addition, negative number for subtraction. (要添加/减去的秒数。正数表示添加,负数表示减去)
246 | * @example
247 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
248 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
249 | *
250 | * DateUtils.dateCalculator('2024/2/12', { years: 1 }).toISOString() //2025-02-12
251 | */
252 | export function dateCalculator(startDate: string | number | Date = new Date(), options: DateTime): Date {
253 | const start = startDate ? new Date(startDate) : new Date();
254 | const { years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = options;
255 |
256 | const newDate = new Date(start.getTime());
257 | newDate.setFullYear(start.getFullYear() + years);
258 | newDate.setMonth(start.getMonth() + months);
259 | newDate.setDate(start.getDate() + days);
260 | newDate.setHours(start.getHours() + hours);
261 | newDate.setMinutes(start.getMinutes() + minutes);
262 | newDate.setSeconds(start.getSeconds() + seconds);
263 |
264 | return newDate;
265 | }
266 | /**
267 | * Convert time zone.(转换时区)
268 | * @param {string} date Give a date.(给定时间)
269 | * @param {TimeZoneOffsetEnum} orginTimeZone Time zone of original time.(原始时间的时区)
270 | * @param {TimeZoneOffsetEnum} timeZone Time zone to be converted.(需要转换的时区)
271 | * @example
272 | * const DateUtils = require('boots-js/cjs/date-utils'); // CommandJS
273 | * import DateUtils from 'boots-js/date-utils' // Es6 Module
274 | *
275 | * DateUtils.convertTimeZone(
276 | * 1711611931754,
277 | * DateUtils.timeZoneOffsetEnum['UTC+08:00'],
278 | * DateUtils.timeZoneOffsetEnum['UTC-06:00'])
279 | * DateUtils.convertTimeZone(
280 | * '2024/2/12',
281 | * DateUtils.timeZoneOffsetEnum['UTC+08:00'],
282 | * DateUtils.timeZoneOffsetEnum['UTC+09:00'])
283 | */
284 | export function convertTimeZone(date: string | number | Date = new Date(), orginTimeZone: TimeZoneOffsetEnum, timeZone: TimeZoneOffsetEnum): Date {
285 | const orginDate = date ? new Date(date) : new Date();
286 | return dateCalculator(orginDate, {
287 | years: 0,
288 | months: 0,
289 | days: 0,
290 | hours: 0,
291 | minutes: orginTimeZone - timeZone,
292 | seconds: 0,
293 | });
294 | }
295 |
296 | /**
297 | * @ignore
298 | */
299 | export default {
300 | DateUnitEnum,
301 | TimeZoneOffsetEnum,
302 | dateFormater,
303 | isLeapYear,
304 | getDaysInMonth,
305 | getDateDiff,
306 | dateCalculator,
307 | convertTimeZone,
308 | };
309 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import PriorityQueue from './priority-queue';
2 | import RegRules from './reg-rules';
3 | import DateUtils from './date-utils';
4 | import PerformanceUtils from './performance-utils';
5 | import StringUtils from './string-utils';
6 | import ObjectUtils from './object-utils';
7 | import ArrayUtils from './array-utils';
8 | import TreeUtils from './tree-utils';
9 |
10 | export default {
11 | PriorityQueue,
12 | RegRules,
13 | DateUtils,
14 | PerformanceUtils,
15 | StringUtils,
16 | ObjectUtils,
17 | ArrayUtils,
18 | TreeUtils,
19 | };
20 |
--------------------------------------------------------------------------------
/src/object-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for working with Object.(一些处理对象的工具)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.ObjectUtils.type(123); //'Number'
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
12 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
13 | * ObjectUtils.type(123); //'Number'
14 | * ```
15 | * @module
16 | */
17 |
18 | /**
19 | * Determine parameter type.(判断给定参数类型)
20 | * @param {*} obj Need to determine the type of value.(需要判断类型的值)
21 | * @example
22 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
23 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
24 | *
25 | * ObjectUtils.type(new Array()); //'Array'
26 | * ObjectUtils.type('123'); //'String'
27 | * ObjectUtils.type(true); //'Boolean'
28 | * ObjectUtils.type(new Map()); //'Map'
29 | */
30 | export function type(obj: any): string {
31 | return Object.prototype.toString.call(obj).split(' ')[1].slice(0, -1);
32 | }
33 | /**
34 | * Deep clone object.(深度拷贝对象)
35 | * @param {any} obj Value to be copied.(需要拷贝的值)
36 | * @example
37 | * // Supported types: primitive types, TypedArray, Array, Set, Map, Object, ArrayBuffer, DataView, Date,RegExp, Symbol, Proxy(Will be treated like an object, interceptors cannot be copied)
38 | * // Notice:Unsupported types, such as Function, WeakRef, WeakSet, WeakMap, etc., will directly copy their references.
39 | *
40 | * // 支持的类型:原始类型、TypedArray、Array、Set、Map、Object、ArrayBuffer、DataView、Date、RegExp、Symbol、Proxy(将被视为对象,拦截器无法复制)
41 | * // 注意:不支持的类型,例如:Function、WeakRef、WeakSet、WeakMap等会直接复制其引用
42 | *
43 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
44 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
45 | *
46 | * const obj1={Number:1},obj2={Boolean:2},obj3={obj:obj1,String:'123'},
47 | * const testObj={
48 | * Int8Array:new Int8Array([1,2,3,4,5]),
49 | * Date:new Date(),
50 | * RegExp:/1234/,
51 | * Array:new Array(...[obj1,obj2,obj3]),
52 | * Set:new Set([obj1,obj2,obj3]),
53 | * Map:map,
54 | * Object:obj3,
55 | * ArrayBuffer:new ArrayBuffer(10),
56 | * DataView:new DataView(new ArrayBuffer(10)),
57 | * Function:fun
58 | * }
59 | *
60 | * let deepCopyObj=ObjectUtils.deepClone(testObj)
61 | * deepCopyObj.Int8Array===testObj.Int8Array //false
62 | * deepCopyObj.Date===testObj.Date //false
63 | * deepCopyObj.Object.obj1.obj===testObj.Object.obj1.obj //false
64 | */
65 | export function deepClone(obj: any): any {
66 | function clone(obj: any, visited = new Map()): any {
67 | let objType = type(obj);
68 | if (objType in NumericTypeEnum) return new obj.constructor(obj);
69 |
70 | // Check for circular references
71 | if (visited.has(obj)) return visited.get(obj);
72 |
73 | switch (objType) {
74 | case ArrayBufferType:
75 | return new Int8Array(new Int8Array(obj)).buffer;
76 | case DataViewType:
77 | return new DataView(new Int8Array(new Int8Array(obj.buffer)).buffer);
78 | case SymbolType:
79 | return Symbol(obj.description);
80 | case ArrayType: {
81 | let tempArray = new Array();
82 | visited.set(obj, tempArray);
83 | for (const item of obj) tempArray.push(clone(item, visited));
84 | visited.delete(obj);
85 | return tempArray;
86 | }
87 | case SetType: {
88 | let tempSet = new Set();
89 | visited.set(obj, tempSet);
90 | for (const item of obj) tempSet.add(clone(item, visited));
91 | visited.delete(obj);
92 | return tempSet;
93 | }
94 | case MapType: {
95 | let tempMap = new Map();
96 | visited.set(obj, tempMap);
97 | for (const [key, value] of obj.entries()) {
98 | tempMap.set(clone(key, visited), clone(value, visited));
99 | }
100 | visited.delete(obj);
101 | return tempMap;
102 | }
103 | case ObjectType: {
104 | let tempObj: any = {};
105 | visited.set(obj, tempObj);
106 | Object.keys(obj).forEach((key) => {
107 | tempObj[key] = clone(obj[key], visited);
108 | });
109 | visited.delete(obj);
110 | return tempObj;
111 | }
112 | }
113 | return obj;
114 | }
115 | return clone(obj);
116 | }
117 | /**
118 | * Compare two objects for equality.(比较两对象是否相等)
119 | * @param {any} obj1 Object to be compared 1.(需要比较的对象1)
120 | * @param {any} obj2 Object to be compared 2.(需要比较的对象2)
121 | * @example
122 | * // Supported types: primitive types, TypedArray, Array, Set, Map, Object, ArrayBuffer, DataView, Date,RegExp, Symbol(Compare their descriptions), Proxy(Will be treated like an object, Interceptors cannot be compared)
123 | * // Note: Unsupported types, such as Function, WeakRef, WeakSet, WeakMap, etc., will directly compare their reference addresses.
124 | *
125 | * // 支持的类型:原始类型、TypedArray、Array、Set、Map、Object、ArrayBuffer、DataView、Date、RegExp、Symbol(比较其description)、Proxy(将被视为对象,拦截器无法比较)
126 | * // 注意:不支持的类型,例如:Function、WeakRef、WeakSet、WeakMap等会直接比较其引用地址
127 | *
128 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
129 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
130 | *
131 | * const testObj2={
132 | * BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
133 | * RegExp:/1234/,
134 | * Array:new Array(...[obj1,obj2,obj3,obj4]),
135 | * Set:new Set([obj1,obj2,obj3,obj4]),
136 | * Object:obj4,
137 | * Map:map2,
138 | * Date:date,
139 | * ArrayBuffer:new ArrayBuffer(10),
140 | * DataView:new DataView(new ArrayBuffer(10)),
141 | * }
142 | *
143 | * ObjectUtils.isEqual(testObj2,ObjectUtils.deepClone(testObj2)) //true
144 | * let testObj5=ObjectUtils.deepClone(testObj2)
145 | * testObj5.Object.obj1.String='12344'
146 | * ObjectUtils.isEqual(testObj2,testObj5) //false
147 | */
148 | export function isEqual(obj1: any, obj2: any): boolean {
149 | function compare(obj1: any, obj2: any, visited1 = new Set(), visited2 = new Set()): boolean {
150 | if (obj1 === obj2) return true;
151 | let type1 = type(obj1),
152 | type2 = type(obj2);
153 | if (type1 !== type2) return false;
154 | if (type1 in NumericTypeEnum || type1 === SymbolType) return obj1.valueOf().toString() === obj2.valueOf().toString();
155 | if (type1 === ArrayBufferType) return new Int8Array(obj1).toString() === new Int8Array(obj2).toString();
156 | if (type1 === DataViewType) return new Int8Array(obj1.buffer).toString() === new Int8Array(obj2.buffer).toString();
157 |
158 | if (visited1.has(obj1) || visited2.has(obj2)) return true;
159 | visited1.add(obj1);
160 | visited2.add(obj2);
161 | let result = false;
162 | switch (type1) {
163 | case ArrayType:
164 | result = obj1.length === obj2.length;
165 | if (result === true) {
166 | for (let i = 0; i < obj2.length; i++)
167 | if (compare(obj1[i], obj2[i], visited1, visited2) === false) {
168 | result = false;
169 | break;
170 | }
171 | }
172 | break;
173 | case SetType:
174 | case MapType:
175 | result = argToStrKey(obj1) === argToStrKey(obj2);
176 | break;
177 | case ObjectType:
178 | let keys1 = Object.keys(obj1).sort(),
179 | keys2 = Object.keys(obj2).sort();
180 | result = compare(keys1, keys2);
181 | if (result === true) {
182 | for (const key of keys1) {
183 | if (compare(obj1[key], obj2[key], visited1, visited2) === false) {
184 | result = false;
185 | break;
186 | }
187 | }
188 | }
189 | break;
190 | }
191 | visited1.delete(obj1);
192 | visited2.delete(obj2);
193 | return result;
194 | }
195 | return compare(obj1, obj2);
196 | }
197 | /**
198 | * Convert parameters to String as unique key.(将参数转换为String作为唯一key)
199 | * @param {any} arg Parameters that need to be converted.(需要转换的参数)
200 | * @example
201 | * // Supported types: primitive types, TypedArray, Array, Set, Map, Object, ArrayBuffer, DataView, Date,RegExp, Symbol(Compare their descriptions), Proxy(Will be treated like an object, Interceptors cannot be compared)
202 | * // Note: Unsupported types, such as WeakRef, WeakSet, WeakMap, etc., will directly output the type.
203 | *
204 | * // 支持的类型:原始类型、TypedArray、Array、Set、Map、Object、ArrayBuffer、Function、DataView、Date、 RegExp、Symbol、Proxy(将被视为对象,拦截器无法输出)
205 | * // 注意:不支持的类型,例如:WeakRef、WeakSet、WeakMap等会直接输出类型
206 | *
207 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
208 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
209 | *
210 | * const testObj2={
211 | * BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
212 | * RegExp:/1234/,
213 | * Array:new Array(...[obj1,obj2,obj3,obj4]),
214 | * Set:new Set([obj1,obj2,obj3,obj4]),
215 | * Object:obj4,
216 | * Map:map2,
217 | * Date:date,
218 | * ArrayBuffer:new ArrayBuffer(10),
219 | * DataView:new DataView(new ArrayBuffer(10)),
220 | * }
221 | * const testObj3={
222 | * Array:new Array(...[obj1,obj2,obj3,obj4]),
223 | * Set:new Set([obj1,obj2,obj3,obj4]),
224 | * BigInt64Array:new BigInt64Array([BigInt(123),BigInt(123),BigInt(123)]),
225 | * ArrayBuffer:new ArrayBuffer(10),
226 | * Object:obj4,
227 | * Map:map2,
228 | * Date:date,
229 | * DataView:new DataView(new ArrayBuffer(10)),
230 | * RegExp:/1234/,
231 | * }
232 | *
233 | * let testObj5=ObjectUtils.deepClone(testObj2)
234 | * testObj5.Object.obj1.String='12344'
235 | *
236 | * ObjectUtils.argToStrKey(testObj2)===ObjectUtils.argToStrKey(testObj3) //true
237 | * ObjectUtils.argToStrKey(testObj2)===ObjectUtils.argToStrKey(testObj5) //false
238 | */
239 | export function argToStrKey(arg: any): string {
240 | function generateKey(arg: any, visited = new Set()): string {
241 | let objType = type(arg);
242 | if (visited.has(arg)) return '[Circular Reference]';
243 | if (objType in NumericTypeEnum || objType === FunctionType || objType === SymbolType) return arg.toString();
244 | switch (objType) {
245 | case ArrayBufferType:
246 | return new Int8Array(arg).toString();
247 | case DataViewType:
248 | return new Int8Array(arg.buffer).toString();
249 | /*case WeakRefType:
250 | return type + ":{" + generateKey(arg.deref(), visited) + "}";*/
251 | case ObjectType: {
252 | visited.add(arg);
253 | let sortArray: string[] = [];
254 | let argList: string[] = [];
255 | Object.keys(arg).forEach((key) => {
256 | sortArray.push(key);
257 | });
258 | sortArray.sort();
259 | for (let item of sortArray) {
260 | argList.push(item);
261 | argList.push(generateKey(arg[item], visited));
262 | }
263 | visited.delete(arg);
264 | return '{' + argList.join(':') + '}';
265 | }
266 | case ArrayType:
267 | case SetType: {
268 | visited.add(arg);
269 | let strList: string[] = [];
270 | for (let item of arg) strList.push(generateKey(item, visited));
271 | if (objType === SetType) strList.sort();
272 | visited.delete(arg);
273 | return type + ':[' + strList.join(',') + ']';
274 | }
275 | case MapType: {
276 | visited.add(arg);
277 | let sortArray: string[] = [];
278 | let keyMap = new Map();
279 | let argList: string[] = [];
280 | for (const key of arg.keys()) {
281 | let genKey = generateKey(key, visited);
282 | sortArray.push(genKey);
283 | keyMap.set(genKey, arg.get(key));
284 | }
285 | sortArray.sort();
286 | for (let item of sortArray) {
287 | argList.push(item);
288 | argList.push(generateKey(keyMap.get(item), visited));
289 | }
290 | visited.delete(arg);
291 | return type + ':{' + argList.join(':') + '}';
292 | }
293 | }
294 | return String(arg);
295 | }
296 | return generateKey(arg);
297 | }
298 |
299 | /**
300 | * Deep merge obj2 to obj1.(深度合并obj2到obj1)
301 | * @param {any} obj1
302 | * @param {any} obj2
303 | * @param {*} arrayMergeModeEnum Array merging mode.(数组合并模式)
304 | * - ReplaceMerge: Replace merge, directly use the left array,this value is the default.(替换合并,直接使用左边数组,该值为默认值)
305 | * - IncrementalMerge: Incremental merging, array splicing merging.(增量合并,数组拼接合并)
306 | * - CompareMerge: Compare merge, Deeply compare the contents of two arrays and merge them.(比较合并,深度比较两数组内容合并)
307 | * @example
308 | * // Note: Comparing reference types during merging is based on address comparison, not usage value comparison!
309 | * // 注意:合并中比较引用类型都是进行地址比对,并未对使用值比对!
310 | *
311 | * const ObjectUtils = require('boots-js/cjs/object-utils'); // CommandJS
312 | * import ObjectUtils from 'boots-js/object-utils' // Es6 Module
313 | *
314 | * // ReplaceMerge(替换合并)
315 | * ObjectUtils.deepMerge([1,2,3],[4,5,6]) // [1,2,3]
316 | * // IncrementalMerge(增量合并)
317 | * ObjectUtils.deepMerge([1,2,3],[3,4],ObjectUtils.ArrayMergeModeEnum.IncrementalMerge) // [1,2,3,3,4]
318 | * // CompareMerge(比较合并)
319 | * ObjectUtils.deepMerge([1,2,3],[3,4],ObjectUtils.ArrayMergeModeEnum.CompareMerge) // [1,2,3,4]
320 | *
321 | */
322 | export function deepMerge(obj1: any, obj2: any, arrayMergeModeEnum = ArrayMergeModeEnum.ReplaceMerge): any {
323 | function mergeTypedArray(typeArray1: Int8Array, typeArray2: Int8Array): Array {
324 | let arr1 = Array.from(typeArray1),
325 | arr2 = Array.from(typeArray2);
326 | if (arrayMergeModeEnum === ArrayMergeModeEnum.CompareMerge) {
327 | arr2.forEach((val: any) => {
328 | if (!arr1.includes(val)) arr1.push(val);
329 | });
330 | return arr1;
331 | } else return arr1.concat(arr2);
332 | }
333 | function recursion(obj1: any, obj2: any, visited1 = new WeakSet(), visited2 = new WeakSet()) {
334 | if (obj1 === obj2) return obj1;
335 | const type1 = type(obj1),
336 | type2 = type(obj2);
337 | if (type1 !== type2 || type1 === NumericTypeEnum.Date || type1 === NumericTypeEnum.RegExp) return obj1;
338 | if (type1 in NumericTypeEnum && arrayMergeModeEnum !== ArrayMergeModeEnum.ReplaceMerge) {
339 | let mergeArr = mergeTypedArray(obj1, obj2);
340 | obj1 = new obj1.constructor(mergeArr);
341 | }
342 | if (visited1.has(obj1) || visited2.has(obj2)) return obj1;
343 | visited1.add(obj1);
344 | visited2.add(obj2);
345 | switch (type1) {
346 | case ArrayType:
347 | if (arrayMergeModeEnum === ArrayMergeModeEnum.IncrementalMerge) {
348 | obj1 = obj1.concat(obj2);
349 | } else if (arrayMergeModeEnum === ArrayMergeModeEnum.CompareMerge) {
350 | obj2.forEach((val: any) => {
351 | if (!obj1.includes(val)) obj1.push(val);
352 | });
353 | }
354 | break;
355 | case ObjectType:
356 | Object.keys(obj2).forEach((key: string) => {
357 | if (key in obj1) obj1[key] = recursion(obj1[key], obj2[key]);
358 | else obj1[key] = obj2[key];
359 | });
360 | break;
361 | case MapType:
362 | for (let key of obj2.keys()) {
363 | if (obj1.has(key)) obj1.set(key, recursion(obj1.get(key), obj2.get(key)));
364 | else obj1.set(key, obj2.get(key));
365 | }
366 | break;
367 | case SetType:
368 | obj1 = new Set([...obj1, ...obj2]);
369 | break;
370 | case ArrayBufferType:
371 | if (arrayMergeModeEnum !== ArrayMergeModeEnum.ReplaceMerge)
372 | obj1 = new Int8Array(mergeTypedArray(new Int8Array(obj1), new Int8Array(obj2))).buffer;
373 | break;
374 | case DataViewType:
375 | if (arrayMergeModeEnum !== ArrayMergeModeEnum.ReplaceMerge)
376 | obj1 = new DataView(new Int8Array(mergeTypedArray(new Int8Array(obj1.buffer), new Int8Array(obj2.buffer))).buffer);
377 | break;
378 | }
379 | visited1.delete(obj1);
380 | visited2.delete(obj2);
381 | return obj1;
382 | }
383 | return recursion(obj1, obj2);
384 | }
385 | /**
386 | * Array merge mode enum.(数组合并模式枚举)
387 | * - `IncrementalMerge`: Incremental merging, array splicing merging.(增量合并,数组拼接合并)
388 | * - `ReplaceMerge`: Replace merge, directly use the left array.(替换合并,直接使用左边数组)
389 | * - `CompareMerge`: Compare merge, Deeply compare the contents of two arrays and merge them.(比较合并,深度比较两数组内容合并)
390 | */
391 | export enum ArrayMergeModeEnum {
392 | /**
393 | * Incremental merging, array splicing merging.(增量合并,数组拼接合并)
394 | */
395 | IncrementalMerge,
396 | /**
397 | * Replace merge, directly use the left array.(替换合并,直接使用左边数组)
398 | */
399 | ReplaceMerge,
400 | /**
401 | * Compare merge, Deeply compare the contents of two arrays and merge them.(比较合并,深度比较两数组内容合并)
402 | */
403 | CompareMerge,
404 | }
405 | enum NumericTypeEnum {
406 | Int8Array = 'Int8Array',
407 | Int16Array = 'Int16Array',
408 | Int32Array = 'Int32Array',
409 | Uint8Array = 'Uint8Array',
410 | Uint16Array = 'Uint16Array',
411 | Uint32Array = 'Uint32Array',
412 | Float32Array = 'Float32Array',
413 | Float64Array = 'Float64Array',
414 | BigInt64Array = 'BigInt64Array',
415 | BigUint64Array = 'BigUint64Array',
416 | Date = 'Date',
417 | RegExp = 'RegExp',
418 | }
419 | const ArrayType = 'Array';
420 | const SetType = 'Set';
421 | const MapType = 'Map';
422 | const ObjectType = 'Object';
423 | const ArrayBufferType = 'ArrayBuffer';
424 | const DataViewType = 'DataView';
425 | const FunctionType = 'Function';
426 | const SymbolType = 'Symbol';
427 |
428 | /**
429 | * @ignore
430 | */
431 | export default {
432 | type,
433 | deepClone,
434 | isEqual,
435 | argToStrKey,
436 | deepMerge,
437 | ArrayMergeModeEnum,
438 | };
439 |
--------------------------------------------------------------------------------
/src/performance-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for improve performance.(一些提升性能的方法)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.PerformanceUtils.debounce(test);
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const PerformanceUtils = require('boots-js/cjs/performance-utils'); // CommandJS
12 | * import PerformanceUtils from 'boots-js/performance-utils' // Es6 Module
13 | * PerformanceUtils.debounce(test);
14 | * ```
15 | * @module
16 | */
17 |
18 | import { argToStrKey } from './object-utils';
19 | /**
20 | * Debounce function.(防抖函数)
21 | * @param {Function} func Functions that require debounce.(需要防抖的函数)
22 | * @param {number} delay Delay,default 300ms.(延迟时间,默认300ms)
23 | * @param {boolean} isImmediate Whether to execute the function immediately,Default false.(是否立即执行函数,默认否)
24 | * @example
25 | * const PerformanceUtils = require('boots-js/cjs/performance-utils'); // CommandJS
26 | * import PerformanceUtils from 'boots-js/performance-utils' // Es6 Module
27 | *
28 | * let num=0
29 | * function add(nv){
30 | * num+=nv
31 | * }
32 | * const debounceAdd=PerformanceUtils.debounce(add);
33 | * debounceAdd(1);
34 | * debounceAdd(1);
35 | * debounceAdd(1);
36 | * console.info(num); // 1
37 | */
38 | export function debounce(func: Function, delay: number = 300, isImmediate: boolean = false): Function {
39 | let timeoutId: NodeJS.Timeout | undefined;
40 |
41 | return function (this: any, ...args: any) {
42 | if (isImmediate) !timeoutId && func.apply(this, args);
43 | else clearTimeout(timeoutId);
44 |
45 | timeoutId = setTimeout(() => {
46 | if (isImmediate) timeoutId = undefined;
47 | else func.apply(this, args);
48 | }, delay);
49 | };
50 | }
51 | /**
52 | * Throttle function.(节流函数)
53 | * @param {Function} func Functions that require debounce.(需要防抖的函数)
54 | * @param {number} delay Delay,default 300ms.(延迟时间,默认300ms)
55 | * @example
56 | * const PerformanceUtils = require('boots-js/cjs/performance-utils'); // CommandJS
57 | * import PerformanceUtils from 'boots-js/performance-utils' // Es6 Module
58 | *
59 | * let num=0
60 | * function add(nv){
61 | * num+=nv
62 | * }
63 | * const throttleAdd=PerformanceUtils.throttle(add);
64 | * throttleAdd(1);
65 | * throttleAdd(1);
66 | * throttleAdd(1);
67 | * console.info(num); // 1
68 | */
69 | export function throttle(func: Function, delay: number = 300): Function {
70 | let previous: number = 0;
71 | return function (this: any, ...args: any) {
72 | const now = new Date().getTime();
73 | if (now - previous > delay) {
74 | func.apply(this, args);
75 | previous = now;
76 | }
77 | };
78 | }
79 | /**
80 | * Caching function calculation results.(缓存函数的计算结果)
81 | * @param {Function} func Functions that require cache.(需要缓存的函数)
82 | * @param {MemoizeOptions} options An object specifying the caching options.(指定缓存选项的对象)
83 | * - maxCacheSize: The maximum number of cached items allowed. Defaults to Infinity. (缓存的最大数量。默认为 Infinity)
84 | * - expirationTime: The time in milliseconds after which a cached item expires. Defaults to Infinity. (缓存项过期时间,以毫秒为单位。默认为 Infinity)
85 | * @example
86 | * const PerformanceUtils = require('boots-js/cjs/performance-utils'); // CommandJS
87 | * import PerformanceUtils from 'boots-js/performance-utils' // Es6 Module
88 | *
89 | * let count=0
90 | * function addCount(nv){
91 | * count+=nv;
92 | * return count
93 | * }
94 | * const memoizeAdd=PerformanceUtils.memoize(addCount,{
95 | * expirationTime:2000
96 | * })
97 | * memoizeAdd(1);memoizeAdd(1);memoizeAdd(1);
98 | * console.info(count) //1
99 | * memoizeAdd(2);memoizeAdd(2);memoizeAdd(2);
100 | * console.info(count) //3
101 | */
102 | export function memoize(
103 | func: Function,
104 | options: MemoizeOptions = {
105 | maxCacheSize: Infinity,
106 | expirationTime: Infinity,
107 | }
108 | ): Function {
109 | const cache: Map = new Map();
110 | const { maxCacheSize = Infinity, expirationTime = Infinity } = options;
111 |
112 | return function (this: any, ...args: any[]) {
113 | const key = args.map((arg) => argToStrKey(arg)).join('-');
114 | const cachedItem = cache.get(key);
115 | if (cachedItem) {
116 | if (Date.now() - cachedItem.timestamp > expirationTime) cache.delete(key);
117 | else return cachedItem.result;
118 | }
119 |
120 | const result = func.apply(this, args);
121 |
122 | const newCacheItem = {
123 | result,
124 | timestamp: Date.now(),
125 | };
126 |
127 | cache.set(key, newCacheItem);
128 |
129 | if (cache.size > maxCacheSize) {
130 | const oldestKey = cache.keys().next().value;
131 | cache.delete(oldestKey);
132 | }
133 |
134 | return result;
135 | };
136 | }
137 | /**
138 | * cache options object(缓存选项对象)
139 | */
140 | interface MemoizeOptions {
141 | maxCacheSize: number;
142 | expirationTime: number;
143 | }
144 | /**
145 | * cache item object(缓存项对象)
146 | */
147 | interface CacheItem {
148 | result: any;
149 | timestamp: number;
150 | }
151 |
152 | /**
153 | * @ignore
154 | */
155 | export default {
156 | debounce,
157 | throttle,
158 | memoize,
159 | };
160 |
--------------------------------------------------------------------------------
/src/priority-queue.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * priority queue.(优先级队列)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * let ascPriorityQueue = new BootsJS.PriorityQueue(true);
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const PriorityQueue = require('boots-js/cjs/priority-queue'); // CommandJS
12 | * import PriorityQueue from 'boots-js/priority-queue' // Es6 Module
13 | * let priorityQueue = new PriorityQueue();
14 | * ```
15 | * @module
16 | */
17 |
18 | /**
19 | * queue item object(队列项对象)
20 | */
21 | interface queueItem {
22 | /**
23 | * Enqueued elements(入队的元素)
24 | */
25 | element: any;
26 | /**
27 | * Priority(优先级)
28 | */
29 | priority: number;
30 | }
31 | export default class PriorityQueue {
32 | #isAsc: boolean;
33 | #heap: any[];
34 | /**
35 | * Constructor.(构造函数)
36 | * @param {*} isAscOrder Whether to sort in ascending order, the default is descending order.(是否升序排序,默认降序)
37 | * @example
38 | * let priorityQueue = new PriorityQueue();
39 | */
40 | constructor(isAscOrder: boolean = false) {
41 | this.#isAsc = isAscOrder;
42 | this.#heap = [];
43 | }
44 | /**
45 | * Current number of queue elements.(当前队列元素数量)
46 | * @example
47 | * let priorityQueue = new PriorityQueue();
48 | * priorityQueue.length //0
49 | */
50 | get length(): number {
51 | return this.#heap.length;
52 | }
53 | /**
54 | * Is the queue empty.(当前队列是否为空)
55 | * @example
56 | * let priorityQueue = new PriorityQueue();
57 | * priorityQueue.isEmpty //true
58 | */
59 | get isEmpty(): boolean {
60 | return this.length === 0;
61 | }
62 | /**
63 | * Get the top element of the queue.(获取队顶元素)
64 | * @example
65 | * let priorityQueue = new PriorityQueue();
66 | * priorityQueue.enqueue('1', 1)
67 | * priorityQueue.top //{element:'1',priority:1}
68 | */
69 | get top(): queueItem {
70 | return this.#heap[0];
71 | }
72 | /**
73 | * Add elements to the queue and sort them according to priority.(将元素添加到队列中并根据优先级排序)
74 | * @param {*} element 元素
75 | * @param {*} priority 优先级
76 | * @example
77 | * let priorityQueue = new PriorityQueue();
78 | * priorityQueue.enqueue('1', 1)
79 | * priorityQueue.enqueue({
80 | * a:'1',
81 | * b:'2'
82 | * }, 2)
83 | */
84 | enqueue(element: any, priority: number) {
85 | this.#heap.push({
86 | element: element,
87 | priority: priority,
88 | });
89 | this.#heapifyUp();
90 | }
91 | /**
92 | * Dequeue the highest or lowest priority element.(将优先级最高或最低的元素出队)
93 | * @example
94 | * let priorityQueue = new PriorityQueue();
95 | * priorityQueue.enqueue('1', 1)
96 | * priorityQueue.enqueue('1', 5)
97 | * priorityQueue.dequeue() //{element:'1',priority:5}
98 | */
99 | dequeue(): queueItem | undefined {
100 | if (this.isEmpty) return undefined;
101 | if (this.#heap.length === 1) return this.#heap.pop();
102 | let node = this.#heap[0];
103 | this.#heap[0] = this.#heap.pop();
104 | this.#heapifyDown();
105 | return node;
106 | }
107 |
108 | #swap(i: number, j: number) {
109 | let temp = this.#heap[i];
110 | this.#heap[i] = this.#heap[j];
111 | this.#heap[j] = temp;
112 | }
113 | #heapifyUp() {
114 | let index = this.#heap.length - 1;
115 | while (index > 0) {
116 | let rootIndex = Math.floor((index - 1) / 2);
117 | if (
118 | (this.#isAsc && this.#heap[index].priority < this.#heap[rootIndex].priority) ||
119 | (!this.#isAsc && this.#heap[index].priority > this.#heap[rootIndex].priority)
120 | ) {
121 | this.#swap(index, rootIndex);
122 | index = rootIndex;
123 | } else break;
124 | }
125 | }
126 | #heapifyDown() {
127 | let index = 0;
128 | while (index * 2 + 1 < this.#heap.length) {
129 | let minChildIndex = index * 2 + 1,
130 | rightChildIndex = index * 2 + 2;
131 | if (
132 | this.#heap[rightChildIndex] != undefined &&
133 | ((this.#isAsc && this.#heap[rightChildIndex].priority < this.#heap[minChildIndex].priority) ||
134 | (!this.#isAsc && this.#heap[rightChildIndex].priority > this.#heap[minChildIndex].priority))
135 | ) {
136 | minChildIndex = rightChildIndex;
137 | }
138 | if (
139 | (this.#isAsc && this.#heap[index].priority > this.#heap[minChildIndex].priority) ||
140 | (!this.#isAsc && this.#heap[index].priority < this.#heap[minChildIndex].priority)
141 | ) {
142 | this.#swap(index, minChildIndex);
143 | index = minChildIndex;
144 | } else break;
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/reg-rules.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Some common regular expression patterns.(一些常见的正则表达式规则)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.RegRules.IDCardRule.test('12948392023');
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
12 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
13 | * RegRules.IDCardRule.test('12948392023');
14 | * ```
15 | * @module
16 | */
17 |
18 | /**
19 | * Matches any digit from 0 to 9.(仅匹配数字的正则表达式)
20 | * @example
21 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
22 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
23 | *
24 | * RegRules.digitsRule.test('18523127384') //true
25 | */
26 | export const digitsRule = /^\d+$/;
27 | /**
28 | * Matches any letter, both lowercase and uppercase.(仅匹配字母的正则表达式)
29 | * @example
30 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
31 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
32 | *
33 | * RegRules.lettersRule.test('aaaa') //true
34 | */
35 | export const lettersRule = /^[a-zA-Z]+$/;
36 | /**
37 | * Matches any lowercase letter.(仅匹配小写字母的正则表达式)
38 | * @example
39 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
40 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
41 | *
42 | * RegRules.lowercaseLettersRule.test('aaaa') //true
43 | */
44 | export const lowercaseLettersRule = /^[a-z]+$/;
45 | /**
46 | * Matches any uppercase letter.(仅匹配大写字母的正则表达式)
47 | * @example
48 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
49 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
50 | *
51 | * RegRules.uppercaseLettersRule.test('ABC') //true
52 | */
53 | export const uppercaseLettersRule = /^[A-Z]+$/;
54 | /**
55 | * Matches any letter or digit.(仅匹配字母或数字的正则表达式)
56 | * @example
57 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
58 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
59 | *
60 | * RegRules.digitsAndLettersRule.test('134Afsv') //true
61 | */
62 | export const digitsAndLettersRule = /^[a-zA-Z0-9]+$/;
63 | /**
64 | * Matches chinese Identity Card Number.(匹配身份证号码的正则表达式)
65 | * @example
66 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
67 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
68 | *
69 | * RegRules.chineseIDCardRule.test('52052219830823283x') //true
70 | */
71 | export const chineseIDCardRule = /\b(^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}(\d|X|x)$)\b/;
72 | /**
73 | * Matches Chinese Mobile Phone Number.(匹配中国手机号码的正则表达式)
74 | * @example
75 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
76 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
77 | *
78 | * RegRules.chinesePhoneNumberRule.test('18523127384') //true
79 | */
80 | export const chinesePhoneNumberRule = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;
81 | /**
82 | * Matches email address.(匹配电子邮箱的正则表达式)
83 | * @example
84 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
85 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
86 | *
87 | * RegRules.emailRule.test('wjl@gmail.com') //true
88 | */
89 | export const emailRule = /^[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}$/;
90 | /**
91 | * Matches IP address.(匹配IP地址的正则表达式)
92 | * @example
93 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
94 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
95 | *
96 | * RegRules.IPAddressRule.test('192.168.0.1') //true
97 | */
98 | export const IPAddressRule = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
99 | /**
100 | * Matches file extensions of images.(匹配文件后缀为图片的正则表达式)
101 | * @example
102 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
103 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
104 | *
105 | * RegRules.imageRule.test('test.png') //true
106 | */
107 | export const imageRule = /\.(jpg|jpeg|png|gif|bmp|avif|webp|svg)$/;
108 | /**
109 | * Matches file extensions of audio.(匹配文件后缀为音频的正则表达式)
110 | * @example
111 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
112 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
113 | *
114 | * RegRules.audioRule.test('test.mp3') //true
115 | */
116 | export const audioRule = /\.(mp3|wav|flac|aac|ogg|wma)$/;
117 | /**
118 | * Matches file extensions of video.(匹配文件后缀为视频的正则表达式)
119 | * @example
120 | * const RegRules = require('boots-js/cjs/reg-rules'); // CommandJS
121 | * import RegRules from 'boots-js/reg-rules' // Es6 Module
122 | *
123 | * RegRules.videoRule.test('test.mp4') //true
124 | */
125 | export const videoRule = /\.(mp4|mov|avi|mkv|wmv|webm)$/;
126 |
127 | /**
128 | * @ignore
129 | */
130 | export default {
131 | videoRule,
132 | audioRule,
133 | imageRule,
134 | IPAddressRule,
135 | emailRule,
136 | chinesePhoneNumberRule,
137 | chineseIDCardRule,
138 | digitsAndLettersRule,
139 | uppercaseLettersRule,
140 | lowercaseLettersRule,
141 | lettersRule,
142 | digitsRule,
143 | };
144 |
--------------------------------------------------------------------------------
/src/string-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for processing strings.(一些处理字符串的方法)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.StringUtils.dashNameToUpperCamelCaseName('string-utils')
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const StringUtils = require('boots-js/cjs/string-utils'); // CommandJS
12 | * import StringUtils from 'boots-js/string-utils' // Es6 Module
13 | * StringUtils.dashNameToUpperCamelCaseName('string-utils')
14 | * ```
15 | * @module
16 | */
17 |
18 | /**
19 | * @enum Trim options enum.(去除空格选项枚举)
20 | * - `all`: Remove all spaces from string. (去除字符串中所有空格)
21 | * - `side`: Remove leading and trailing spaces from string. (去除字符串左右两端空格)
22 | * - `leading`: Remove spaces leading spaces from string. (去除字符串前导空格)
23 | * - `trailing`: Remove spaces trailing spaces from string. (去除字符串尾部空格)
24 | */
25 | export enum TrimOptions {
26 | /**
27 | * Remove all spaces from string. (去除字符串中所有空格)
28 | */
29 | all,
30 | /**
31 | * Remove leading and trailing spaces from string. (去除字符串左右两端空格)
32 | */
33 | side,
34 | /**
35 | * Remove spaces leading spaces from string. (去除字符串前导空格)
36 | */
37 | leading,
38 | /**
39 | * Remove spaces trailing spaces from string. (去除字符串尾部空格)
40 | */
41 | trailing,
42 | }
43 | /**
44 | * Convert dash naming to camel case naming.(短横线命名转大写驼峰命名)
45 | * @param {string} dashName The dash name that needs to be converted.(需要转换的短横线名称)
46 | * @example
47 | * const StringUtils = require('boots-js/cjs/string-utils'); // CommandJS
48 | * import StringUtils from 'boots-js/string-utils' // Es6 Module
49 | *
50 | * StringUtils.dashNameToUpperCamelCaseName('string-utils') //StringUtils
51 | */
52 | export function dashNameToUpperCamelCaseName(dashName: string): string {
53 | return dashName
54 | .replace(/-([a-zA-Z])/g, function (match, letter) {
55 | return letter.toUpperCase();
56 | })
57 | .replace(/^[a-z]/, function (letter) {
58 | return letter.toUpperCase();
59 | });
60 | }
61 | /**
62 | * Convert camel case naming to dash naming.(驼峰命名转短横线命名)
63 | * @param {string} camelCaseName The camel case name that needs to be converted.(需要转换的驼峰命名)
64 | * @example
65 | * const StringUtils = require('boots-js/cjs/string-utils'); // CommandJS
66 | * import StringUtils from 'boots-js/string-utils' // Es6 Module
67 | *
68 | * StringUtils.camelCaseNameToDashName('StringUtils') //string-utils
69 | */
70 | export function camelCaseNameToDashName(camelCaseName: string): string {
71 | return camelCaseName
72 | .replace(/^[A-Z]/, function (letter) {
73 | return letter.toLowerCase();
74 | })
75 | .replace(/[A-Z]/g, function (letter) {
76 | return '-' + letter.toLowerCase();
77 | });
78 | }
79 | /**
80 | * Remove spaces from string.(去除字符串中的空格)
81 | * @param {string} str A string that needs to be remove spaces.(需要去除空格的字符串)
82 | * @param {TrimOptions} trimOptions Trim options,default is all.(去除空格选项,默认为all)
83 | * - `all`: Remove all spaces from string. (去除字符串中所有空格)
84 | * - `side`: Remove leading and trailing spaces from string. (去除字符串左右两端空格)
85 | * - `leading`: Remove spaces leading spaces from string. (去除字符串前导空格)
86 | * - `trailing`: Remove spaces trailing spaces from string. (去除字符串尾部空格)
87 | * @example
88 | * const StringUtils = require('boots-js/cjs/string-utils'); // CommandJS
89 | * import StringUtils from 'boots-js/string-utils' // Es6 Module
90 | *
91 | * StringUtils.trim(' String Utils ') //StringUtils
92 | * StringUtils.trim(' StringUtils ',StringUtils.trimOptions.side) //StringUtils
93 | * StringUtils.trim('StringUtils ',StringUtils.trimOptions.trailing) //StringUtils
94 | * StringUtils.trim(' StringUtils',StringUtils.trimOptions.leading) //StringUtils
95 | */
96 | export function trim(str: string, trimOptions: TrimOptions = TrimOptions.all): string {
97 | if (!checkIsString(str)) {
98 | console.warn(`${str}非字符串!`);
99 | return str;
100 | }
101 | switch (trimOptions) {
102 | case TrimOptions.all:
103 | return str.replace(/\s/g, '');
104 | case TrimOptions.side:
105 | return str.trim();
106 | case TrimOptions.leading:
107 | return str.replace(/^\s+/, '');
108 | case TrimOptions.trailing:
109 | return str.replace(/\s+$/, '');
110 | default:
111 | throw new Error('Please pass in the correct TrimOptions parameters');
112 | }
113 | }
114 | function checkIsString(str: any): boolean {
115 | if (typeof str === 'string') {
116 | return true;
117 | }
118 |
119 | return false;
120 | }
121 |
122 | /**
123 | * @ignore
124 | */
125 | export default {
126 | TrimOptions,
127 | dashNameToUpperCamelCaseName,
128 | camelCaseNameToDashName,
129 | trim,
130 | };
131 |
--------------------------------------------------------------------------------
/src/tree-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * utils for working with tree.(一些处理树的工具)
3 | *
4 | * ```ts
5 | * // -------- Global Import(全局引入)
6 | * const BootsJS = require('boots-js/cjs'); // CommandJS
7 | * import BootsJS from 'boots-js' // Es6 Module
8 | * BootsJS.TreeUtils.tree2Array(tree,'childList',options)
9 | *
10 | * // -------- Import on Demand(按需引入)
11 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
12 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
13 | * TreeUtils.tree2Array(tree,'childList',options)
14 | * ```
15 | * @module
16 | */
17 |
18 | /**
19 | * Convert tree to array.(把树转换成数组)
20 | * @param {Array} treeList Given a list of trees.(给定树的列表)
21 | * @param {string} childListAttributeName The attribute name of the child node stored in the tree.(树中存放子节点的属性名)
22 | * @param {Tree2ArrayOptions} options Setting options.(设置选项)
23 | * - isGenerateLevel: Whether to generate level,default false.(是否生成层级)
24 | * - generateLevelAttributeName: Specify the attribute name of the generated level.(指定生成的层级的属性名称)
25 | * - isGenerateParentID: Whether to generate parent node ID,default false.(是否生成父节点ID)
26 | * - generateParentIDAttributeName: Specify the attribute name of the generated parent node ID.(指定生成的父节点ID的属性名称)
27 | * - nodeIDAttributeName: Attribute name of node ID.(节点ID的属性名称)
28 | * - deleteAttributeList: pecify the attribute in the node to be deleted.(指定删除节点中的属性)
29 | * @example
30 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
31 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
32 | *
33 | * const tree = {
34 | * name: '中国',
35 | * code: '0',
36 | * childList: [
37 | * {
38 | * name: '重庆',
39 | * code: '01',
40 | * },
41 | * {
42 | * name: '四川',
43 | * code: '02',
44 | * },
45 | * {
46 | * name: '广东',
47 | * code: '03',
48 | * },
49 | * ]
50 | * }
51 | * let arr = TreeUtils.tree2Array([tree], 'childList', {
52 | * isGenerateLevel: true,
53 | * generateLevelAttributeName:'level',
54 | * isGenerateParentID: true,
55 | * generateParentIDAttributeName: 'parentCode',
56 | * nodeIDAttributeName: 'code',
57 | * deleteAttributeList: ['childList']
58 | * })
59 | * console.info(arr)
60 | * [
61 | * { name: '中国', code: '0' , level:0 },
62 | * { name: '重庆', code: '01', level:1 , parentCode: '0' },
63 | * { name: '四川', code: '02', level:1 , parentCode: '0' },
64 | * { name: '广东', code: '03', level:1 , parentCode: '0' },
65 | * ]
66 | */
67 | export function tree2Array(treeList: Array, childListAttributeName: string, options: Tree2ArrayOptions = tree2ArrayDefaultOptions): Array {
68 | if (!Array.isArray(treeList)) {
69 | console.warn(`${treeList} is not an Array!`);
70 | return treeList;
71 | }
72 | let {
73 | isGenerateLevel = false,
74 | generateLevelAttributeName = 'level',
75 | isGenerateParentID = false,
76 | generateParentIDAttributeName = 'parentID',
77 | nodeIDAttributeName = 'id',
78 | deleteAttributeList = [],
79 | } = options,
80 | quene = [...treeList],
81 | result = [],
82 | level = 0;
83 | while (quene.length > 0) {
84 | let length = quene.length;
85 | while (length-- > 0) {
86 | const node = quene.pop(),
87 | childList = node[childListAttributeName];
88 | if (Array.isArray(childList)) {
89 | childList.forEach((val: any) => {
90 | if (isGenerateParentID) val[generateParentIDAttributeName] = node[nodeIDAttributeName];
91 | quene.unshift(val);
92 | });
93 | }
94 | if (isGenerateLevel) node[generateLevelAttributeName] = level;
95 | if (Array.isArray(deleteAttributeList)) {
96 | deleteAttributeList.forEach((val) => {
97 | delete node[val];
98 | });
99 | }
100 | result.push(node);
101 | }
102 | level++;
103 | }
104 | return result;
105 | }
106 | /**
107 | * Convert array to tree.(把数组转换为树)
108 | * @param {Array} arr Given an array.(给定数组)
109 | * @param {string} nodeIDAttributeName Attribute name of node ID.(节点ID的属性名称)
110 | * @param {string} parentIDAttributeName Attribute name of parent node ID.(父节点ID的属性名称)
111 | * @param {string} generateChildListAttributeName Specify the attribute name of the child node in the tree.(指定树中存放子节点的属性名)
112 | * @param {Function} judgeRootNodeFunction Function to determine whether a node is the root node.(判断节点是否为根节点的方法)
113 | * @example
114 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
115 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
116 | *
117 | * const arr = [
118 | * { name: '中国', code: '0' , level:0 },
119 | * { name: '重庆', code: '01', level:1 , parentCode: '0' },
120 | * { name: '四川', code: '02', level:1 , parentCode: '0' },
121 | * { name: '广东', code: '03', level:1 , parentCode: '0' },
122 | * ]
123 | * let genTree = TreeUtils.array2Tree(arr, 'code', 'parentCode', 'childList', (node) => {
124 | * return !('parentCode' in node)
125 | * })
126 | * console.info(genTree)
127 | * [
128 | * {
129 | * name: '中国',
130 | * code: '0',
131 | * level: 0,
132 | * childList: [
133 | * { name: '重庆', code: '01', level:1, parentCode: '0', childList: [] },
134 | * { name: '四川', code: '02', level:1, parentCode: '0', childList: [] },
135 | * { name: '广东', code: '03', level:1, parentCode: '0', childList: [] }
136 | * ]
137 | * }
138 | * ]
139 | */
140 | export function array2Tree(
141 | arr: Array,
142 | nodeIDAttributeName: string,
143 | parentIDAttributeName: string,
144 | generateChildListAttributeName: string,
145 | judgeRootNodeFunction: (node: any) => boolean
146 | ): Array {
147 | const result: Array = [],
148 | map = new Map();
149 | if (!Array.isArray(arr)) {
150 | console.warn(`${arr} is not an Array!`);
151 | return [];
152 | }
153 | arr.forEach((val) => {
154 | val[generateChildListAttributeName] = map.has(val[nodeIDAttributeName]) ? map.get(val[nodeIDAttributeName])[generateChildListAttributeName] : [];
155 | if (judgeRootNodeFunction(val)) result.push(val);
156 |
157 | if (!map.has(val[parentIDAttributeName])) {
158 | let obj: any = {};
159 | obj[generateChildListAttributeName] = [];
160 | map.set(val[parentIDAttributeName], obj);
161 | }
162 |
163 | map.get(val[parentIDAttributeName])[generateChildListAttributeName].push(val);
164 | map.set(val[nodeIDAttributeName], val);
165 | });
166 | return result;
167 | }
168 | /**
169 | * Get all child nodes of a node.(获取节点的所有子节点)
170 | * @param {Array} treeList Given a list of trees.(给定树的列表)
171 | * @param {string} nodeIDAttributeName Attribute name of node ID.(节点ID的属性名称)
172 | * @param {string} nodeID Specify the ID of the node whose child nodes need to be obtained.(指定需要获取其子节点的节点的ID)
173 | * @param {string} childListAttributeName The attribute name of the child node stored in the tree.(树中存放子节点的属性名)
174 | * @example
175 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
176 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
177 | *
178 | * const tree = {
179 | * name: '中国',
180 | * code: '0',
181 | * childList: [
182 | * {
183 | * name: '重庆',
184 | * code: '01',
185 | * },
186 | * {
187 | * name: '四川',
188 | * code: '02',
189 | * },
190 | * {
191 | * name: '广东',
192 | * code: '03',
193 | * },
194 | * ]
195 | * }
196 | * let arr = TreeUtils.getChildList([tree], 'code', '0', 'childList')
197 | * console.info(arr)
198 | * [
199 | * { name: '重庆', code: '01' },
200 | * { name: '四川', code: '02' },
201 | * { name: '广东', code: '03' },
202 | * ]
203 | */
204 | export function getChildList(treeList: Array, nodeIDAttributeName: string, nodeID: string, childListAttributeName: string): Array {
205 | if (!Array.isArray(treeList)) {
206 | console.warn(`${treeList} is not an Array!`);
207 | return treeList;
208 | }
209 | let quene = [...treeList];
210 | while (quene.length > 0) {
211 | const node = quene.pop(),
212 | childList = node[childListAttributeName];
213 | if (node[nodeIDAttributeName] === nodeID) return childList;
214 | if (Array.isArray(childList)) {
215 | childList.forEach((val: any) => {
216 | quene.unshift(val);
217 | });
218 | }
219 | }
220 | return [];
221 | }
222 | /**
223 | * Returns all nodes that meet the condition.(返回满足条件的所有节点)
224 | * @param {Array} treeList Given a list of trees.(给定树的列表)
225 | * @param {string} childListAttributeName The attribute name of the child node stored in the tree.(树中存放子节点的属性名)
226 | * @param {Function} filterFunction Function to filter nodes.(筛选节点的函数)
227 | * @param {Tree2ArrayOptions} options Setting options.(设置选项)
228 | * - isGenerateLevel: Whether to generate level,default false.(是否生成层级)
229 | * - generateLevelAttributeName: Specify the attribute name of the generated level.(指定生成的层级的属性名称)
230 | * - isGenerateParentID: Whether to generate parent node ID,default false.(是否生成父节点ID)
231 | * - generateParentIDAttributeName: Specify the attribute name of the generated parent node ID.(指定生成的父节点ID的属性名称)
232 | * - nodeIDAttributeName: Attribute name of node ID.(节点ID的属性名称)
233 | * - deleteAttributeList: pecify the attribute in the node to be deleted.(指定删除节点中的属性)
234 | * @example
235 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
236 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
237 | *
238 | * const tree = {
239 | * name: '中国',
240 | * code: '0',
241 | * level: 0,
242 | * childList: [
243 | * { name: '重庆', code: '01', level:1, parentCode: '0', childList: [] },
244 | * { name: '四川', code: '02', level:1, parentCode: '0', childList: [] },
245 | * { name: '广东', code: '03', level:1, parentCode: '0', childList: [] }
246 | * ]
247 | * }
248 | * let arr = TreeUtils.filter([tree], 'childList', (obj) => {
249 | * return obj.parentCode === '0'
250 | * })
251 | * console.info(arr)
252 | * [
253 | * { name: '重庆', code: '01', level:1 , parentCode: '0', childList: [] },
254 | * { name: '四川', code: '02', level:1 , parentCode: '0', childList: [] },
255 | * { name: '广东', code: '03', level:1 , parentCode: '0', childList: [] },
256 | * ]
257 | */
258 | export function filter(
259 | treeList: Array,
260 | childListAttributeName: string,
261 | filterFunction: (value: any, index: number, array: any[]) => boolean,
262 | options: Tree2ArrayOptions = tree2ArrayDefaultOptions
263 | ) {
264 | return tree2Array(treeList, childListAttributeName, options).filter(filterFunction);
265 | }
266 | /**
267 | * Find the path of a node.(查找某节点的路径)
268 | * @param {Array} treeList Given a list of trees.(给定树的列表)
269 | * @param {string} nodeIDAttributeName Attribute name of node ID.(节点ID的属性名称)
270 | * @param {string} nodeID Specify the node ID whose path needs to be obtained.(指定需要获取路径的节点ID)
271 | * @param {string} childListAttributeName The attribute name of the child node stored in the tree.(树中存放子节点的属性名)
272 | * @example
273 | * const TreeUtils = require('boots-js/cjs/tree-utils'); // CommandJS
274 | * import TreeUtils from 'boots-js/tree-utils' // Es6 Module
275 | *
276 | * const tree = {
277 | * name: '中国',
278 | * code: '0',
279 | * level: 0,
280 | * childList: [
281 | * { name: '重庆', code: '01', level:1, parentCode: '0', childList: [] },
282 | * { name: '四川', code: '02', level:1, parentCode: '0', childList: [] },
283 | * { name: '广东', code: '03', level:1, parentCode: '0', childList: [] }
284 | * ]
285 | * }
286 | * let path = TreeUtils.findPath([tree],'code','03','childList')
287 | * console.info(path)
288 | * [
289 | * {
290 | * name: '中国',
291 | * code: '0',
292 | * level: 0,
293 | * childList: [ [Object], [Object], [Object] ]
294 | * },
295 | * { name: '广东', code: '03', parentCode: '0', level: 1, childList: [] }
296 | * ]
297 | *
298 | *
299 | */
300 | export function findPath(treeList: Array, nodeIDAttributeName: string, nodeID: string, childListAttributeName: string): Array {
301 | if (!Array.isArray(treeList)) {
302 | console.warn(`${treeList} is not an Array!`);
303 | return treeList;
304 | }
305 | function DFS(nodeList: Array, pathList: Array): Array {
306 | if (!Array.isArray(nodeList)) return [];
307 | for (let node of nodeList) {
308 | if (node[nodeIDAttributeName] === nodeID) return [...pathList, node];
309 | const result = DFS(node[childListAttributeName], [...pathList, node]);
310 | if (result?.length > 0) return result;
311 | }
312 | return [];
313 | }
314 | return DFS(treeList, []);
315 | }
316 | /**
317 | * Tree to array setting options.(树转数组设置选项)
318 | */
319 | interface Tree2ArrayOptions {
320 | /**
321 | * Whether to generate level.(是否生成层级)
322 | */
323 | isGenerateLevel: boolean;
324 | /**
325 | * Specify the attribute name of the generated level.(指定生成的层级的属性名称)
326 | */
327 | generateLevelAttributeName: string;
328 | /**
329 | * Whether to generate parent node ID.(是否生成父节点ID)
330 | */
331 | isGenerateParentID: boolean;
332 | /**
333 | * Specify the attribute name of the generated parent node ID.(指定生成的父节点ID的属性名称)
334 | */
335 | generateParentIDAttributeName: string;
336 | /**
337 | * Attribute name of node ID.(节点ID的属性名称)
338 | */
339 | nodeIDAttributeName: string;
340 | /**
341 | * Specify the attribute in the node to be deleted.(指定删除节点中的属性)
342 | */
343 | deleteAttributeList: Array;
344 | }
345 | const tree2ArrayDefaultOptions: Tree2ArrayOptions = {
346 | isGenerateLevel: false,
347 | generateLevelAttributeName: 'level',
348 | isGenerateParentID: false,
349 | generateParentIDAttributeName: 'parentID',
350 | nodeIDAttributeName: 'id',
351 | deleteAttributeList: [],
352 | };
353 |
354 | /**
355 | * @ignore
356 | */
357 | export default {
358 | tree2Array,
359 | array2Tree,
360 | getChildList,
361 | filter,
362 | findPath,
363 | };
364 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "ESNext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "esModuleInterop": true,
8 | "skipLibCheck": true,
9 | "forceConsistentCasingInFileNames": true
10 | },
11 | "include": [
12 | "src"
13 | ],
14 | "exclude": [
15 | "node_modules",
16 | "dist"
17 | ]
18 | }
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "hideGenerator": true,
3 | "entryPoints": [
4 | "src/*"
5 | ],
6 | "exclude": [
7 | "**/index.ts"
8 | ],
9 | "out": "docs"
10 | }
--------------------------------------------------------------------------------
/utils/find-export-files.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | module.exports=function findIndexFiles(dirname,dir) {
5 | const indexFiles = {
6 |
7 | };
8 | const regex = /\.ts$/,directory=path.resolve(dirname,dir),excludeFile=[];
9 |
10 | function traverse(currentDir) {
11 | const files = fs.readdirSync(currentDir);
12 | files.forEach(file => {
13 | const filePath = path.join(currentDir, file);
14 | const stat = fs.statSync(filePath);
15 |
16 | if (stat.isDirectory()) {
17 | traverse(filePath);
18 | } else if (regex.test(file)&&!excludeFile.includes(file)) {
19 | indexFiles[file.split('.')[0]]=('./'+path.relative(dirname, filePath))
20 | }
21 | });
22 | }
23 |
24 | traverse(directory);
25 | return indexFiles;
26 | }
--------------------------------------------------------------------------------
/utils/get-library-name.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports=function getLibraryName(filePath) {
4 | let pathName=path.basename(filePath, '.ts')
5 | return pathName==='index'?'BootsJS':pathName
6 | .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
7 | .replace(/^[a-z]/, (letter) => letter.toUpperCase());
8 | }
--------------------------------------------------------------------------------