├── .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 | # ![logo](https://github.com/JunLiangWangX/BootsJS/blob/main/resource/logo.png?raw=true) 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 | # ![logo](https://github.com/JunLiangWangX/BootsJS/blob/main/resource/logo.png?raw=true) 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 | } --------------------------------------------------------------------------------