diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 580cdbee..4b7ff7e4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,92 @@ # 更新日志 +### [1.3.90](https://github.com/novlan1/t-comm/compare/v1.3.89...v1.3.90) (2024-05-23) + + +### Features 🎉 + +* add isDirectory ([b267126](https://github.com/novlan1/t-comm/commit/b267126b14933e40f53b75d99b13555b80dc7d55)) +* **git:** add getAllGitRepo ([7b8fb82](https://github.com/novlan1/t-comm/commit/7b8fb82a980004c19eef777ff8bba4b12405b542)) +* **git:** add reclone and transformGitToSSH ([28a88bc](https://github.com/novlan1/t-comm/commit/28a88bca071f5ceb86ea14780f0254062a595724)) + +### [1.3.89](https://github.com/novlan1/t-comm/compare/v1.3.88...v1.3.89) (2024-05-14) + + +### Features 🎉 + +* 优化daily-merge ([d47cb37](https://github.com/novlan1/t-comm/commit/d47cb3787e374b2f5c15c831544f998480c346bf)) +* **ast:** 支持保留之前的引用 ([90ace6d](https://github.com/novlan1/t-comm/commit/90ace6da1202f19296da86b824600f5456aa28fe)) +* **ast:** 支持空导入 ([fdf4491](https://github.com/novlan1/t-comm/commit/fdf4491d7a8a9b6ed235816a2019e8baa518b47d)) + +### [1.3.88](https://github.com/novlan1/t-comm/compare/v1.3.87...v1.3.88) (2024-05-06) + + +### Features 🎉 + +* **ast:** 支持source为数组 ([4e76a6a](https://github.com/novlan1/t-comm/commit/4e76a6ac5f4ee1e6bff6dc3ba6741cf457430764)) + +### [1.3.87](https://github.com/novlan1/t-comm/compare/v1.3.86...v1.3.87) (2024-05-06) + + +### Features 🎉 + +* add export ast ([db8e3b0](https://github.com/novlan1/t-comm/commit/db8e3b0b3555614b7e314a200b073972f378e92e)) + + +### Bug Fixes 🐞 + +* **ast:** 修复默认导出的替换 ([51e3cd2](https://github.com/novlan1/t-comm/commit/51e3cd2746d127c43dac3600782f5fc91a967088)) + +### [1.3.86](https://github.com/novlan1/t-comm/compare/v1.3.85...v1.3.86) (2024-05-04) + + +### Features 🎉 + +* **ast:** add replace dependencies ([d9a36e4](https://github.com/novlan1/t-comm/commit/d9a36e4544f61525311f717d7449a318f513372e)) + +### [1.3.85](https://github.com/novlan1/t-comm/compare/v1.3.84...v1.3.85) (2024-04-20) + + +### Features 🎉 + +* **slash:** add remove last slash ([bc5f125](https://github.com/novlan1/t-comm/commit/bc5f1253e8c6a710f2f0576505a00735d7befc15)) + +### [1.3.84](https://github.com/novlan1/t-comm/compare/v1.3.82...v1.3.84) (2024-04-20) + + +### Features 🎉 + +* **v-console:** 支持showConsole事件 ([b6fe6c1](https://github.com/novlan1/t-comm/commit/b6fe6c10e7fcb6fbc95bfca902623ba0d076315c)) + +### [1.3.82](https://github.com/novlan1/t-comm/compare/v1.3.81...v1.3.82) (2024-04-19) + + +### Features 🎉 + +* **v-console:** 插件显示的时候重新渲染 ([0bd9a40](https://github.com/novlan1/t-comm/commit/0bd9a4010dde479c5d3c2b908b568c72848f6101)) + +### [1.3.81](https://github.com/novlan1/t-comm/compare/v1.3.80...v1.3.81) (2024-04-19) + + +### Chore 🚀 + +* lint ([81710e7](https://github.com/novlan1/t-comm/commit/81710e7606c58d57055c75b7ea81b3b50d39a4f3)) + + +### Documentation 📖 + +* **git:** update docs ([7c3d86e](https://github.com/novlan1/t-comm/commit/7c3d86e08ab7b1b503d3db0e9d2b75e706a63290)) + + +### Features 🎉 + +* **scheduler:** 支持unshift插入请求 ([c97ef4e](https://github.com/novlan1/t-comm/commit/c97ef4e13942a6aacfe87a73d8c7577c05a1348e)) + + +### Bug Fixes 🐞 + +* **debug:** 修复vconsole ([7cb3723](https://github.com/novlan1/t-comm/commit/7cb37232d2f5a8c82fca23b2edcbbd9ecc6b4400)) + ### [1.3.80](https://github.com/novlan1/t-comm/compare/v1.3.79...v1.3.80) (2024-04-16) diff --git a/docs/zh/daily-merge.md b/docs/zh/daily-merge.md index da849678..058ae62a 100644 --- a/docs/zh/daily-merge.md +++ b/docs/zh/daily-merge.md @@ -11,7 +11,7 @@ import { dailyMerge} from 't-comm/lib/daily-merge/index'; ``` -## `dailyMerge(param)` +## `dailyMerge(param0)` **描述**:

每日合并

@@ -19,9 +19,17 @@ import { dailyMerge} from 't-comm/lib/daily-merge/index'; **参数**: -| 参数名 | 类型 | 描述 | -| --- | --- | --- | -| param | object |

参数

| +| 参数名 | 类型 | 默认值 | 描述 | +| --- | --- | --- | --- | +| param0 | object | |

参数

| +| param0.webhookUrl | string | |

机器人地址

| +| param0.appName | string | |

项目名称

| +| param0.devRoot | string | |

项目根路径

| +| param0.baseUrl | string | |

基础请求 url

| +| param0.repoName | string | |

仓库名称

| +| param0.privateToken | string | |

密钥

| +| [param0.isDryRun] | boolean | false |

是否演练

| +| [param0.mainBranch] | string | "'develop'" |

主分支

| diff --git a/docs/zh/git.md b/docs/zh/git.md index 50e1d728..233d585f 100644 --- a/docs/zh/git.md +++ b/docs/zh/git.md @@ -4,27 +4,61 @@ ```ts import { + getAllGitRepo, getGitCurBranch, getGitCommitMessage, getGitCommitInfo, getGitLastTag, getGitCommitsBeforeTag, - getGitAuthor + getGitAuthor, + reCloneGitRemote } from 't-comm'; // or import { + getAllGitRepo, getGitCurBranch, getGitCommitMessage, getGitCommitInfo, getGitLastTag, getGitCommitsBeforeTag, - getGitAuthor + getGitAuthor, + reCloneGitRemote } from 't-comm/lib/git/index'; ``` +## `getAllGitRepo(root)` + + +**描述**:

获取所有 git 仓库

+ +**参数**: + + +| 参数名 | 类型 | 描述 | +| --- | --- | --- | +| root | string |

根路径

| + +**返回**: array
+ +

路径列表

+ +**示例** + +```ts +getAllGitRepo('/root/yang'); + +[ + { + root: '/root', + origin: 'git@git.address', + } +] +``` + + ## `getGitCurBranch()` @@ -149,3 +183,21 @@ getGitCommitInfo() **返回**:

user

+ + +## `reCloneGitRemote(list)` + + +**描述**:

根据配置表,重新 clone 仓库

+ +**参数**: + + +| 参数名 | 类型 | 描述 | +| --- | --- | --- | +| list | Array<item> |

列表

| +| item.root | string |

路径

| +| item.origin | string |

origin

| + + + diff --git a/docs/zh/i18n.md b/docs/zh/i18n.md new file mode 100644 index 00000000..669b016b --- /dev/null +++ b/docs/zh/i18n.md @@ -0,0 +1,37 @@ +[[toc]] + +## 引入 + +```ts +import { getI18nToken } from 't-comm'; + +// or + +import { getI18nToken} from 't-comm/lib/i18n/index'; +``` + + +## `getI18nToken(appId, appKey)` + + +**描述**:

获取 i18n token

+ +**参数**: + + +| 参数名 | 类型 | 描述 | +| --- | --- | --- | +| appId | string |

appId

| +| appKey | string |

appKey

| + +**返回**: Promise.<string>
+ +

token

+ +**示例** + +```ts +getI18nToken('appId', 'appKey').then(token => { + console.log('token', token) +}) +``` diff --git a/docs/zh/slash.md b/docs/zh/slash.md new file mode 100644 index 00000000..b9eaace2 --- /dev/null +++ b/docs/zh/slash.md @@ -0,0 +1,94 @@ +[[toc]] + +## 引入 + +```ts +import { + removeFirstSlash, + removeLastSlash, + removeFirstAndLastSlash +} from 't-comm'; + +// or + +import { + removeFirstSlash, + removeLastSlash, + removeFirstAndLastSlash +} from 't-comm/lib/slash/index'; +``` + + +## `removeFirstSlash([str])` + + +**描述**:

移除第一个反斜杠

+ +**参数**: + + +| 参数名 | 类型 | 默认值 | 描述 | +| --- | --- | --- | --- | +| [str] | string | "''" |

输入字符串

| + +**返回**: string
+ +

字符串

+ +**示例** + +```ts +removeFirstSlash('/abc/ddd/') + +'abc/ddd/' +``` + + +## `removeLastSlash([str])` + + +**描述**:

移除最后一个反斜杠

+ +**参数**: + + +| 参数名 | 类型 | 默认值 | 描述 | +| --- | --- | --- | --- | +| [str] | string | "''" |

输入字符串

| + +**返回**: string
+ +

字符串

+ +**示例** + +```ts +removeLastSlash('/abc/') + +'/abc' +``` + + +## `removeFirstAndLastSlash([str])` + + +**描述**:

移除第一个和最后一个反斜杠

+ +**参数**: + + +| 参数名 | 类型 | 默认值 | 描述 | +| --- | --- | --- | --- | +| [str] | string | "''" |

输入字符串

| + +**返回**: string
+ +

字符串

+ +**示例** + +```ts +removeFirstAndLastSlash('/abc/') + +'abc' +``` diff --git a/package.json b/package.json index 31e80619..69388cfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "t-comm", - "version": "1.3.80", + "version": "1.3.90", "description": "丰富易用的工具库", "main": "lib/index.js", "module": "lib/index.esm.js", diff --git a/script/trial/tgit/project.js b/script/trial/tgit/project.js new file mode 100644 index 00000000..1a3c9358 --- /dev/null +++ b/script/trial/tgit/project.js @@ -0,0 +1,18 @@ +const { getOneProjectDetail } = require('../../../lib'); +require('../../utils/env'); + +console.log('process.env', process.env); +function main() { + getOneProjectDetail({ + projectName: 'pmd-mobile/match/pro', + privateToken: process.env.GIT_WOA_PRIVATE_TOKEN, + baseUrl: process.env.GIT_WOA_BASE_URL, + }).then((res) => { + console.log('res', res); + }) + .catch((err) => { + console.log('err', err); + }); +} + +main(); diff --git a/src/ast/index.ts b/src/ast/index.ts new file mode 100644 index 00000000..a7c9e766 --- /dev/null +++ b/src/ast/index.ts @@ -0,0 +1,3 @@ +export { IImportType } from './types'; +export { parseReplaceConfig } from './parse-config-list'; +export { replaceDependencies } from './replace-dependencies'; diff --git a/src/ast/parse-config-list.ts b/src/ast/parse-config-list.ts new file mode 100644 index 00000000..c2096795 --- /dev/null +++ b/src/ast/parse-config-list.ts @@ -0,0 +1,89 @@ +import { IImportItem, IImportType, IReplaceConfig } from './types'; + + +function parseOneReplaceConfig(config: IImportItem): { + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; +} { + if (typeof config === 'string') { + return { + sourceName: config, + sourceType: IImportType.ImportSpecifier, + targetName: config, + targetType: IImportType.ImportSpecifier, + }; + } + if (Array.isArray(config)) { + return { + sourceName: config[0], + sourceType: IImportType.ImportSpecifier, + targetName: config[1] || config[0], + targetType: IImportType.ImportSpecifier, + }; + } + + return { + sourceName: config.sourceName || '', + sourceType: config.sourceType || IImportType.ImportSpecifier, + targetName: config.targetName || config.sourceName || '', + targetType: config.targetType || config.sourceType || IImportType.ImportSpecifier, + }; +} + + +export function parseReplaceConfig(configList: Array) { + const result = configList.reduce((acc: Array<{ + source: Array; + target: string; + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; + }>, item) => { + const { importedList, source, target } = item; + const newSource = Array.isArray(source) ? source : [source]; + + const list = importedList.map(item => ({ + source: newSource, + target, + ...parseOneReplaceConfig(item), + })); + + if (!importedList.length) { + acc.push({ + ...item, + source: newSource, + sourceName: 'FAKE', + targetName: 'FAKE', + sourceType: IImportType.FAKE, + targetType: IImportType.FAKE, + }); + } else { + acc.push(...list); + } + + return acc; + }, []); + + const newResult = result.reduce((acc: Array<{ + source: string; + target: string; + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; + }>, item) => { + const { source } = item; + const list = source.map(sourceItem => ({ + ...item, + source: sourceItem, + })); + + acc.push(...list); + return acc; + }, []); + + return newResult; +} diff --git a/src/ast/replace-dependencies.ts b/src/ast/replace-dependencies.ts new file mode 100644 index 00000000..5b92bc2e --- /dev/null +++ b/src/ast/replace-dependencies.ts @@ -0,0 +1,222 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ +import { IParsedConfigItem, IImportType } from './types'; + + +export function replaceDependencies(content: string, parsedConfigList: Array, keyword: string) { + const parser = require('@babel/parser'); + const traverse = require('@babel/traverse').default; + const generator = require('@babel/generator'); + + let replaced = false; + + const ast = parser.parse(content, { + // 不加这个配置,报错:SyntaxError: 'import' and 'export' may appear only with 'sourceType: "module"' + sourceType: 'module', + plugins: ['typescript'], + }); + + traverse(ast, { + ImportDeclaration(path: any) { + const sourceValue = path.node.source.value; + + const importedList = path.node.specifiers.map((item: any) => { + const { type } = item; + + return { + type, + local: item.local.name, + imported: item.imported?.name || '', + }; + }); + + if (sourceValue.includes(keyword)) return; + const target = getTarget(sourceValue, importedList, parsedConfigList); + + target.forEach((item) => { + path.insertAfter(item); + }); + + if (target.length) { + path.remove(); + replaced = true; + } + }, + }); + + const output = generator.default(ast, {}); + + if (replaced) { + return output.code; + } + + return content; +} + +function localFlatten(list: Array<{ + target: string; + [key: string]: any; +}>) { + return list.reduce((acc: Record, current) => { + const { target } = current!; + + if (acc[target]) { + acc[target].push(current!); + } else { + acc[target] = [current!]; + } + return acc; + }, {}); +} + + +function getTarget(originSource: string, originImportedList: Array<{ + type: IImportType; + local: string; + imported: string; +}>, parsedConfigList: Array) { + if (!originImportedList.length) { + const foundItem = parsedConfigList.find(item => item.source === originSource); + if (!foundItem?.target) { + return []; + } + return [ + genNewImport(foundItem?.target, []), + ]; + } + const parsedImportList = originImportedList.map((curOrigin) => { + const current = parsedConfigList.find((item) => { + const { source, sourceType, sourceName } = item; + + if (originSource !== source || curOrigin.type !== sourceType) { + return false; + } + + if (sourceType === IImportType.ImportSpecifier) { + return curOrigin.imported === sourceName; + } + + // ImportDefaultSpecifier 和 ImportNamespaceSpecifier 直接返回 true + return true; + }); + + if (!current) { + return { + ...curOrigin, + _type: 'OLD', + target: originSource, + targetName: curOrigin.imported, + targetType: curOrigin.type, + }; + } + + return { + ...current, + ...curOrigin, + _type: 'NEW', + }; + }); + + const newImportList = parsedImportList.filter(item => item._type === 'NEW') as Array<{ + type: IImportType; + local: string; + imported: string; + + source: string; + target: string; + + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; + }>; + const oldImportList = parsedImportList.filter(item => item._type === 'OLD') as Array<{ + type: IImportType; + target: string; + local: string; + imported: string; + targetName: string; + }>; + + const obj = localFlatten(newImportList) ; + const oldObj = localFlatten(oldImportList); + // newImportList.reduce((acc: Record>, current) => { + // const { target } = current!; + + // if (acc[target]) { + // acc[target].push(current!); + // } else { + // acc[target] = [current!]; + // } + // return acc; + // }, {}); + + + const nodeList = Object.keys(obj).map((source) => { + const node = genNewImport(source, obj[source]); + return node; + }); + + const oldNodeList = Object.keys(oldObj).map((source) => { + const node = genNewImport(source, oldObj[source]); + return node; + }); + + if (nodeList.length) { + return [ + ...nodeList, + oldNodeList, + ]; + } + + return []; +} + + +function genNewImport(source: string, importedList: Array<{ + type: IImportType; + local: string; + imported: string; + source: string; + target: string; + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; +}>) { + const t = require('@babel/types'); + + + const list = importedList.map((current) => { + const { local, targetType, targetName } = current; + + if (targetType === IImportType.ImportSpecifier) { + return t.ImportSpecifier( + t.identifier(local), + t.identifier(targetName), + ); + } + + if (targetType === IImportType.ImportDefaultSpecifier) { + return t.ImportDefaultSpecifier(t.identifier(local)); + } + + if (targetType === IImportType.importNamespaceSpecifier) { + return t.ImportNamespaceSpecifier(t.identifier(local)); + } + }); + + return t.ImportDeclaration( + list, + t.StringLiteral(source), + ); +} diff --git a/src/ast/types.ts b/src/ast/types.ts new file mode 100644 index 00000000..93855682 --- /dev/null +++ b/src/ast/types.ts @@ -0,0 +1,32 @@ +export enum IImportType { + ImportSpecifier = 'ImportSpecifier', + ImportDefaultSpecifier= 'ImportDefaultSpecifier', + importNamespaceSpecifier= 'ImportNamespaceSpecifier', + + FAKE = 'FAKE', +} + +export type IImportItem = string | Array | { + sourceName?: string; + sourceType?: IImportType; + targetName?: string; + targetType?: IImportType; +}; + +export type IReplaceConfig = { + importedList: Array; + // 源位置 + source: string | Array; + // 目标位置 + target: string; +}; + +export type IParsedConfigItem = { + source: string; + target: string; + + sourceName: string; + sourceType: IImportType; + targetName: string; + targetType: IImportType; +}; diff --git a/src/daily-merge/daily-merge.ts b/src/daily-merge/daily-merge.ts index ed093346..500b56bf 100644 --- a/src/daily-merge/daily-merge.ts +++ b/src/daily-merge/daily-merge.ts @@ -1,4 +1,6 @@ import { getBranchesByProjectName } from '../tgit/branch'; +import { getOneProjectDetail } from '../tgit/project'; + import { execCommand } from '../node/node-command'; import { batchSendWxRobotMarkdown } from '../wecom-robot/batch-send'; import { getGitCommitInfo } from '../git/git'; @@ -163,8 +165,19 @@ async function sendSendMsg(content: string, webhookUrl: string) { /** * 每日合并 - * @param {object} param 参数 * + * @export + * @async + * @param {object} param0 参数 + * @param {string} param0.webhookUrl 机器人地址 + * @param {string} param0.appName 项目名称 + * @param {string} param0.devRoot 项目根路径 + * @param {string} param0.baseUrl 基础请求 url + * @param {string} param0.repoName 仓库名称 + * @param {string} param0.privateToken 密钥 + * @param {boolean} [param0.isDryRun=false] 是否演练 + * @param {string} [param0.mainBranch='develop'] 主分支 + * @returns {*} * @example * * ```ts @@ -185,7 +198,6 @@ async function sendSendMsg(content: string, webhookUrl: string) { export async function dailyMerge({ webhookUrl, appName, - projectId, devRoot, baseUrl, @@ -197,7 +209,6 @@ export async function dailyMerge({ }: { webhookUrl: string; appName: string; - projectId: string | number; devRoot: string; baseUrl: string; @@ -257,6 +268,14 @@ export async function dailyMerge({ return !all.includes(branch); }); + const projectDetail: any = await getOneProjectDetail({ + baseUrl, + projectName: repoName, + privateToken, + }); + const projectId = projectDetail.id || ''; + + console.log('[projectDetail]', projectDetail); const content = getMessageContent({ appName, @@ -273,6 +292,11 @@ export async function dailyMerge({ console.log('[noMergeBranches]', JSON.stringify(noMergeBranches)); console.log('[robot message]', content); + + if (!webhookUrl) { + console.log('[No webhookUrl]'); + return; + } await sendSendMsg(content, webhookUrl); console.log('<===== END '); diff --git a/src/fs/fs.ts b/src/fs/fs.ts index 0881f509..b313c204 100644 --- a/src/fs/fs.ts +++ b/src/fs/fs.ts @@ -48,3 +48,9 @@ export function readFileSync(file: string, isJson = false) { return result; } + + +export function isDirectory(filePath = '') { + const stat = fs.lstatSync(filePath); + return stat.isDirectory(); +} diff --git a/src/fs/index.ts b/src/fs/index.ts index 9c42354a..c6bfe9de 100644 --- a/src/fs/index.ts +++ b/src/fs/index.ts @@ -1 +1 @@ -export { writeFileSync, readFileSync } from './fs'; +export { writeFileSync, readFileSync, isDirectory } from './fs'; diff --git a/src/git/all-git-repo.ts b/src/git/all-git-repo.ts new file mode 100644 index 00000000..df6b3313 --- /dev/null +++ b/src/git/all-git-repo.ts @@ -0,0 +1,76 @@ +import * as fs from 'fs'; +import { isDirectory } from '../fs/fs'; +import { execCommand } from '../node/node-command'; + + +/** + * 获取所有 git 仓库 + * + * @export + * @param {string} root 根路径 + * @returns {array} 路径列表 + * @example + * ```ts + * getAllGitRepo('/root/yang'); + * + * [ + * { + * root: '/root', + * origin: 'git@git.address', + * } + * ] + * ``` + */ +export function getAllGitRepo(root: string) { + const gitList: Array = []; + getAllGitRoots(root, gitList); + const gitOriginList = getGitOrigin(gitList); + + return gitOriginList; +} + + +function getAllGitRoots(root: string, gitList: Array) { + const list = fs.readdirSync(root); + + list.forEach((item) => { + const filePath = `${root}/${item}`; + + if (isDirectory(filePath)) { + const gitPath = `${filePath}/.git`; + + if (fs.existsSync(gitPath)) { + gitList.push(filePath); + } else { + getAllGitRoots(filePath, gitList); + } + } + }); +} + +function getGitOrigin(gitList: Array) { + const gitOriginList = gitList.map((item) => { + const res = execCommand('git remote -v', item); + console.log('[getGitOrigin] res: ', res); + const origin = parseItemOrigin(res); + + return { + root: item, + origin, + }; + }); + + return gitOriginList; +} + + +function parseItemOrigin(result = '') { + const reg = /\s+(git@.*\.git)/; + const httpReg = /\s+(http.*)\s*\(/; + + const match = result.match(reg); + const httpMatch = result.match(httpReg); + + return (match?.[1] || httpMatch?.[1] || '').trim(); +} + diff --git a/src/git/index.ts b/src/git/index.ts index 3b98c404..a15903ae 100644 --- a/src/git/index.ts +++ b/src/git/index.ts @@ -1,4 +1,6 @@ +export { getAllGitRepo } from './all-git-repo'; export { rmFirstAndLastSlash, getGitCodeLink, getGitMRLink } from './git-link'; + export { getGitCurBranch, getGitCommitInfo, @@ -9,3 +11,4 @@ export { getGitTagTime, getGitAuthor, } from './git'; +export { transformGitToSSH } from './ssh'; diff --git a/src/git/re-clone.ts b/src/git/re-clone.ts new file mode 100644 index 00000000..5c8d408a --- /dev/null +++ b/src/git/re-clone.ts @@ -0,0 +1,33 @@ +import { transformGitToSSH } from './ssh'; +import { execCommand } from '../node/node-command'; +import * as path from 'path'; + + +/** + * 根据配置表,重新 clone 仓库 + * @param {Array} list 列表 + * @param {string} item.root 路径 + * @param {string} item.origin origin + */ +export function reCloneGitRemote(list: Array<{ + root: string; + origin: string; +}> = []) { + list.forEach((item) => { + const { root, origin } = item; + const realOrigin = transformGitToSSH(origin.trim()); + + console.log('[reCloneGitRemote] root: ', root); + console.log('[reCloneGitRemote] origin: ', realOrigin); + + execCommand(`rm -rf ${root}`); + const dir = path.dirname(root); + + console.log('[reCloneGitRemote] dir: ', dir); + try { + execCommand(`git clone ${realOrigin}`, dir, 'inherit'); + } catch (err) { + console.log('[reCloneGitRemote] err: ', err); + } + }); +} diff --git a/src/git/ssh.ts b/src/git/ssh.ts new file mode 100644 index 00000000..25605d57 --- /dev/null +++ b/src/git/ssh.ts @@ -0,0 +1,21 @@ + +export function transformGitToSSH(link = '') { + if (!link.startsWith('http')) { + return link; + } + + const reg = /https:\/\/([^/]+)\/(.*)$/; + const match = link.match(reg); + + if (!match) { + return ''; + } + + const result = `git@${match[1]}:${match[2]}`; + + if (result.endsWith('.git')) { + return result; + } + + return `${result}.git`; +} diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index edaf50ce..a49b966a 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -1,5 +1,20 @@ import axios from 'axios'; + +/** + * 获取 i18n token + * + * @export + * @param {string} appId appId + * @param {string} appKey appKey + * @returns {Promise} token + * @example + * ```ts + * getI18nToken('appId', 'appKey').then(token => { + * console.log('token', token) + * }) + * ``` + */ export function getI18nToken(appId: string, appKey: string) { return new Promise((resolve, reject) => { axios({ diff --git a/src/index.ts b/src/index.ts index 3ef75b54..83b18998 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +export * from './ast'; export * from './base'; export * from './bite'; export * from './build-upload'; diff --git a/src/slash/index.ts b/src/slash/index.ts index c2c8bf6a..0e7ada6a 100644 --- a/src/slash/index.ts +++ b/src/slash/index.ts @@ -1 +1 @@ -export { removeFirstAndLastSlash, removeFirstSlash } from './slash'; +export { removeFirstAndLastSlash, removeFirstSlash, removeLastSlash } from './slash'; diff --git a/src/slash/slash.ts b/src/slash/slash.ts index 8b2cf4a0..dbecae5f 100644 --- a/src/slash/slash.ts +++ b/src/slash/slash.ts @@ -1,11 +1,58 @@ -export function removeFirstSlash(key = '') { - if (key.startsWith('/')) { - return key.slice(1); + +/** + * 移除第一个反斜杠 + * + * @export + * @param {string} [str=''] 输入字符串 + * @returns {string} 字符串 + * @example + * ```ts + * removeFirstSlash('/abc/ddd/') + * + * 'abc/ddd/' + * ``` + */ +export function removeFirstSlash(str = '') { + if (str.startsWith('/')) { + return str.slice(1); } - return key; + return str; +} + + +/** + * 移除最后一个反斜杠 + * + * @export + * @param {string} [str=''] 输入字符串 + * @returns {string} 字符串 + * + * @example + * ```ts + * removeLastSlash('/abc/') + * + * '/abc' + * ``` + */ +export function removeLastSlash(str = '') { + return str.replace(/\/$/, ''); } +/** + *移除第一个和最后一个反斜杠 + * + * @export + * @param {string} [str=''] 输入字符串 + * @returns {string} 字符串 + * + * @example + * ```ts + * removeFirstAndLastSlash('/abc/') + * + * 'abc' + * ``` + */ export function removeFirstAndLastSlash(str = '') { return str.replace(/^\/|\/$/g, ''); } diff --git a/src/tgit/project.ts b/src/tgit/project.ts index 4996895b..4b9df536 100644 --- a/src/tgit/project.ts +++ b/src/tgit/project.ts @@ -17,13 +17,19 @@ import { instance } from './helper'; export function getOneProjectDetail({ projectName, privateToken, + baseUrl, }: { projectName: string; privateToken: string; + baseUrl?: string; }) { + let url = `/api/v3/projects/${encodeURIComponent(projectName)}`; + if (baseUrl) { + url = `${baseUrl}${url}`; + } return new Promise((resolve, reject) => { instance({ - url: `/projects/${encodeURIComponent(projectName)}`, + url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken, diff --git a/src/v-console/config.ts b/src/v-console/config.ts index dd798282..264a9938 100644 --- a/src/v-console/config.ts +++ b/src/v-console/config.ts @@ -9,6 +9,11 @@ export const V_CONSOLE_DOM = { URL_INPUT_ID: 'vConsolePluginInput', URL_JUMP_BUTTON: 'vConsolePluginUrlJumpButton', GO_BACK_BUTTON: 'vConsolePluginGoBackButton', + + PLUGIN_NAME_PREFIX: '__vc_plug_', + // 需要小写,作为插件 id + PLUGIN_VERSION_NAME: 'version_performance', + PLUGIN_SIMPLE_VERSION_NAME: 'simple_version', } as const; export const EMPTY_LINE = `
`; diff --git a/src/v-console/debug.ts b/src/v-console/debug.ts index e23d8740..c916e34b 100644 --- a/src/v-console/debug.ts +++ b/src/v-console/debug.ts @@ -1,5 +1,4 @@ -import { loadVConsole } from './v-console' -import { isPromise } from '../validate/type' +import { loadVConsole } from './v-console'; /** @@ -32,18 +31,17 @@ export function genVConsole({ vConsoleConfig?: Record; asyncConfirmFunc?: Function; }) { - if (hide) { return; } if (immediateShow) { - loadVConsole(vConsoleConfig) + loadVConsole(vConsoleConfig); } - if (isPromise(asyncConfirmFunc)) { + if (typeof asyncConfirmFunc === 'function') { asyncConfirmFunc?.()?.then(() => { - loadVConsole(vConsoleConfig) - }) + loadVConsole(vConsoleConfig); + }); } } diff --git a/src/v-console/index.ts b/src/v-console/index.ts index 19482a21..c27bb3ed 100644 --- a/src/v-console/index.ts +++ b/src/v-console/index.ts @@ -6,4 +6,4 @@ export { checkAndShowVConsole, } from './toggle'; export { loadVConsole } from './v-console'; -export { genVConsole } from './debug' +export { genVConsole } from './debug'; diff --git a/src/v-console/plugin/version-simple.ts b/src/v-console/plugin/version-simple.ts index 80207336..31cb6912 100644 --- a/src/v-console/plugin/version-simple.ts +++ b/src/v-console/plugin/version-simple.ts @@ -1,27 +1,47 @@ import { V_CONSOLE_DOM } from '../config'; import { dateFormat } from '../../time/time'; + +function renderVersion(callback: Function) { +// @ts-ignore + const info = window.igameVersion; + let innerHtml = ''; + + if (info?.version) { + const time = `${dateFormat(new Date(+info.version), 'yyyy-MM-dd hh:mm:ss')}`; + + innerHtml = ` +
构建时间:${time || ''}
+
构建作者:${info.author || ''}
+ `; + } else { + innerHtml = `
无构建信息
`; + } + + callback(`
${innerHtml}
`); +} + + export function initVersionSimplePlugin() { - const plugin = new VConsole.VConsolePlugin('simpleVersion', '版本信息'); + const plugin = new VConsole.VConsolePlugin(V_CONSOLE_DOM.PLUGIN_SIMPLE_VERSION_NAME, '版本信息'); + const callback = function (html: string) { + const dom = document.getElementById(`${V_CONSOLE_DOM.PLUGIN_NAME_PREFIX}${V_CONSOLE_DOM.PLUGIN_SIMPLE_VERSION_NAME}`); - // 先加载version.js,种上IGameVersion对象 - plugin.on('renderTab', (callback: any) => { - // @ts-ignore - const info = window.igameVersion; - let innerHtml = ''; - - if (info?.version) { - const time = `${dateFormat(new Date(+info.version), 'yyyy-MM-dd hh:mm:ss')}`; - - innerHtml = ` -
构建时间:${time || ''}
-
构建作者:${info.author || ''}
- `; - } else { - innerHtml = `
无构建信息
`; + if (dom) { + dom.innerHTML = html; } + }; - callback(`
${innerHtml}
`); + // 先加载version.js,种上IGameVersion对象 + plugin.on('renderTab', (callback: any) => { + renderVersion(callback); }); + plugin.on('showConsole', () => { + renderVersion(callback); + }); + plugin.on('show', () => { + renderVersion(callback); + }); + return plugin; } diff --git a/src/v-console/plugin/version.ts b/src/v-console/plugin/version.ts index 3c7b4670..59e3b1db 100644 --- a/src/v-console/plugin/version.ts +++ b/src/v-console/plugin/version.ts @@ -30,32 +30,50 @@ function goBack() { window.vConsole?.hide?.(); } -export function initVersionPlugin() { - const plugin = new VConsole.VConsolePlugin('versionPerformance', '其他信息'); +function renderPlugin(callback: Function) { const parseNumber = (num: string | number) => +(+num).toFixed(2); - - plugin.on('renderTab', (callback: Function) => { - let html = `
`; - html += getPerformanceInfo() - .map((item) => { - const { label, value } = item; - return `
${label}:${parseNumber(value)}ms
`; - }) - .concat(EMPTY_LINE) - .concat(getVersionHtml()) - .join('\n'); - - html += ` + let html = `
`; + + html += getPerformanceInfo() + .map((item) => { + const { label, value } = item; + return `
${label}:${parseNumber(value)}ms
`; + }) + .concat(EMPTY_LINE) + .concat(getVersionHtml()) + .join('\n'); + + html += ` - `; + `; - html += '
'; + html += '
'; - callback(html); - }); + callback(html); +} + + +export function initVersionPlugin() { + const plugin = new VConsole.VConsolePlugin(V_CONSOLE_DOM.PLUGIN_VERSION_NAME, '其他信息'); + const callback = function (html: string) { + const dom = document.getElementById(`${V_CONSOLE_DOM.PLUGIN_NAME_PREFIX}${V_CONSOLE_DOM.PLUGIN_VERSION_NAME}`); + + if (dom) { + dom.innerHTML = html; + } + }; + plugin.on('renderTab', (callback: Function) => { + renderPlugin(callback); + }); + plugin.on('showConsole', () => { + renderPlugin(callback); + }); + plugin.on('show', () => { + renderPlugin(callback); + }); const btnList: Array = []; btnList.push({ diff --git a/src/v-console/v-console.ts b/src/v-console/v-console.ts index a2fcba9e..5b47559c 100644 --- a/src/v-console/v-console.ts +++ b/src/v-console/v-console.ts @@ -54,6 +54,10 @@ function initVConsole(options: Record, plugins: Array) { id: 'vConsolePluginStyle', content: V_CONSOLE_STYLE_CONTENT, }); + + if (window && !window.vConsole) { + window.vConsole = vConsole; + } return vConsole; } diff --git a/test/ast/parse-config-list.spec.ts b/test/ast/parse-config-list.spec.ts new file mode 100644 index 00000000..352d8ed2 --- /dev/null +++ b/test/ast/parse-config-list.spec.ts @@ -0,0 +1,100 @@ +import { parseReplaceConfig } from '../../src'; + + +const list: any = [ + { + source: [ + 'src/common', + 'src/common/network', + 'src/common/network/post', + 'src/common/network-v2', + ], + importedList: [ + 'post', + ], + target: '@tx/pmd-network', + }, + { + source: 'src/common/report/data-center/merchant-report', + importedList: [ + 'reportBrandExposure', + 'reportMerchantExposure', + ], + target: '@tx/pmd-report', + }, + { + source: 'src/common/network/axios', + importedList: [ + { + sourceType: 'ImportDefaultSpecifier', + targetName: 'axios', + targetType: 'ImportSpecifier', + }, + ], + target: '@tx/pmd-tools/lib/axios', + }, +]; + +const newList: any = [ + { + source: 'src/common', + target: '@tx/pmd-network', + sourceName: 'post', + sourceType: 'ImportSpecifier', + targetName: 'post', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/network', + target: '@tx/pmd-network', + sourceName: 'post', + sourceType: 'ImportSpecifier', + targetName: 'post', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/network/post', + target: '@tx/pmd-network', + sourceName: 'post', + sourceType: 'ImportSpecifier', + targetName: 'post', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/network-v2', + target: '@tx/pmd-network', + sourceName: 'post', + sourceType: 'ImportSpecifier', + targetName: 'post', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/report/data-center/merchant-report', + target: '@tx/pmd-report', + sourceName: 'reportBrandExposure', + sourceType: 'ImportSpecifier', + targetName: 'reportBrandExposure', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/report/data-center/merchant-report', + target: '@tx/pmd-report', + sourceName: 'reportMerchantExposure', + sourceType: 'ImportSpecifier', + targetName: 'reportMerchantExposure', + targetType: 'ImportSpecifier', + }, + { + source: 'src/common/network/axios', + target: '@tx/pmd-tools/lib/axios', + sourceName: '', + sourceType: 'ImportDefaultSpecifier', + targetName: 'axios', + targetType: 'ImportSpecifier', + }, +]; +describe('parseReplaceConfig', () => { + it('parseReplaceConfig', () => { + expect(parseReplaceConfig(list)).toEqual(newList); + }); +}); diff --git a/test/base/list.test.ts b/test/base/list.test.ts index cbe01b80..b1785084 100644 --- a/test/base/list.test.ts +++ b/test/base/list.test.ts @@ -1,10 +1,14 @@ import { flatten, flat, shuffle, getAccCellWidth } from '../../src'; +import * as listUtils from '../../src/base/list'; describe('flatten', () => { it('flatten', () => { expect(typeof flatten([{ id: 1, name: 'a' }], 'id')).toBe('object'); expect(flatten([{ id: 1, name: 'a' }], 'id')[1].name).toBe('a'); + + + expect(listUtils.flatten([{ id: 1, name: 'a' }], 'id')[1].name).toBe('a'); }); }); diff --git a/test/slash/slash.spec.ts b/test/slash/slash.spec.ts new file mode 100644 index 00000000..8be559db --- /dev/null +++ b/test/slash/slash.spec.ts @@ -0,0 +1,36 @@ +import { removeFirstAndLastSlash, removeFirstSlash, removeLastSlash } from '../../src/slash'; + + +describe('removeFirstAndLastSlash', () => { + it('removeFirstAndLastSlash', () => { + expect(removeLastSlash('')).toBe(''); + expect(removeFirstAndLastSlash('//')).toBe(''); + expect(removeFirstAndLastSlash('/abc.adf.adf/')).toBe('abc.adf.adf'); + expect(removeFirstAndLastSlash('/abc.adf.adf')).toBe('abc.adf.adf'); + expect(removeFirstAndLastSlash('abc.adf.adf/')).toBe('abc.adf.adf'); + expect(removeFirstAndLastSlash('abc.adf.adf')).toBe('abc.adf.adf'); + }); +}); + +describe('removeFirstSlash', () => { + it('removeFirstSlash', () => { + expect(removeLastSlash('')).toBe(''); + expect(removeFirstSlash('//')).toBe('/'); + expect(removeFirstSlash('/abc.adf.adf/')).toBe('abc.adf.adf/'); + expect(removeFirstSlash('/abc.adf.adf')).toBe('abc.adf.adf'); + expect(removeFirstSlash('abc.adf.adf/')).toBe('abc.adf.adf/'); + expect(removeFirstSlash('abc.adf.adf')).toBe('abc.adf.adf'); + }); +}); + + +describe('removeLastSlash', () => { + it('removeLastSlash', () => { + expect(removeLastSlash('')).toBe(''); + expect(removeLastSlash('//')).toBe('/'); + expect(removeLastSlash('/abc.adf.adf/')).toBe('/abc.adf.adf'); + expect(removeLastSlash('/abc.adf.adf')).toBe('/abc.adf.adf'); + expect(removeLastSlash('abc.adf.adf/')).toBe('abc.adf.adf'); + expect(removeLastSlash('abc.adf.adf')).toBe('abc.adf.adf'); + }); +});