diff --git a/.eslintignore b/.eslintignore
index 38702545..1902a282 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -5,5 +5,8 @@
/public/
/config/
/repo/
-!.vuepress
+
+!/docs/.vuepress/
+/docs/.vuepress/dist/*
+
barcode/
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 0de9d04a..4bc45458 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,5 +1,132 @@
# 更新日志
+### [1.3.143](https://github.com/novlan1/t-comm/compare/v1.3.142...v1.3.143) (2025-01-07)
+
+
+### Features 🎉
+
+* **lint:** stylelint 不处理vue文件 ([84c536f](https://github.com/novlan1/t-comm/commit/84c536f4b11072e6b6ad1dcdcc07964e36d362fc))
+
+### [1.3.142](https://github.com/novlan1/t-comm/compare/v1.3.141...v1.3.142) (2025-01-07)
+
+
+### Features 🎉
+
+* **lint:** 优化全量模式 ([c22dfc0](https://github.com/novlan1/t-comm/commit/c22dfc02d481446817daf67aad4a951eb90ffa09))
+
+### [1.3.141](https://github.com/novlan1/t-comm/compare/v1.3.140...v1.3.141) (2025-01-07)
+
+
+### Features 🎉
+
+* **lint:** 支持全量模式 ([d123dfe](https://github.com/novlan1/t-comm/commit/d123dfe4188ab287f3e7a787208c2073f029af4f))
+
+### [1.3.140](https://github.com/novlan1/t-comm/compare/v1.3.139...v1.3.140) (2025-01-06)
+
+
+### Features 🎉
+
+* **lint:** add checkLint ([6fa2e55](https://github.com/novlan1/t-comm/commit/6fa2e5540354e94afe8e5e6e784a6332b21996d0))
+
+### [1.3.139](https://github.com/novlan1/t-comm/compare/v1.3.138...v1.3.139) (2024-12-25)
+
+
+### Features 🎉
+
+* **csv:** add generateCSVData ([367cc49](https://github.com/novlan1/t-comm/commit/367cc4954bebc5ccdb7ecf841f183b32dae5c091))
+
+### [1.3.138](https://github.com/novlan1/t-comm/compare/v1.3.137...v1.3.138) (2024-12-24)
+
+
+### Features 🎉
+
+* **csv:** add generateCSV ([d3d68e1](https://github.com/novlan1/t-comm/commit/d3d68e1612a224f7ef6614f5ae959643be4a731d))
+
+### [1.3.137](https://github.com/novlan1/t-comm/compare/v1.3.136...v1.3.137) (2024-12-24)
+
+
+### Documentation 📖
+
+* update docs ([8956a30](https://github.com/novlan1/t-comm/commit/8956a309c5f2c42d065d0ae134fb9bc41da078ee))
+
+
+### Features 🎉
+
+* **slice-object:** 边界处理 ([12d0585](https://github.com/novlan1/t-comm/commit/12d05853cd559b1dc7ee229e20261cffac4a380f))
+
+### [1.3.136](https://github.com/novlan1/t-comm/compare/v1.3.135...v1.3.136) (2024-12-18)
+
+
+### Features 🎉
+
+* **debounce:** add debug ([f58f48c](https://github.com/novlan1/t-comm/commit/f58f48ccfe68d07260b7f07721f6658a8e7fc31e))
+
+### [1.3.135](https://github.com/novlan1/t-comm/compare/v1.3.134...v1.3.135) (2024-12-18)
+
+### [1.3.134](https://github.com/novlan1/t-comm/compare/v1.3.133...v1.3.134) (2024-12-18)
+
+
+### Features 🎉
+
+* **debounce:** update type ([b3c4e63](https://github.com/novlan1/t-comm/commit/b3c4e631a727a44d6822b983177635f0bf8e668d))
+
+### [1.3.133](https://github.com/novlan1/t-comm/compare/v1.3.132...v1.3.133) (2024-12-18)
+
+
+### Features 🎉
+
+* **debounce:** add debounceRun ([cfe2740](https://github.com/novlan1/t-comm/commit/cfe27400fc16d6e6e876d308d66fc082a7ac279f))
+
+### [1.3.132](https://github.com/novlan1/t-comm/compare/v1.3.131...v1.3.132) (2024-12-18)
+
+
+### Features 🎉
+
+* **uni-hook:** add debug in invoke ([1118bfa](https://github.com/novlan1/t-comm/commit/1118bfa62be713a7a519e877fb148b5f5b01c625))
+
+### [1.3.131](https://github.com/novlan1/t-comm/compare/v1.3.130...v1.3.131) (2024-12-18)
+
+
+### Features 🎉
+
+* **uni-hook:** use addInterceptor ([889df7d](https://github.com/novlan1/t-comm/commit/889df7d8dab31c1e99694e40b2486d764e54224b))
+
+### [1.3.130](https://github.com/novlan1/t-comm/compare/v1.3.129...v1.3.130) (2024-12-18)
+
+
+### Features 🎉
+
+* update dotenv ([e570221](https://github.com/novlan1/t-comm/commit/e5702211621d2f142c72ce585d260be69b7d974a))
+
+### [1.3.129](https://github.com/novlan1/t-comm/compare/v1.3.128...v1.3.129) (2024-12-18)
+
+
+### Features 🎉
+
+* **startUniProject:** 支持可选参数 ([383b8ec](https://github.com/novlan1/t-comm/commit/383b8ec12d44bbc8b6172e6a557aa85204883e06))
+
+### [1.3.128](https://github.com/novlan1/t-comm/compare/v1.3.127...v1.3.128) (2024-12-18)
+
+
+### Features 🎉
+
+* **dotenv:** add loadDotenv ([e2c9fc6](https://github.com/novlan1/t-comm/commit/e2c9fc6ba317197a87a9afa48ae4c9d2f5058596))
+
+### [1.3.127](https://github.com/novlan1/t-comm/compare/v1.3.126...v1.3.127) (2024-12-16)
+
+
+### Documentation 📖
+
+* update docs ([f25e150](https://github.com/novlan1/t-comm/commit/f25e1502738fb25909789ef5a8b54e617967d735))
+* update docs ([12b9f1d](https://github.com/novlan1/t-comm/commit/12b9f1d00300074e01975e282aa8d3e81ef101cd))
+* update docs ([ad95eff](https://github.com/novlan1/t-comm/commit/ad95eff2beb8da645999e1a4e02f37a06ad1590c))
+* update docs ([2ab440a](https://github.com/novlan1/t-comm/commit/2ab440a4cbd53c70861c672f2d8182582231dce3))
+
+
+### Features 🎉
+
+* **node-command:** support more options ([ef67c06](https://github.com/novlan1/t-comm/commit/ef67c06dfe5bad65e3a8f946d15a351216074708))
+
### [1.3.126](https://github.com/novlan1/t-comm/compare/v1.3.125...v1.3.126) (2024-12-13)
diff --git a/docs/zh/csv.md b/docs/zh/csv.md
new file mode 100644
index 00000000..6d9324b7
--- /dev/null
+++ b/docs/zh/csv.md
@@ -0,0 +1,32 @@
+[[toc]]
+
+
引入
+
+```ts
+import { generateCSV } from 't-comm';
+
+// or
+import { generateCSV} from 't-comm/lib/csv/index';
+```
+
+
+## `generateCSV(dataList)`
+
+
+**描述**:生成 CSV 文件内容,可以用于 fs.writeFileSync 输出
+第一行为表头
+
+**参数**:
+
+
+| 参数名 | 类型 | 描述 |
+| --- | --- | --- |
+| dataList | Array<Array<string>>
| 二维数据列表
|
+
+**返回**: 生成的字符串
+
+**示例**
+
+```ts
+generateCSV([['a','b'], ['1', '2']]);
+```
diff --git a/docs/zh/daily-merge.md b/docs/zh/daily-merge.md
index b8e7f334..6defcac5 100644
--- a/docs/zh/daily-merge.md
+++ b/docs/zh/daily-merge.md
@@ -14,6 +14,19 @@ import { dailyMerge} from 't-comm/lib/daily-merge/index';
**描述**:每日合并
+
+- 获取昨天有活跃的分支
+- 对于每个分支,进行合并并推送
+
+- 清理 Git 环境
+- 切到主分支,并拉最新代码
+- 切到当前分支,拉最新代码
+- 尝试执行 git merge
+- 对比 merge 前后的 commit 信息是否相同,作为判断 merge 是否成功的依据
+
+
+- 发送机器人消息
+
**参数**:
@@ -39,7 +52,6 @@ import { dailyMerge} from 't-comm/lib/daily-merge/index';
dailyMerge({
webhookUrl: 'xx',
appName: 'xx',
- projectId: 'xx',
devRoot: 'xx',
baseUrl: 'xx',
diff --git a/docs/zh/debounce.md b/docs/zh/debounce.md
index 5479b952..59ca01e1 100644
--- a/docs/zh/debounce.md
+++ b/docs/zh/debounce.md
@@ -3,14 +3,35 @@
引入
```ts
-import { debounce } from 't-comm';
+import { debounceRun, debounce } from 't-comm';
// or
-import { debounce} from 't-comm/lib/debounce/index';
+import { debounceRun, debounce} from 't-comm/lib/debounce/index';
```
-## `debounce(fn, time)`
+## `debounceRun`
+
+
+**描述**:不用生成中间函数的防抖
+
+**参数**:
+
+
+
+**示例**
+
+```ts
+debounceRun(func, args, {
+ funcKey: 'funcKey',
+ wait: 500, // 默认 500
+ throttle: false, // 是否是节流,默认 false
+ immediate: true, // 是否立即执行,默认 true
+})
+``
+
+
+## `debounce(fn, time, immediate)`
**描述**:防抖,场景:搜索
@@ -24,6 +45,7 @@ import { debounce} from 't-comm/lib/debounce/index';
| --- | --- | --- |
| fn | function
| 主函数
|
| time | number
| 间隔时间,单位 ms
|
+| immediate | boolean
| 是否立即执行,默认 false
|
**返回**: 闭包函数
@@ -34,4 +56,6 @@ function count() {
console.log('xxxxx')
}
window.onscroll = debounce(count, 500)
+
+window.onscroll = debounce(count, 500, true)
```
diff --git a/docs/zh/dotenv.md b/docs/zh/dotenv.md
new file mode 100644
index 00000000..65b0eeb7
--- /dev/null
+++ b/docs/zh/dotenv.md
@@ -0,0 +1,38 @@
+[[toc]]
+
+引入
+
+```ts
+import { loadDotenv } from 't-comm';
+
+// or
+import { loadDotenv} from 't-comm/lib/dotenv/index';
+```
+
+
+## `loadDotenv(file, param)`
+
+
+**描述**:用 dotenv-expand 加载环境变量
+
+**参数**:
+
+
+| 参数名 | 描述 |
+| --- | --- |
+| file | 文件路径,默认 .env.local
|
+| param | 参数
|
+
+
+
+**示例**
+
+```ts
+loadEnv();
+
+loadEnv('.env');
+
+loadEnv('.env.local', {
+ debug: false, // 是否打印日志,默认 true
+});
+```
diff --git a/docs/zh/node.md b/docs/zh/node.md
index f5dba546..39fd14a9 100644
--- a/docs/zh/node.md
+++ b/docs/zh/node.md
@@ -179,7 +179,7 @@ import {
| --- | --- | --- |
| command | string
| 命令
|
| root | string
| 执行命令的目录
|
-| stdio | string
| 结果输出,默认为 pipe
|
+| stdio | string
\| object
| 结果输出,默认为 pipe
|
**返回**: string
diff --git a/docs/zh/router.md b/docs/zh/router.md
index e9afb2e7..8f1b2187 100644
--- a/docs/zh/router.md
+++ b/docs/zh/router.md
@@ -6,14 +6,16 @@
import {
findRouteName,
getRouterFuncPath,
- getH5CurrentUrl
+ getH5CurrentUrl,
+ uniHookRouter
} from 't-comm';
// or
import {
findRouteName,
getRouterFuncPath,
- getH5CurrentUrl
+ getH5CurrentUrl,
+ uniHookRouter
} from 't-comm/lib/router/index';
```
@@ -79,3 +81,30 @@ console.log('name', name);
```ts
getH5CurrentUrl(this.$route);
```
+
+
+## `uniHookRouter()`
+
+
+**描述**:拦截路由
+
+**参数**:
+
+
+
+**示例**
+
+```ts
+uniHookRouter({
+ navigateToHooks: [
+ () => console.log('1')
+ ],
+ navigateBackHooks: [
+ () => console.log('2')
+ ],
+ redirectToHooks: [
+ () => console.log('3')
+ ],
+ debug: true,
+})
+```
diff --git a/docs/zh/uni-app.md b/docs/zh/uni-app.md
index b3e2c1ab..f37647cc 100644
--- a/docs/zh/uni-app.md
+++ b/docs/zh/uni-app.md
@@ -3,10 +3,10 @@
引入
```ts
-import { getRouteLeaveCache } from 't-comm';
+import { getRouteLeaveCache, startUniProject } from 't-comm';
// or
-import { getRouteLeaveCache} from 't-comm/lib/uni-app/index';
+import { getRouteLeaveCache, startUniProject} from 't-comm/lib/uni-app/index';
```
@@ -27,3 +27,28 @@ import { getRouteLeaveCache} from 't-comm/lib/uni-app/index';
**返回**: 返回对象,包含 beforeRouteLeave 和 activated 方法
+
+
+## `startUniProject(options)`
+
+
+**描述**:启动 uni-app 项目
+
+**参数**:
+
+
+| 参数名 | 描述 |
+| --- | --- |
+| options | 参数
|
+
+
+
+**示例**
+
+```ts
+startUniProject();
+
+startUniProject({
+ debug: false, // 默认为 true,会打印参数
+})
+```
diff --git a/package.json b/package.json
index adeb23b8..547700fb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "t-comm",
- "version": "1.3.126",
+ "version": "1.3.143",
"description": "专业、稳定、纯粹的工具库",
"main": "lib/index.js",
"module": "lib/index.esm.js",
@@ -146,4 +146,4 @@
"postchangelog": "node script/docs-jsdoc/change-log"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/base/object/slice.ts b/src/base/object/slice.ts
index 298e27c5..5ab53d6c 100644
--- a/src/base/object/slice.ts
+++ b/src/base/object/slice.ts
@@ -1,4 +1,7 @@
export function sliceObject(info: Record, max: number) {
+ if (!info) {
+ return {};
+ }
const keys = Object.keys(info);
if (keys.length < max) {
return info;
diff --git a/src/base/regexp/regexp.ts b/src/base/regexp/regexp.ts
index bf036c2e..d9d65fa2 100644
--- a/src/base/regexp/regexp.ts
+++ b/src/base/regexp/regexp.ts
@@ -38,6 +38,8 @@ const PRE_RELEASE_VERSION = /\d+\.\d+\.\d+-(\w+).\d+/;
*/
export function getPreReleaseTag(version: string) {
const match = version.match(PRE_RELEASE_VERSION);
- if (!match || !match[1]) return '';
+ if (!match?.[1]) {
+ return '';
+ }
return match[1];
}
diff --git a/src/csv/csv.ts b/src/csv/csv.ts
new file mode 100644
index 00000000..84851fe0
--- /dev/null
+++ b/src/csv/csv.ts
@@ -0,0 +1,65 @@
+/**
+ * 生成 CSV 文件内容,可以用于 fs.writeFileSync 输出
+ *
+ * 第一行为表头
+ * @param {Array>} dataList 二维数据列表
+ * @returns 生成的字符串
+ * @example
+ *
+ * ```ts
+ * generateCSV([['a','b'], ['1', '2']]);
+ * ```
+ */
+export function generateCSV(dataList: Array>) {
+ const result: string[] = [];
+ if (!dataList?.[0].length) {
+ return '';
+ }
+
+ dataList.forEach((line, lineIndex) => {
+ line.forEach((text, index) => {
+ if (lineIndex === 0 && index === 0) {
+ result.push(`\ufeff${text},`);
+ } else if (index === line.length - 1) {
+ result.push(`${text}\n`);
+ } else {
+ result.push(`${text},`);
+ }
+ });
+ });
+
+ return result.join('');
+}
+
+
+/**
+ * 生成 CSV 所需数据,可用于传递给 generateCSV 方法
+ *
+ * @param {Array>} list 数据列表
+ * @param {Record} headMap 数据项的 key 和表头标题的映射关系
+ * @returns 二维数组,第一行是表头
+ *
+ * @example
+ * ```ts
+ * generateCSVData([
+ * {
+ * file: 'a.js',
+ * size: 88,
+ * },
+ * {
+ * file: 'b.js',
+ * size: 66,
+ * }
+ * ], { file: '文件名称', size: '文件大小' })
+ *
+ *
+ * // [['文件名称', '文件大小'], ['a.js', 88], ['b.js', 66]]
+ * ```
+ */
+export function generateCSVData(list: Array>, headMap: Record) {
+ const dataList = list.map((item: Record) => Object.keys(headMap).map(key => item[key]));
+ return [
+ Object.values(headMap),
+ ...dataList,
+ ];
+}
diff --git a/src/csv/index.ts b/src/csv/index.ts
new file mode 100644
index 00000000..f205b146
--- /dev/null
+++ b/src/csv/index.ts
@@ -0,0 +1 @@
+export { generateCSV, generateCSVData } from './csv';
diff --git a/src/daily-merge/daily-merge.ts b/src/daily-merge/daily-merge.ts
index 05df7bea..712007c8 100644
--- a/src/daily-merge/daily-merge.ts
+++ b/src/daily-merge/daily-merge.ts
@@ -166,6 +166,15 @@ async function sendSendMsg(content: string, webhookUrl: string) {
/**
* 每日合并
+ * 1. 获取昨天有活跃的分支
+ * 2. 对于每个分支,进行合并并推送
+ * - 清理 Git 环境
+ * - 切到主分支,并拉最新代码
+ * - 切到当前分支,拉最新代码
+ * - 尝试执行 git merge
+ * - 对比 merge 前后的 commit 信息是否相同,作为判断 merge 是否成功的依据
+ * 3. 发送机器人消息
+ *
*
* @export
* @async
@@ -186,7 +195,6 @@ async function sendSendMsg(content: string, webhookUrl: string) {
* dailyMerge({
* webhookUrl: 'xx',
* appName: 'xx',
- * projectId: 'xx',
* devRoot: 'xx',
*
* baseUrl: 'xx',
@@ -243,7 +251,7 @@ export async function dailyMerge({
whiteBranchReg,
});
- if (!branches || !branches.length) {
+ if (!branches?.length) {
console.log('[no branch to merge]');
sendSendMsg(`>【${appName || ''}自动合并】未找到需要合并的分支`, webhookUrl);
return;
diff --git a/src/debounce/debounce.ts b/src/debounce/debounce.ts
index ae3083cf..3294d75a 100644
--- a/src/debounce/debounce.ts
+++ b/src/debounce/debounce.ts
@@ -1 +1,121 @@
-export { debounce } from './index';
+/**
+ * 防抖,场景:搜索
+ *
+ * 触发事件后在 n 秒内函数只能执行一次,如果
+ * 在 n 秒内又触发了事件,则会重新计算函数执行时间
+ *
+ * @param {Function} fn 主函数
+ * @param {number} time 间隔时间,单位 `ms`
+ * @param {boolean} immediate 是否立即执行,默认 `false`
+ * @returns 闭包函数
+ *
+ * @example
+ *
+ * ```ts
+ * function count() {
+ * console.log('xxxxx')
+ * }
+ * window.onscroll = debounce(count, 500)
+ *
+ * window.onscroll = debounce(count, 500, true)
+ * ```
+ */
+export function debounce(fn: Function, time: number, immediate = false) {
+ let timer: ReturnType;
+ let result: any;
+
+ return function (...args: Array) {
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
+ const that = this;
+ // const args = [...arguments];
+
+ if (immediate) {
+ result = fn.apply(that, args);
+ }
+
+ if (timer) {
+ clearTimeout(timer);
+ }
+
+ timer = setTimeout(() => {
+ result = fn.apply(that, args);
+ }, time);
+
+ return result;
+ };
+}
+
+
+/**
+ * 不用生成中间函数的防抖
+ *
+ * @example
+ * ```ts
+ * debounceRun(func, args, {
+ * funcKey: 'funcKey',
+ * wait: 500, // 默认 500
+ * throttle: false, // 是否是节流,默认 false
+ * immediate: true, // 是否立即执行,默认 true
+ * })
+ * ``
+ */
+export const debounceRun = (() => {
+ // 储存方法的 timer 的 map
+ const timerMap = new Map();
+
+ return (func: Function, args: any[] = [], options: {
+ funcKey?: any;
+ wait?: number;
+ throttle?: boolean;
+ immediate?: boolean;
+ debug?: boolean;
+ } = {}) => {
+ const DEFAULT_OPTIONS = {
+ funcKey: null,
+ wait: 500,
+ throttle: false,
+ immediate: true,
+ debug: false,
+ };
+
+ // 如果没有 funcKey 那么直接使用 func 作为 map 的 key
+ const funcKey = options.funcKey || func;
+ const wait = options.wait ?? DEFAULT_OPTIONS.wait;
+ const throttle = options.throttle ?? DEFAULT_OPTIONS.throttle;
+ const immediate = options.immediate ?? DEFAULT_OPTIONS.immediate;
+ const debug = options.debug ?? DEFAULT_OPTIONS.debug;
+
+ // 先看看 map 里面是否有 timer,有 timer 代表之前调用过
+ let timer = timerMap.get(funcKey);
+
+ if (immediate) {
+ func.apply(this, args);
+ }
+
+ if (timer) {
+ if (debug) {
+ console.log('>>> debounceRun cached');
+ }
+ if (throttle) {
+ return;
+ }
+
+ clearTimeout(timer);
+ }
+
+ timer = setTimeout(() => {
+ // 先把这个方法从 map 里面删掉
+ timerMap.delete(funcKey);
+
+ func.apply(this, args);
+
+ if (debug) {
+ console.log('>>> debounceRun executing func');
+ }
+ }, wait);
+
+ // 将方法的 timer 存进 map, key 是 funcKey
+ timerMap.set(funcKey, timer);
+ };
+})();
diff --git a/src/debounce/index.ts b/src/debounce/index.ts
index eaff390a..b8e63e18 100644
--- a/src/debounce/index.ts
+++ b/src/debounce/index.ts
@@ -1,35 +1 @@
-/**
- * 防抖,场景:搜索
- *
- * 触发事件后在 n 秒内函数只能执行一次,如果
- * 在 n 秒内又触发了事件,则会重新计算函数执行时间
- *
- * @param {Function} fn 主函数
- * @param {number} time 间隔时间,单位 `ms`
- * @returns 闭包函数
- *
- * @example
- *
- * ```ts
- * function count() {
- * console.log('xxxxx')
- * }
- * window.onscroll = debounce(count, 500)
- * ```
- */
-export function debounce(fn: Function, time: number) {
- let timer: ReturnType;
-
- return function (...args: Array) {
- // @ts-ignore
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const that = this;
- // const args = [...arguments];
- if (timer) clearTimeout(timer);
-
- timer = setTimeout(() => {
- fn.apply(that, args);
- }, time);
- };
-}
-
+export { debounce, debounceRun } from './debounce';
diff --git a/src/dotenv/dotenv.ts b/src/dotenv/dotenv.ts
new file mode 100644
index 00000000..52fcb288
--- /dev/null
+++ b/src/dotenv/dotenv.ts
@@ -0,0 +1,39 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import fs from 'fs';
+
+
+/**
+ * 用 dotenv-expand 加载环境变量
+ * @param file 文件路径,默认 .env.local
+ * @param param 参数
+ *
+ * @example
+ * ```ts
+ * loadEnv();
+ *
+ * loadEnv('.env');
+ *
+ * loadEnv('.env.local', {
+ * debug: false, // 是否打印日志,默认 true
+ * });
+ * ```
+ */
+export function loadDotenv(file = '.env.local', options?: {
+ debug?: boolean
+}) {
+ const dotenv = require('dotenv');
+ const dotenvExpand = require('dotenv-expand');
+ if (!fs.existsSync(file)) {
+ console.log(`>>> loadEnv ${file} 不存在`);
+ return;
+ }
+
+ const myEnv = dotenv.config({ path: file });
+ dotenvExpand.expand(myEnv);
+
+ const debug = options?.debug ?? true;
+
+ if (debug) {
+ console.log(`>>> loadEnv ${file} success`);
+ }
+}
diff --git a/src/dotenv/index.ts b/src/dotenv/index.ts
new file mode 100644
index 00000000..f98d4dd7
--- /dev/null
+++ b/src/dotenv/index.ts
@@ -0,0 +1 @@
+export { loadDotenv } from './dotenv';
diff --git a/src/index.ts b/src/index.ts
index 2e439eb6..8bd286f4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,6 +15,7 @@ export * from './constant';
export * from './cookie';
export * from './cron';
export * from './css';
+export * from './csv';
export * from './daily-merge';
export * from './date';
export * from './debounce';
@@ -22,6 +23,7 @@ export * from './devops';
export * from './dialog';
export * from './dom';
export * from './dom-to-image';
+export * from './dotenv';
export * from './e-bus';
export * from './e2e-test';
export * from './env';
@@ -36,6 +38,7 @@ export * from './ip';
export * from './jsdoc';
export * from './launch-app';
export * from './launch-game';
+export * from './lint';
export * from './loader';
export * from './lodash-mini';
export * from './log';
diff --git a/src/lint/index.ts b/src/lint/index.ts
new file mode 100644
index 00000000..c431a16b
--- /dev/null
+++ b/src/lint/index.ts
@@ -0,0 +1,2 @@
+export { checkLint } from './lint';
+export * from './types';
diff --git a/src/lint/lint.ts b/src/lint/lint.ts
new file mode 100644
index 00000000..3ed9aebb
--- /dev/null
+++ b/src/lint/lint.ts
@@ -0,0 +1,607 @@
+import fs from 'fs';
+import path from 'path';
+import axios from 'axios';
+
+import { batchSendWxRobotMarkdown } from '../wecom-robot/batch-send';
+import { writeFileSync, readFileSync } from '../fs/fs';
+import { execCommand } from '../node/node-command';
+
+import type { JSErrorFile, SCSSErrorFile } from './types';
+
+
+const ESLINT_CONFIG_FILE = '.eslintrc.js';
+
+
+function removeParserOptionsProject(workspace: string) {
+ const configFile = path.resolve(workspace, ESLINT_CONFIG_FILE);
+ if (!fs.existsSync(configFile)) {
+ console.log('不存在配置文件');
+ return;
+ }
+
+ const content = readFileSync(configFile);
+
+ const reg = /\s+project:\s+'[^']+json',/;
+ const newContent = content.replace(reg, '');
+ if (newContent && newContent !== content) {
+ console.log('已删除 parserOptions.project');
+ writeFileSync(configFile, newContent);
+ }
+}
+
+
+async function createMRNote({
+ privateToken,
+ gitApiPrefix,
+
+ projectName,
+ mrId,
+
+ body,
+ path,
+ line,
+
+ lineType = 'new',
+
+ // 严重程度 可选值 0、1、2、3
+ // 0 : "default"(默认)1 : "slight"(轻微)
+ // 2 : "normal"(一般)3 : "serious"(严重
+ risk = 3,
+
+ // 需解决 可选值 0、1、2
+ // 0 : "default"(默认)
+ // 1 : "unresolved"(未解决)
+ // 2 : "resolved"(已解决)
+ resolveState = 1,
+
+ // 默认值为 true,发通知给相关用户
+ notifyEnabled = true,
+
+}: {
+ privateToken: string;
+ gitApiPrefix: string;
+
+ projectName: string;
+ mrId: string | number;
+
+ body: any;
+ path: string;
+ line: number;
+
+ lineType?: string;
+ risk?: 0 | 1 | 2 | 3;
+
+ resolveState?: 0 | 1 | 2;
+ notifyEnabled?: boolean;
+
+}) {
+ return new Promise((resolve, reject) => {
+ axios({
+ url: `${gitApiPrefix}/projects/${encodeURIComponent(projectName)}/merge_requests/${mrId}/notes?private_token=${privateToken}`,
+ method: 'POST',
+ data: {
+ body,
+ path,
+ line,
+ line_type: lineType,
+ risk,
+ resolve_state: resolveState,
+ notify_enabled: notifyEnabled,
+ },
+ }).then((res) => {
+ resolve(res.data);
+ })
+ .catch((err) => {
+ console.log('err', err);
+ reject(err);
+ });
+ });
+}
+
+
+function parseScssFiles(scssErrorFiles: SCSSErrorFile[], workspace: string): any[] {
+ if (!scssErrorFiles?.length) {
+ return [];
+ }
+
+ const parsedFiles = scssErrorFiles.reduce((acc: any[], item) => {
+ item.warnings?.forEach((messageItem: any) => {
+ acc.push({
+ filePath: item.source,
+ parsedFilePath: path.relative(workspace, item.source),
+ ...messageItem,
+ message: messageItem.text,
+ ruleId: messageItem.rule,
+ });
+ });
+ return acc;
+ }, []);
+
+ return parsedFiles;
+}
+
+
+function parseJSFiles(jsErrorFiles: JSErrorFile[], workspace: string) {
+ if (!jsErrorFiles?.length) {
+ return [];
+ }
+
+ const parsedFiles = jsErrorFiles.reduce((acc: any[], item) => {
+ item.messages?.forEach((messageItem: any) => {
+ acc.push({
+ filePath: item.filePath,
+ parsedFilePath: path.relative(workspace, item.filePath),
+ ...messageItem,
+ });
+ });
+ return acc;
+ }, []);
+
+ return parsedFiles;
+}
+
+
+async function tryCreateMRNote({
+ privateToken,
+ gitApiPrefix,
+
+ errorFiles,
+ projectName,
+
+ mrId,
+ workspace,
+
+ isScss,
+}: {
+ privateToken: string;
+ gitApiPrefix: string;
+
+ errorFiles: Array>;
+ projectName: string;
+
+ mrId: string | number;
+ workspace: string;
+
+ isScss?: boolean;
+}) {
+ const parsedFiles: Array = isScss
+ ? parseScssFiles(errorFiles, workspace)
+ : parseJSFiles(errorFiles, workspace);
+
+ console.log('[isScss]', isScss);
+ console.log('[parsedFiles]', parsedFiles);
+
+ if (!parsedFiles?.length) return;
+
+
+ for (const file of parsedFiles) {
+ await createMRNote({
+ projectName,
+ mrId,
+
+ body: (`${file.message}\n\n [${file.ruleId}]`) || '格式错误',
+ line: file.line,
+ path: file.parsedFilePath,
+ privateToken,
+ gitApiPrefix,
+ });
+ }
+}
+
+
+async function createMRComment({
+ projectName,
+ mrId,
+ data,
+ privateToken,
+ gitApiPrefix,
+}: {
+ projectName: string;
+ mrId: string | number;
+ data: any;
+ privateToken: string;
+ gitApiPrefix: string;
+}): Promise {
+ return new Promise((resolve, reject) => {
+ axios({
+ url: `${gitApiPrefix}/projects/${encodeURIComponent(projectName)}/merge_request/${mrId}/comments?private_token=${privateToken}`,
+ method: 'POST',
+ data: {
+ note: data,
+ },
+ }).then((res) => {
+ resolve(res.data);
+ })
+ .catch((err) => {
+ console.log('err', err);
+ reject(err);
+ });
+ });
+}
+
+export async function checkLint({
+ privateToken,
+ gitApiPrefix,
+ workspace,
+
+ mrUrl,
+ mrId,
+ buildUrl,
+
+ repo,
+ repoUrl,
+
+ sourceBranch,
+ targetBranch,
+
+ docLink,
+ webhookUrl,
+
+ chatId = ['ALL'],
+ checkAll,
+}: {
+ privateToken: string;
+ gitApiPrefix: string;
+ workspace: string;
+
+ mrUrl?: string;
+ mrId?: string;
+ buildUrl: string;
+
+ repo: string;
+ repoUrl?: string;
+ sourceBranch?: string;
+ targetBranch?: string;
+
+ docLink: string
+ webhookUrl: string;
+
+ chatId?: string[];
+ checkAll?: boolean;
+}) {
+ let jsKeyword = '--ext .js,.ts .';
+ let vueKeyword = '--ext .vue .';
+ let sassKeyword = '**/*.{css,scss}';
+
+ if (sourceBranch && targetBranch) {
+ const {
+ jsTsFiles,
+ vueFiles,
+ scssFiles,
+ } = getDiffFile({
+ sourceBranch,
+ targetBranch,
+ workspace,
+ });
+
+ jsKeyword = jsTsFiles.join(' ');
+ vueKeyword = vueFiles.join(' ');
+ sassKeyword = scssFiles.join(' ');
+ }
+
+ const outputJs = path.resolve(workspace, 'lint-js.json');
+ const outputVue = path.resolve(workspace, 'lint-vue.json');
+ const outputScss = path.resolve(workspace, 'lint-scss.json');
+
+
+ console.log('正在执行 lint js/ts ...');
+ execCommand(`npx eslint ${jsKeyword} --quiet -o ${outputJs} --format json || true`, workspace, 'inherit');
+
+ removeParserOptionsProject(workspace);
+
+ console.log('正在执行 lint vue ...');
+
+ execCommand(`npx eslint ${vueKeyword} --quiet -o ${outputVue} --format json || true`, workspace, 'inherit');
+
+ execCommand(`npx stylelint ${sassKeyword} --quiet -o ${outputScss} --formatter json || true`, workspace, 'inherit');
+
+ const {
+ jsTotal,
+ jsErrorFiles,
+
+ vueTotal,
+ vueErrorFiles,
+
+ scssTotal,
+ scssErrorFiles,
+ } = parseResult({
+ outputJs,
+ outputVue,
+ outputScss,
+ });
+
+ let commentSuccess = false;
+ if (mrId) {
+ const message = genRobotMessage({
+ jsTotal,
+ jsErrorFiles,
+
+ vueTotal,
+ vueErrorFiles,
+
+ scssTotal,
+ scssErrorFiles,
+
+ checkAll,
+ mrUrl,
+ sourceBranch,
+ targetBranch,
+
+ buildUrl,
+ docLink,
+
+ repo,
+ repoUrl,
+ });
+
+ try {
+ commentSuccess = await createMRComment({
+ projectName: repo,
+ mrId,
+ data: message,
+ privateToken,
+ gitApiPrefix,
+ });
+ } catch (err) {
+ }
+ }
+
+
+ console.log('[commentSuccess]', commentSuccess);
+
+ const getPostFix = () => {
+ if (!mrId) return '';
+ return commentSuccess ? '评论成功' : '评论失败';
+ };
+
+ const robotMessage = genRobotMessage({
+ jsTotal,
+ jsErrorFiles,
+
+ vueTotal,
+ vueErrorFiles,
+
+ scssTotal,
+ scssErrorFiles,
+
+ postFix: getPostFix(),
+ mrUrl,
+ sourceBranch,
+ targetBranch,
+
+ buildUrl,
+ docLink,
+ checkAll,
+
+ repo,
+ repoUrl,
+ });
+
+
+ await batchSendWxRobotMarkdown({
+ content: robotMessage,
+ chatId,
+ webhookUrl,
+ });
+
+ if (mrId) {
+ await tryCreateMRNote({
+ projectName: repo,
+ mrId,
+ errorFiles: jsErrorFiles,
+ workspace,
+ privateToken,
+ gitApiPrefix,
+ });
+ await tryCreateMRNote({
+ projectName: repo,
+ mrId,
+ errorFiles: vueErrorFiles,
+ workspace,
+ privateToken,
+ gitApiPrefix,
+ });
+ await tryCreateMRNote({
+ projectName: repo,
+ mrId,
+ errorFiles: scssErrorFiles,
+ isScss: true,
+ workspace,
+ privateToken,
+ gitApiPrefix,
+ });
+ }
+}
+
+
+function getErrorInfo(result: Array<{
+ errorCount?: number;
+}>) {
+ const errorFiles = result.filter(item => item.errorCount);
+ const total = errorFiles.reduce((acc, file) => {
+ acc += file.errorCount || 0;
+ return acc;
+ }, 0);
+
+ return {
+ total,
+ errorFiles,
+ };
+}
+
+function parseResult({
+ outputJs,
+ outputVue,
+ outputScss,
+}: {
+ outputJs: string;
+ outputVue: string;
+ outputScss: string;
+}) {
+ const readyFile = (file: string) => {
+ if (!fs.existsSync(file)) {
+ writeFileSync(file, [], true);
+ return [];
+ }
+ return readFileSync(file, true);
+ };
+
+ const jsResult = readyFile(outputJs);
+ const vueResult = readyFile(outputVue);
+ const scssResult = readyFile(outputScss);
+
+ console.log('\n');
+ console.log('[jsResult] \n', JSON.stringify(jsResult, null, 2));
+ console.log('[vueResult] \n', JSON.stringify(vueResult, null, 2));
+ console.log('[scssResult] \n', JSON.stringify(scssResult, null, 2));
+ console.log('\n');
+
+ const {
+ total: jsTotal,
+ errorFiles: jsErrorFiles,
+ } = getErrorInfo(jsResult);
+
+ const {
+ total: vueTotal,
+ errorFiles: vueErrorFiles,
+ } = getErrorInfo(vueResult);
+
+ const parsed = scssResult.filter((item: Record) => item.warnings?.length).map((item: any) => ({
+ ...item,
+ errorCount: item.warnings.filter((warn: Record) => warn.severity === 'error').length,
+ }));
+
+ const {
+ total: scssTotal,
+ errorFiles: scssErrorFiles,
+ } = getErrorInfo(parsed);
+
+
+ return {
+ jsTotal,
+ jsErrorFiles,
+
+ vueTotal,
+ vueErrorFiles,
+
+ scssTotal,
+ scssErrorFiles,
+ };
+}
+
+function genRobotMessage({
+ jsTotal,
+ jsErrorFiles,
+
+ vueTotal,
+ vueErrorFiles,
+
+ scssTotal,
+ scssErrorFiles,
+
+ mrUrl,
+ sourceBranch,
+ targetBranch,
+
+ buildUrl,
+ docLink,
+
+ repo,
+ repoUrl,
+
+ postFix,
+ checkAll = false,
+}: {
+ jsTotal: number;
+ jsErrorFiles: JSErrorFile[];
+
+ vueTotal: number;
+ vueErrorFiles: JSErrorFile[];
+
+ scssTotal: number;
+ scssErrorFiles: SCSSErrorFile[]
+
+ mrUrl?: string;
+ sourceBranch?: string;
+ targetBranch?: string;
+
+ buildUrl: string;
+ docLink: string;
+
+ repo?: string;
+ repoUrl?: string;
+
+ postFix?: string;
+ checkAll?: boolean;
+}) {
+ const genTitle = (prefix: string) => (`${prefix}${checkAll ? '【LINT】全量模式' : '【LINT】增量模式'}`);
+ const postFixList = postFix ? [postFix] : [];
+ const mrInfo = [
+ mrUrl ? `[${mrUrl}](${mrUrl})` : '',
+ (sourceBranch && targetBranch) ? `${sourceBranch} => ${targetBranch}` : '',
+ ].filter(item => item);
+
+ const repoInfo = (checkAll && repo && repoUrl) ? [`[${repo}](${repoUrl})`] : [];
+
+ if (!jsTotal && !vueTotal && !scssTotal) {
+ return [
+ genTitle('✅'),
+ ...mrInfo,
+ ...repoInfo,
+ '未发现代码规范异常',
+ ...postFixList,
+ ].join(',');
+ }
+
+ return [
+ [
+ genTitle('⚠️'),
+ // '遵守代码规范是防止项目腐化的第一步',
+ ...mrInfo,
+ ...repoInfo,
+ `可在[归档产物](${buildUrl})中查看详情,或本地运行 \`npx eslint\` 等命令`,
+ `[说明文档](${docLink})`,
+ '<@guowangyang>',
+ ...postFixList,
+ ].join(','),
+
+ [`- **JS/TS 错误**:${jsTotal ? `${jsErrorFiles.length}个文件${jsTotal}个错误` : '无'}`],
+ [`- **Vue 错误**:${vueTotal ? `${vueErrorFiles.length}个文件${vueTotal}个错误` : '无'}`],
+ [`- **SCSS/CSS 错误**:${scssTotal ? `${scssErrorFiles.length}个文件${scssTotal}个错误` : '无'}`],
+ ].join('\n');
+}
+
+
+function getDiffFile({
+ sourceBranch,
+ targetBranch,
+ workspace,
+}: {
+ sourceBranch: string;
+ targetBranch: string;
+ workspace: string;
+}) {
+ execCommand(`git clean -df && git reset --hard HEAD && git checkout ${targetBranch} && git pull && git submodule update --init`, workspace, 'inherit');
+ execCommand(`git checkout ${sourceBranch} && git reset --hard "origin/${sourceBranch}" && git pull`, workspace, 'inherit');
+
+ const list = execCommand(`git diff --name-only ${sourceBranch} ${targetBranch}`, workspace, {
+ stdio: 'pipe',
+ line: -1,
+ }).split('\n')
+ .map(item => item.trim())
+ .filter(item => item)
+ .filter(item => fs.existsSync(path.resolve(workspace, item)));
+
+ console.log('diff list: ', JSON.stringify(list, null, 2));
+
+ const jsTsFiles = list.filter(item => item.endsWith('.js') || item.endsWith('.ts'));
+ const vueFiles = list.filter(item => item.endsWith('.vue'));
+ const scssFiles = list.filter(item => item.endsWith('.scss') || item.endsWith('.css'));
+
+ return {
+ jsTsFiles,
+ vueFiles,
+ scssFiles,
+ };
+}
+
diff --git a/src/lint/types.ts b/src/lint/types.ts
new file mode 100644
index 00000000..561325a2
--- /dev/null
+++ b/src/lint/types.ts
@@ -0,0 +1,2 @@
+export type JSErrorFile = Record;
+export type SCSSErrorFile = Record;
diff --git a/src/mp-ci/mp-upload-and-report.ts b/src/mp-ci/mp-upload-and-report.ts
index e018bd9f..1f8eb024 100644
--- a/src/mp-ci/mp-upload-and-report.ts
+++ b/src/mp-ci/mp-upload-and-report.ts
@@ -32,7 +32,7 @@ async function reportToRd({
bkBuildUrl,
bkPipelineId,
}: Record) {
- if (!bundleInfo || !bundleInfo.__APP__) return;
+ if (!bundleInfo?.__APP__) return;
const mainBundleSize = parseInt(`${(bundleInfo.__APP__?.size || 0) / 1024}`, 10);
const totalBundleSize = parseInt(`${(bundleInfo.__FULL__?.size || 0) / 1024}`, 10);
diff --git a/src/node/node-command.ts b/src/node/node-command.ts
index 6f799c63..f5a97f94 100644
--- a/src/node/node-command.ts
+++ b/src/node/node-command.ts
@@ -6,23 +6,46 @@
* 这个方法会对输出结果截断,只返回第一行内容
* @param {string} command 命令
* @param {string} root 执行命令的目录
- * @param {string} stdio 结果输出,默认为 pipe
+ * @param {string | object} stdio 结果输出,默认为 pipe
* @returns {string} 命令执行结果
*/
-export function execCommand(command: string, root?: string, stdio?: string): string {
+export function execCommand(command: string, root?: string, options?: string | {
+ stdio?: string;
+ line?: number;
+}): string {
if (!root) {
root = process.cwd();
}
const { execSync } = require('child_process');
- return (
- execSync(command, {
- cwd: root || process.cwd(),
- encoding: 'utf-8',
- stdio: stdio || 'pipe',
- })
- ?.split('\n')[0]
- ?.trim() || ''
- );
+
+ let innerOptions: {
+ stdio?: string;
+ line?: number;
+ } = {};
+
+ if (typeof options === 'string') {
+ innerOptions = {
+ stdio: options,
+ };
+ } else if (typeof options === 'object') {
+ innerOptions = options;
+ }
+
+ const stdio = innerOptions?.stdio ?? 'pipe';
+ const line = innerOptions?.line ?? 0;
+
+ const res = execSync(command, {
+ cwd: root || process.cwd(),
+ encoding: 'utf-8',
+ stdio: stdio || 'pipe',
+ });
+
+ if (line > -1) {
+ return res?.split('\n')[line]
+ ?.trim() || '';
+ }
+
+ return res;
}
diff --git a/src/router/uni-hook-router.ts b/src/router/uni-hook-router.ts
index 078930fa..99fce6d4 100644
--- a/src/router/uni-hook-router.ts
+++ b/src/router/uni-hook-router.ts
@@ -1,16 +1,82 @@
+/**
+ * 拦截路由
+ *
+ * @example
+ * ```ts
+ * uniHookRouter({
+ * navigateToHooks: [
+ * () => console.log('1')
+ * ],
+ * navigateBackHooks: [
+ * () => console.log('2')
+ * ],
+ * redirectToHooks: [
+ * () => console.log('3')
+ * ],
+ * debug: true,
+ * })
+ * ```
+ */
export function uniHookRouter({
navigateToHooks,
navigateBackHooks,
redirectToHooks,
+
+ tryUniInterCeptor = true,
+ debug = false,
}: {
navigateToHooks?: Array;
navigateBackHooks?: Array;
redirectToHooks?: Array;
+
+ tryUniInterCeptor?: boolean;
+ debug?: boolean;
}) {
const originNavigateTo = uni.navigateTo;
const originNavigateBack = uni.navigateBack;
const originReplaceTo = uni.redirectTo;
+
+ const toDebug = (name: string, callbacks?: Array) => ({
+ invoke(...args: Array) {
+ callbacks?.forEach(cb => cb?.(...args));
+ if (debug) {
+ console.log(`>>> uniHookRouter ${name} invoke`);
+ }
+ },
+ success() {
+ if (debug) {
+ console.log(`>>> uniHookRouter ${name} success`);
+ }
+ },
+ fail() {
+ if (debug) {
+ console.log(`>>> uniHookRouter ${name} fail`);
+ }
+ },
+ complete() {
+ if (debug) {
+ console.log(`>>> uniHookRouter ${name} complete`);
+ }
+ },
+ });
+
+ if (tryUniInterCeptor && typeof uni.addInterceptor === 'function') {
+ uni.addInterceptor('navigateTo', {
+ ...toDebug('navigateTo', navigateToHooks),
+ });
+
+ uni.addInterceptor('navigateBack', {
+ ...toDebug('navigateBack', navigateBackHooks),
+ });
+
+ uni.addInterceptor('redirectTo', {
+ ...toDebug('redirectTo', redirectToHooks),
+ });
+
+ return;
+ }
+
if (originNavigateTo) {
uni.navigateTo = (...args: Array) => {
navigateToHooks?.forEach(cb => cb?.(...args));
diff --git a/src/tgit/project.ts b/src/tgit/project.ts
index 4b9df536..0973f1c6 100644
--- a/src/tgit/project.ts
+++ b/src/tgit/project.ts
@@ -115,7 +115,7 @@ export async function getAllProjects(privateToken: string, search = ''): Promise
});
res = res.concat(temp);
page += 1;
- if (!temp || !temp.length) {
+ if (!temp?.length) {
break;
}
}
diff --git a/src/uni-app/index.ts b/src/uni-app/index.ts
index 5b32ee51..0e602a11 100644
--- a/src/uni-app/index.ts
+++ b/src/uni-app/index.ts
@@ -1 +1,2 @@
export { getRouteLeaveCache } from './route-leave-cache';
+export { startUniProject } from './start';
diff --git a/src/uni-app/start.ts b/src/uni-app/start.ts
new file mode 100644
index 00000000..ce520a58
--- /dev/null
+++ b/src/uni-app/start.ts
@@ -0,0 +1,39 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+
+
+/**
+ * 启动 uni-app 项目
+ * @param options 参数
+ *
+ * @example
+ * ```ts
+ * startUniProject();
+ *
+ * startUniProject({
+ * debug: false, // 默认为 true,会打印参数
+ * })
+ * ```
+ */
+export function startUniProject(options?: {
+ debug?: boolean
+}) {
+ const { spawnSync } = require('child_process');
+ const isWindows = require('os').platform() === 'win32';
+
+ let command = isWindows ? 'npm.cmd' : 'npm';
+ const realArgv = process.argv.slice(2);
+ const debug = options?.debug ?? true;
+
+ if (debug) {
+ console.log('>>> startUniProject realArgv: ', realArgv);
+ }
+
+ let otherArgv = ['run', ...realArgv];
+
+ if (realArgv[0] === 'uni') {
+ command = isWindows ? 'npx.cmd' : 'npx';
+ otherArgv = realArgv;
+ }
+
+ spawnSync(command, otherArgv, { stdio: 'inherit', shell: true });
+}
diff --git a/src/v-console/debug.ts b/src/v-console/debug.ts
index 5182082a..e8cde2aa 100644
--- a/src/v-console/debug.ts
+++ b/src/v-console/debug.ts
@@ -44,8 +44,8 @@ export function genVConsole({
loadVConsole(vConsoleConfig);
})
// 异常捕获,避免 TAM PROMISE_ERROR 错误上报
- .catch((error: any) => {
- console.log('checkIsDevList', error);
- });
+ .catch((error: any) => {
+ console.log('checkIsDevList', error);
+ });
}
}
diff --git a/src/version-tip/gen-version-tip.ts b/src/version-tip/gen-version-tip.ts
index 7507734f..ca819e72 100644
--- a/src/version-tip/gen-version-tip.ts
+++ b/src/version-tip/gen-version-tip.ts
@@ -27,7 +27,7 @@ export function parseChangeLog({
// 大版本
// 不是第一个版本
- if (!currentVersion || !currentVersion[0]) {
+ if (!currentVersion?.[0]) {
currentVersion = changelogStr.match(new RegExp(
`(?<=## \\[${targetVersion}\\].*\n).*?(?=\n##+ \\[?\\d+.\\d+.\\d+)`,
's',
@@ -35,7 +35,7 @@ export function parseChangeLog({
}
// changeLog 的另一种形式,lerna 生成的
- if (!currentVersion || !currentVersion[0]) {
+ if (!currentVersion?.[0]) {
changelogStr = changelogStr.replace(/<\/?small>/g, '');
currentVersion = changelogStr.match(new RegExp(
`(?<=## ${targetVersion}.*\n).*?(?=\n##+ ?\\d+.\\d+.\\d+)`,
@@ -45,7 +45,7 @@ export function parseChangeLog({
// 非大版本
// 第一个版本
- if (!currentVersion || !currentVersion[0]) {
+ if (!currentVersion?.[0]) {
currentVersion = changelogStr.match(new RegExp(
`(?<=### ${targetVersion}.*\n).*`,
's',
@@ -54,14 +54,14 @@ export function parseChangeLog({
// 大版本
// 第一个版本
- if (!currentVersion || !currentVersion[0]) {
+ if (!currentVersion?.[0]) {
currentVersion = changelogStr.match(new RegExp(
`(?<=## ${targetVersion}.*\n).*`,
's',
));
}
- if (!currentVersion || !currentVersion[0]) {
+ if (!currentVersion?.[0]) {
console.log(`[GEN VERSION TIP] ERROR: NOT FOUND CHANGELOG INFO OF ${targetVersion} `);
return '';
}
diff --git a/test/base/object.test.ts b/test/base/object.test.ts
index bc0f9f51..ed8d5c96 100644
--- a/test/base/object.test.ts
+++ b/test/base/object.test.ts
@@ -98,4 +98,11 @@ describe('sliceObject', () => {
b: { b: 2 },
});
});
+
+ it('sliceObject empty', () => {
+ // @ts-expect-error
+ expect(sliceObject(null, 2)).toEqual({});
+ // @ts-expect-error
+ expect(sliceObject(undefined, 2)).toEqual({});
+ });
});