├── .editorConfig ├── .github └── workflows │ └── release.yml ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── lerna-debug.log ├── lerna.json ├── package.json ├── packages ├── api │ ├── .editorConfig │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ └── api.test.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── types │ │ │ └── index.ts │ └── tsconfig.json ├── cli │ ├── .gitignore │ ├── .npmrc │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ └── cli.test.js │ ├── bin │ │ └── gcm.js │ ├── package.json │ ├── server │ │ └── index.html │ ├── src │ │ ├── constants │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── libs │ │ │ ├── add.ts │ │ │ ├── config.ts │ │ │ ├── current.ts │ │ │ ├── doctor.ts │ │ │ ├── index.ts │ │ │ ├── list.ts │ │ │ ├── remove.ts │ │ │ ├── scan.ts │ │ │ ├── sync │ │ │ │ ├── const.ts │ │ │ │ ├── index.ts │ │ │ │ ├── server.ts │ │ │ │ ├── steps.ts │ │ │ │ └── utils.ts │ │ │ ├── upgrade.ts │ │ │ └── use.ts │ │ ├── notification.json │ │ ├── types │ │ │ └── index.ts │ │ └── utils │ │ │ ├── fs.ts │ │ │ └── index.ts │ └── tsconfig.json └── vsc-ext │ ├── .eslintrc.json │ ├── .gitignore │ ├── .npmrc │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── README.md │ ├── marketplace.png │ ├── package.json │ ├── pnpm-lock.yaml │ ├── preview.png │ ├── src │ ├── extension.ts │ └── test │ │ ├── runTest.ts │ │ └── suite │ │ ├── extension.test.ts │ │ └── index.ts │ ├── tsconfig.json │ └── vsc-extension-quickstart.md ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── tsconfig.json /.editorConfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release new version 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | publish-npm: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # checkout 代码 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 # 获取完整历史 16 | clean: true # 清理工作目录 17 | # 准备 node 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: 18 21 | registry-url: https://registry.npmjs.org/ 22 | # 准备 pnpm 23 | - uses: pnpm/action-setup@v4 24 | with: 25 | cache: pnpm 26 | # 配置 git 用户 27 | - name: Setup git user 28 | run: | 29 | git config --global user.email ${{secrets.USER_EMAIL}} 30 | git config --global user.name ${{secrets.USER_NAME}} 31 | # 安装依赖 32 | - name: Install dependencies 33 | run: pnpm install 34 | # 构建产物 35 | - name: Build 36 | run: pnpm build 37 | # 强制同步远程 git 标签(消除远程已删除的 tag 缓存对 Lerna 计算 commit 的影响) 38 | - name: Sync git tags 39 | run: | 40 | git fetch --prune --prune-tags # 清理已删除的远程标签 41 | git fetch --tags -f # 强制更新所有标签 42 | # Lerna 发布 43 | - name: Publish with Lerna 44 | env: 45 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 46 | GH_TOKEN: ${{secrets.COMMIT_TOKEN}} 47 | run: | 48 | pnpm ci:version 49 | pnpm ci:publish 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | es 3 | node_modules 4 | config.json 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "lexmin" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.8.0](https://github.com/lexmin0412/gcm/compare/v1.7.1...v1.8.0) (2025-02-20) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **cli:** 修复 upgrade 命令的输出异常 ([530c10a](https://github.com/lexmin0412/gcm/commit/530c10ae9d01d558fcaf812e7081f269927c784d)) 12 | * **cli:** 修复构建时 puppeteer 类型报错 ([9c03561](https://github.com/lexmin0412/gcm/commit/9c0356145e67a0d0953ab0d312afd6c907d1b0c5)) 13 | * 修复拼写错误 ([0da4ba1](https://github.com/lexmin0412/gcm/commit/0da4ba1927d9a7f8880beb661ee05aba4f19ed9b)) 14 | 15 | 16 | ### Features 17 | 18 | * **api:** readConfigs API 返回新增 sync 配置对象 ([bbb3308](https://github.com/lexmin0412/gcm/commit/bbb330873515270b6341e8661b249dd5b369a8b1)) 19 | * **cli:** 增加配置获取和更新函数 ([37d2b38](https://github.com/lexmin0412/gcm/commit/37d2b38f88ecd65d2e6d2bb06a748b3acbcb60f5)) 20 | * **cli/sync:** 完成冲突对比合并页面交互功能 ([32191eb](https://github.com/lexmin0412/gcm/commit/32191eb726aaeb028047394710a21c23f1238705)) 21 | * **cli/sync:** 完成判断冲突=>合并冲突=>推送配置 MVP 闭环 ([64b736d](https://github.com/lexmin0412/gcm/commit/64b736dc4ef149a8e09bdecbe9096d1fcccce07f)) 22 | * **cli:** sync 的 github 仓库改为用户自定义 ([2744c55](https://github.com/lexmin0412/gcm/commit/2744c55462ad42ae315253377080f956ea90f5e4)) 23 | * **cli:** 完成 sync 操作的服务启动功能 ([6bb9608](https://github.com/lexmin0412/gcm/commit/6bb9608cd6c3526020a0b4ae553966909cb8c8c1)) 24 | * **cli:** 完成 sync 的大体流程 ([5caff70](https://github.com/lexmin0412/gcm/commit/5caff705f8be49b50a892b3a1bc53947301b29b7)) 25 | * **cli:** 新增配置获取/更新、同步的 cli 入口 ([f52d056](https://github.com/lexmin0412/gcm/commit/f52d056aa0c325a62d51d0788f4c709829cd50e2)) 26 | * **packages:** 新增 api 子包 ([8422baa](https://github.com/lexmin0412/gcm/commit/8422baab45bd5d851c864e4e7bad1017cb54a594)) 27 | * **packages:** 迁移 gcm-vscode 仓库作为子包, 新增支持交互式切换配置 ([f08a999](https://github.com/lexmin0412/gcm/commit/f08a99948d23adfebc4c595a1b0898c1174805b2)) 28 | * **vsc-ext:** add open command ([385a65a](https://github.com/lexmin0412/gcm/commit/385a65aa92364fa82cef37975a470031acb5af0a)) 29 | 30 | 31 | 32 | 33 | 34 | ## 1.7.3 (2025-02-20) 35 | 36 | **Note:** Version bump only for package root 37 | 38 | 39 | 40 | 41 | 42 | 43 | ## 1.7.1 (2024-04-15) 44 | 45 | **Note:** Version bump only for package root 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ### 1.6.14 (2024-04-07) 54 | 55 | ### 1.6.13 (2023-05-30) 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCM 2 | 3 | git 用户配置管理工具。 4 | 5 | ![version](https://img.shields.io/npm/v/@lexmin0412/gcm) ![NPM Last Update](https://img.shields.io/npm/last-update/@lexmin0412/gcm) ![NPM Downloads](https://img.shields.io/npm/dm/@lexmin0412/gcm) ![GitHub commit activity](https://img.shields.io/github/commit-activity/y/lexmin0412/git-config-manager) 6 | 7 | ![Repo Beats](https://repobeats.axiom.co/api/embed/7b1fdf8c60db6bc080c1086aeb519fbc19531717.svg "Repobeats analytics image") 8 | 9 | ## 目录 10 | 11 | - [简介](#简介) 12 | - [安装](#安装) 13 | - [功能](#功能) 14 | 15 | ## 简介 16 | 17 | GCM, 全称 Git Config Manager,用于在不同工作区(目录) 灵活切换不同的 Git 配置,降低心智负担。 18 | 19 | 开发这个工具的初衷,是因为我的设备既用于公司项目开发,平时也会写一些自己的项目,我需要频繁不断地在不同 Git 账号之间切换以确保我使用正确的用户来进行提交,一次次的 `git config user.name xxx`,`git config user.name xxx@xxx.com` 耗费了我大量的时间,每次手动输入也难免会产生差错,造成一些未知用户的提交。于是我下定决心要开发这个工具,它可以帮你实现如下需求: 20 | 21 | - 全局维护多个 Git 用户配置,在需要时可以方便地切换 22 | - 快速查询当前目录应用的 Git 用户配置 23 | - 扫描某个目录下存在多少份不同的 Git 用户配置,然后快速纠正它 24 | 25 | 查看 [功能](#功能) 部分来了解它所有的 API,如果你有什么意见或建议,也欢迎通过 [issue](https://github.com/lexmin0412/gcm/issues) 来进行交流。 26 | 27 | ## 安装 28 | 29 | ```shell 30 | npm install @lexmin0412/gcm -g 31 | ``` 32 | 33 | ## 功能 34 | 35 | **说明:** 36 | > 如果本机已有 gcm 命令,可以使用 `gitconf` 命令来替换,如 `gcm list` 可以改为 `gitconf list`,其他命令同理。 37 | 38 | ### `gcm list` 39 | 40 | 简写:`gcm ls`。 41 | 42 | 查看所有用户配置。 43 | 44 | ### `gcm add` 45 | 46 | 添加用户配置,需要输入别名、用户、邮箱信息。 47 | 48 | ### `gcm use ` 49 | 50 | 通过别名切换用户配置。 51 | 52 | ### `gcm remove` 53 | 54 | 简写:`gcm rm`。 55 | 56 | 通过别名删除用户配置。 57 | 58 | ### `gcm current` 59 | 60 | > 注:v1.1.0 以上版本支持。 61 | 62 | 简写:`gcm cur`。 63 | 64 | 显示当前用户正在使用的配置。 65 | 66 | ### `gcm scan` 67 | 68 | > 注:v1.2.0 以上版本支持。 69 | 70 | 简写:`gcm sc`。 71 | 72 | 扫描指定目录(默认当前用户目录)下的所有用户配置。 73 | 74 | ### `gcm doctor` 75 | 76 | > 注:v1.3.0 以上版本支持。 77 | 78 | 诊断当前目录下使用的 git 配置是否正确。 79 | 80 | ### `gcm upgrade` 81 | 82 | > 注:v1.4.0 以上版本支持。 83 | 84 | 更新全局 `gcm` 到 latest 版本。 85 | 86 | ### `gcm sync` 87 | 88 | > 注:v1.8.0 以上版本支持。 89 | 90 | 同步 GCM 配置到远程仓库。 91 | 92 | ### `gcm get-config` 93 | 94 | > 注:v1.8.0 以上版本支持。 95 | 96 | 获取 GCM 的同步配置。 97 | 98 | ### `gcm set-config` 99 | 100 | > 注:v1.8.0 以上版本支持。 101 | 102 | 修改 GCM 的同步配置。 103 | 104 | 105 | ## 更新日志 106 | 107 | [点我](https://github.com/lexmin0412/gcm/releases) 前往。 108 | 109 | ## Star History 110 | 111 | [![Star History Chart](https://api.star-history.com/svg?repos=lexmin0412/git-config-manager&type=Date)](https://star-history.com/#lexmin0412/git-config-manager&Date) 112 | -------------------------------------------------------------------------------- /lerna-debug.log: -------------------------------------------------------------------------------- 1 | 0 silly argv { 2 | 0 silly argv _: [ 'create' ], 3 | 0 silly argv lernaVersion: '8.1.2', 4 | 0 silly argv '$0': 'node_modules/lerna/dist/cli.js', 5 | 0 silly argv name: 'core' 6 | 0 silly argv } 7 | 1 notice cli v8.1.2 8 | 2 verbose packageConfigs Package manager "pnpm" detected. Resolving packages using `pnpm-workspace.yaml`. 9 | 3 verbose rootPath /Users/cellerchan/code/git_repos/gcm 10 | 4 error Error: canceled 11 | 4 error at Interface. (/Users/cellerchan/code/git_repos/gcm/node_modules/.pnpm/read@3.0.1/node_modules/read/dist/commonjs/read.js:89:21) 12 | 4 error at Interface.emit (node:events:514:28) 13 | 4 error at [_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1131:18) 14 | 4 error at ReadStream.onkeypress (node:internal/readline/interface:270:20) 15 | 4 error at ReadStream.emit (node:events:514:28) 16 | 4 error at emitKeys (node:internal/readline/utils:357:14) 17 | 4 error at emitKeys.next () 18 | 4 error at ReadStream.onData (node:internal/readline/emitKeypressEvents:64:36) 19 | 4 error at ReadStream.emit (node:events:514:28) 20 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "1.8.0", 4 | "npmClient": "pnpm", 5 | "command": { 6 | "version": { 7 | "message": "chore(release): publish %s", 8 | "conventionalCommits": true, 9 | "createRelease": "github" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "version": "1.6.14", 5 | "description": "快速切换本地 Git 用户配置", 6 | "main": "./lib/index.js", 7 | "bin": { 8 | "gcm": "./bin/gcm.js", 9 | "gitconf": "./bin/gcm.js" 10 | }, 11 | "engines": { 12 | "node": ">=18.0.0" 13 | }, 14 | "scripts": { 15 | "new:pkg": "lerna create", 16 | "test:version": "pnpm build && node lib/index.js --version", 17 | "test:use": "pnpm build && node lib/index.js use github", 18 | "test:add": "pnpm build && node lib/index.js add", 19 | "test:remove": "pnpm build && node lib/index.js remove", 20 | "test:list": "pnpm build && node lib/index.js list", 21 | "test:scan": "pnpm build && node lib/index.js scan", 22 | "test:current": "pnpm build && node lib/index.js current", 23 | "test:doctor": "pnpm build && node lib/index.js doctor", 24 | "test:upgrade": "pnpm build && node lib/index.js upgrade", 25 | "build": "pnpm --filter=@lexmin0412/gcm* run build", 26 | "ts": "ts-node src/", 27 | "ci:version": "lerna version --yes --conventional-commits --create-release github", 28 | "ci:publish": "lerna publish from-package --yes" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/lexmin0412/gcm.git" 33 | }, 34 | "keywords": [ 35 | "gcm", 36 | "tools", 37 | "cli", 38 | "git", 39 | "userConfig" 40 | ], 41 | "author": { 42 | "name": "lexmin0412", 43 | "email": "zhangle_dev@outlook.com", 44 | "url": "http://lexmin.cn" 45 | }, 46 | "license": "ISC", 47 | "bugs": { 48 | "url": "https://github.com/lexmin0412/gcm/issues" 49 | }, 50 | "homepage": "https://github.com/lexmin0412/gcm#readme", 51 | "publishConfig": { 52 | "access": "public", 53 | "registry": "https://registry.npmjs.org/" 54 | }, 55 | "devDependencies": { 56 | "@types/inquirer": "^8.2.1", 57 | "@types/node": "^18.6.3", 58 | "@types/semver": "^7.3.12", 59 | "lerna": "^8.1.2", 60 | "rimraf": "^3.0.2", 61 | "ts-node": "^10.9.1", 62 | "typescript": "^4.7.4" 63 | }, 64 | "files": [ 65 | "es", 66 | "lib" 67 | ], 68 | "types": "./lib/index.d.ts", 69 | "jsnext:main": "./es/index.js", 70 | "module": "./es/index.js", 71 | "packageManager": "pnpm@8.1.0+sha512.7b61fbdf04d1c667397d617fe9026a6eec14e8edcd455642d46d561571bc12a7cdffe641bbc5d51c96703c7e978740f16c4a19830f3fc32daeb60083c0fbb84a" 72 | } 73 | -------------------------------------------------------------------------------- /packages/api/.editorConfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /packages/api/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | es 3 | node_modules -------------------------------------------------------------------------------- /packages/api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.8.0](https://github.com/lexmin0412/git-config-manager/compare/v1.7.1...v1.8.0) (2025-02-20) 7 | 8 | 9 | ### Features 10 | 11 | * **api:** readConfigs API 返回新增 sync 配置对象 ([bbb3308](https://github.com/lexmin0412/git-config-manager/commit/bbb330873515270b6341e8661b249dd5b369a8b1)) 12 | * **packages:** 新增 api 子包 ([8422baa](https://github.com/lexmin0412/git-config-manager/commit/8422baab45bd5d851c864e4e7bad1017cb54a594)) 13 | -------------------------------------------------------------------------------- /packages/api/README.md: -------------------------------------------------------------------------------- 1 | # @lexmin0412/gcm-api 2 | 3 | Git 配置管理工具的 Node.js API 模块 4 | 5 | ## 安装 6 | 7 | ```bash 8 | npm install @lexmin0412/gcm-api 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/api/__tests__/api.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const api = require('..'); 4 | const assert = require('assert').strict; 5 | 6 | assert.strictEqual(api(), 'Hello from api'); 7 | console.info('api tests passed'); 8 | -------------------------------------------------------------------------------- /packages/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lexmin0412/gcm-api", 3 | "version": "1.8.0", 4 | "description": "GCM 的 Node.js API", 5 | "keywords": [ 6 | "gcm" 7 | ], 8 | "author": { 9 | "name": "lexmin0412", 10 | "email": "zhangle_dev@outlook.com", 11 | "url": "https://github.com/lexmin0412" 12 | }, 13 | "homepage": "https://github.com/lexmin0412/git-config-manager/tree/master/packages/cli#readme", 14 | "license": "ISC", 15 | "main": "./lib/index.js", 16 | "directories": { 17 | "lib": "lib", 18 | "test": "__tests__" 19 | }, 20 | "files": [ 21 | "es", 22 | "lib" 23 | ], 24 | "publishConfig": { 25 | "access": "public", 26 | "registry": "https://registry.npmjs.org/" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/lexmin0412/git-config-manager.git" 31 | }, 32 | "scripts": { 33 | "test": "node ./__tests__/api.test.js", 34 | "build:commonjs": "rimraf lib && tsc --target es5 --outDir lib --module commonjs --declaration", 35 | "build:es": "rimraf es && tsc --target es6 --outDir es --module es6", 36 | "build": "npm run build:commonjs && npm run build:es", 37 | "ts": "ts-node src/", 38 | "prepublishOnly": "npm run build" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/lexmin0412/git-config-manager/issues" 42 | }, 43 | "types": "./lib/index.d.ts", 44 | "jsnext:main": "./es/index.js", 45 | "module": "./es/index.js", 46 | "private": false, 47 | "devDependencies": { 48 | "@types/node": "^18.6.3", 49 | "rimraf": "^3.0.2", 50 | "ts-node": "^10.9.1", 51 | "typescript": "^4.7.4" 52 | }, 53 | "dependencies": { 54 | "picocolors": "^1.0.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/api/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as fs from 'fs' 3 | import * as os from 'os' 4 | import { execSync } from 'child_process' 5 | import { ISyncConfig, UserConfig } from './types' 6 | import pc from 'picocolors' 7 | 8 | const USER_HOME = os.homedir() 9 | 10 | export const rootPath = path.resolve(USER_HOME, '.gcm') 11 | 12 | if ( !fs.existsSync(rootPath) ) { 13 | fs.mkdirSync(rootPath) 14 | } 15 | 16 | export const configJsonPath = path.resolve(rootPath, 'config.json') 17 | 18 | export const createEmptyJsonWhenNeeds = () => { 19 | if (!fs.existsSync(configJsonPath)) { 20 | const users: UserConfig[] = [] 21 | fs.writeFileSync(configJsonPath, JSON.stringify({users}, null, 2)) 22 | } 23 | } 24 | 25 | export const getPkgJson = () => { 26 | return require(path.resolve(__dirname, '..', '..', 'package.json')) 27 | } 28 | 29 | export const getCurrentConfig = () => { 30 | const currentUserName = execSync('git config --get user.name').toString().trim() 31 | const currentUserEmail = execSync('git config --get user.email').toString().trim() 32 | return { 33 | name: currentUserName, 34 | email: currentUserEmail 35 | } 36 | } 37 | 38 | export const addConfig = (config: UserConfig) => { 39 | createEmptyJsonWhenNeeds() 40 | const configJson = require(configJsonPath) 41 | configJson.users.push(config) 42 | fs.writeFileSync(configJsonPath, JSON.stringify(configJson, null, 2)) 43 | console.log(pc.green('添加成功✅')) 44 | } 45 | 46 | export const removeConfig = (alias: string) => { 47 | if (!isConfigJsonExists()) { 48 | console.error(pc.red('配置文件不存在')) 49 | process.exit(1) 50 | } 51 | const configJson = require(configJsonPath) 52 | const targetUserConfigIndex = configJson.users.findIndex(((config: UserConfig)=>config.alias === alias)) 53 | if (targetUserConfigIndex === -1 ) { 54 | console.error(pc.red(`不存在别名为 ${alias} 的配置`)) 55 | process.exit(1) 56 | } 57 | configJson.users.splice(targetUserConfigIndex, 1) 58 | fs.writeFileSync(configJsonPath, JSON.stringify(configJson, null, 2)) 59 | console.log(pc.green(`配置 ${alias} 删除成功`)) 60 | } 61 | 62 | export const isConfigJsonExists = () => { 63 | const isExists = fs.existsSync(configJsonPath) 64 | return isExists 65 | } 66 | 67 | export const readConfigs = (): { 68 | sync: ISyncConfig 69 | users: UserConfig[] 70 | } => { 71 | createEmptyJsonWhenNeeds() 72 | const configs = JSON.parse(fs.readFileSync(configJsonPath, 'utf8')) 73 | return configs 74 | } 75 | 76 | export const getAllUserConfigs = () => { 77 | const configs = readConfigs() 78 | return configs.users 79 | } 80 | 81 | export const getConfigByAlias = (alias: string) => { 82 | const configs = readConfigs() 83 | const config = configs.users.find((config: UserConfig) => config.alias === alias) 84 | return config || null 85 | } 86 | 87 | export const getProjectConfig = (projectPath: string = process.cwd()) => { 88 | const currentUserName = execSync('git config --get user.name', { 89 | cwd: projectPath 90 | }).toString().trim() 91 | const currentUserEmail = execSync('git config --get user.email', { 92 | cwd: projectPath 93 | }).toString().trim() 94 | return { 95 | name: currentUserName, 96 | email: currentUserEmail 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/api/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface UserConfig { 2 | alias: string 3 | name: string 4 | email: string 5 | origin: string 6 | } 7 | 8 | export interface Origin { 9 | origin: string 10 | } 11 | 12 | /** 13 | * 同步配置 14 | */ 15 | export interface ISyncConfig { 16 | type: 'github' 17 | repoUrl: string 18 | dir: string 19 | filename: string 20 | } 21 | -------------------------------------------------------------------------------- /packages/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "lib": [ 5 | "es6" 6 | ], 7 | "alwaysStrict": true, 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true 12 | }, 13 | "includes": [ 14 | "src/*" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | es 3 | node_modules -------------------------------------------------------------------------------- /packages/cli/.npmrc: -------------------------------------------------------------------------------- 1 | PUPPETEER_DOWNLOAD_BASE_URL=https://cdn.npmmirror.com/binaries/chrome-for-testing 2 | -------------------------------------------------------------------------------- /packages/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.8.0](https://github.com/lexmin0412/gcm/compare/v1.7.1...v1.8.0) (2025-02-20) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **cli:** 修复 upgrade 命令的输出异常 ([530c10a](https://github.com/lexmin0412/gcm/commit/530c10ae9d01d558fcaf812e7081f269927c784d)) 12 | * **cli:** 修复构建时 puppeteer 类型报错 ([9c03561](https://github.com/lexmin0412/gcm/commit/9c0356145e67a0d0953ab0d312afd6c907d1b0c5)) 13 | * 修复拼写错误 ([0da4ba1](https://github.com/lexmin0412/gcm/commit/0da4ba1927d9a7f8880beb661ee05aba4f19ed9b)) 14 | 15 | 16 | ### Features 17 | 18 | * **cli:** 增加配置获取和更新函数 ([37d2b38](https://github.com/lexmin0412/gcm/commit/37d2b38f88ecd65d2e6d2bb06a748b3acbcb60f5)) 19 | * **cli/sync:** 完成冲突对比合并页面交互功能 ([32191eb](https://github.com/lexmin0412/gcm/commit/32191eb726aaeb028047394710a21c23f1238705)) 20 | * **cli/sync:** 完成判断冲突=>合并冲突=>推送配置 MVP 闭环 ([64b736d](https://github.com/lexmin0412/gcm/commit/64b736dc4ef149a8e09bdecbe9096d1fcccce07f)) 21 | * **cli:** sync 的 github 仓库改为用户自定义 ([2744c55](https://github.com/lexmin0412/gcm/commit/2744c55462ad42ae315253377080f956ea90f5e4)) 22 | * **cli:** 完成 sync 操作的服务启动功能 ([6bb9608](https://github.com/lexmin0412/gcm/commit/6bb9608cd6c3526020a0b4ae553966909cb8c8c1)) 23 | * **cli:** 完成 sync 的大体流程 ([5caff70](https://github.com/lexmin0412/gcm/commit/5caff705f8be49b50a892b3a1bc53947301b29b7)) 24 | * **cli:** 新增配置获取/更新、同步的 cli 入口 ([f52d056](https://github.com/lexmin0412/gcm/commit/f52d056aa0c325a62d51d0788f4c709829cd50e2)) 25 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # @lexmin0412/gcm-cli 2 | 3 | GCM 的命令行接口。 4 | -------------------------------------------------------------------------------- /packages/cli/__tests__/cli.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const cli = require('..'); 4 | const assert = require('assert').strict; 5 | 6 | assert.strictEqual(cli(), 'Hello from cli'); 7 | console.info('cli tests passed'); 8 | -------------------------------------------------------------------------------- /packages/cli/bin/gcm.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | require('./../lib/index') 3 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lexmin0412/gcm", 3 | "version": "1.8.0", 4 | "description": "GCM 的命令行接口", 5 | "bin": { 6 | "gcm": "./bin/gcm.js", 7 | "gitconf": "./bin/gcm.js" 8 | }, 9 | "keywords": [ 10 | "git-config" 11 | ], 12 | "author": "lexmin0412 ", 13 | "homepage": "https://github.com/lexmin0412/gcm/tree/main/packages/cli#readme", 14 | "license": "ISC", 15 | "main": "./lib/index.js", 16 | "directories": { 17 | "lib": "lib", 18 | "test": "__tests__" 19 | }, 20 | "files": [ 21 | "es", 22 | "lib", 23 | "server" 24 | ], 25 | "publishConfig": { 26 | "registry": "https://registry.npmjs.org/", 27 | "access": "public" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/lexmin0412/gcm.git" 32 | }, 33 | "scripts": { 34 | "test": "node ./__tests__/cli.test.js", 35 | "build:commonjs": "rimraf lib && tsc --target es5 --outDir lib --module commonjs --declaration", 36 | "build:es": "rimraf es && tsc --target es6 --outDir es --module es6", 37 | "build": "npm run build:commonjs && npm run build:es", 38 | "ts": "ts-node src/" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/lexmin0412/gcm/issues" 42 | }, 43 | "devDependencies": { 44 | "@types/node": "^18.6.3", 45 | "rimraf": "^3.0.2", 46 | "ts-node": "^10.9.1", 47 | "typescript": "^4.7.4" 48 | }, 49 | "dependencies": { 50 | "@lexmin0412/gcm-api": "workspace:*", 51 | "@lexmin0412/run": "^0.0.1", 52 | "commander": "^9.4.0", 53 | "figlet": "^1.5.2", 54 | "inquirer": "8.2.3", 55 | "latest-version": "5.1.0", 56 | "picocolors": "^1.0.0", 57 | "puppeteer": "^24.2.1", 58 | "semver": "^7.3.7" 59 | }, 60 | "types": "./lib/index.d.ts", 61 | "jsnext:main": "./es/index.js", 62 | "module": "./es/index.js" 63 | } 64 | -------------------------------------------------------------------------------- /packages/cli/server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLI Browser Interaction 6 | 7 | 8 | 9 |
10 |

GCM 冲突处理

11 | 12 |
13 |
14 |

本地配置

15 | 19 |
20 |
21 |

远程配置

22 | 26 |
27 |
28 | 29 |
30 |
31 |

32 | 合并后配置 33 |

34 | 35 |
36 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/cli/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_ORIGINS = [ 2 | { 3 | origin: 'github.com' 4 | }, 5 | { 6 | origin: 'gitlab.com' 7 | }, 8 | { 9 | origin: 'gitee.com' 10 | }, 11 | ] 12 | -------------------------------------------------------------------------------- /packages/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events' 2 | import * as path from 'path' 3 | import pc from "picocolors" 4 | import { program } from 'commander' 5 | import { gt } from 'semver' 6 | import { add, current, doctor, list, remove, scan, upgrade, use } from './libs' 7 | import notification from './notification.json' 8 | import { sync } from './libs/sync' 9 | import { getConfig, setConfig } from './libs/config' 10 | const figlet = require('figlet') 11 | const pkgJsonPath = path.resolve(__dirname, '..', 'package.json') 12 | const pkgJson = require(pkgJsonPath) 13 | 14 | if (gt('1.4.0', pkgJson.version)) { 15 | console.log(pc.yellow(notification.text)) 16 | } 17 | console.log(''); 18 | 19 | const artText = figlet.textSync('G C M', { 20 | font: 'Standard', 21 | horizontalLayout: 'default', 22 | verticalLayout: 'default', 23 | width: 80, 24 | whitespaceBreak: true 25 | }) 26 | 27 | console.log(`> gcm ${process.argv[2]} 28 | ${pc.green(artText)}`); 29 | 30 | // 解决事件监听过多,运行命令后弹出警告信息的问题(默认最大监听器数量为 10 个) 31 | EventEmitter.setMaxListeners(20) 32 | 33 | program 34 | .version(pkgJson.version) 35 | .command('use ') 36 | .description('use git user config by alias') 37 | .action((alias: string) => { 38 | try { 39 | use(alias) 40 | } catch (error) { 41 | console.error(error) 42 | process.exit(1) 43 | } 44 | }) 45 | 46 | program 47 | .version(pkgJson.version) 48 | .command('add') 49 | .description('add user config') 50 | .action(() => { 51 | try { 52 | add() 53 | } catch (error) { 54 | console.error(error) 55 | process.exit(1) 56 | } 57 | }) 58 | 59 | program 60 | .version(pkgJson.version) 61 | .command('remove') 62 | .alias('rm') 63 | .description('remove user config') 64 | .action(() => { 65 | try { 66 | remove() 67 | } catch (error) { 68 | console.error(error) 69 | process.exit(1) 70 | } 71 | }) 72 | 73 | program 74 | .version(pkgJson.version) 75 | .command('list') 76 | .alias('ls') 77 | .description('list all configs in global config file') 78 | .action(() => { 79 | try { 80 | list() 81 | } catch (error) { 82 | console.error(error) 83 | process.exit(1) 84 | } 85 | }) 86 | 87 | program 88 | .version(pkgJson.version) 89 | .command('scan') 90 | .alias('sc') 91 | .description('scan all git project\'s config in directory') 92 | .action(() => { 93 | try { 94 | scan() 95 | } catch (error) { 96 | console.error(error) 97 | process.exit(1) 98 | } 99 | }) 100 | 101 | program 102 | .version(pkgJson.version) 103 | .command('doctor') 104 | .description('verify if your git config in current workspace is correct') 105 | .action(() => { 106 | try { 107 | doctor() 108 | } catch (error) { 109 | console.error(error) 110 | process.exit(1) 111 | } 112 | }) 113 | 114 | program 115 | .version(pkgJson.version) 116 | .command('upgrade') 117 | .description('upgrade version of gcm self') 118 | .action(() => { 119 | try { 120 | upgrade() 121 | } catch (error) { 122 | console.error(error) 123 | process.exit(1) 124 | } 125 | }) 126 | 127 | program 128 | .version(pkgJson.version) 129 | .command('get-config ') 130 | .description('get configuration') 131 | .action((type: 'sync') => { 132 | try { 133 | getConfig(type) 134 | } catch (error) { 135 | console.error(error) 136 | process.exit(1) 137 | } 138 | }) 139 | 140 | program 141 | .version(pkgJson.version) 142 | .command('set-config ') 143 | .description('update configuration') 144 | .action((type: 'sync') => { 145 | try { 146 | setConfig(type) 147 | } catch (error) { 148 | console.error(error) 149 | process.exit(1) 150 | } 151 | }) 152 | 153 | program 154 | .version(pkgJson.version) 155 | .command('sync') 156 | .description('sync config to remote') 157 | .action(() => { 158 | try { 159 | sync() 160 | } catch (error) { 161 | console.error(error) 162 | process.exit(1) 163 | } 164 | }) 165 | 166 | program 167 | .version(pkgJson.version) 168 | .command('current') 169 | .alias('cur') 170 | .description('get current git config in config file') 171 | .action(() => { 172 | try { 173 | current() 174 | } catch (error) { 175 | console.error(error) 176 | process.exit(1) 177 | } 178 | }) 179 | 180 | program.parse() 181 | -------------------------------------------------------------------------------- /packages/cli/src/libs/add.ts: -------------------------------------------------------------------------------- 1 | import inquirer from 'inquirer'; 2 | import pc from 'picocolors' 3 | import { addConfig, createEmptyJsonWhenNeeds, getAllUserConfigs } from '@lexmin0412/gcm-api' 4 | import { DEFAULT_ORIGINS } from '../constants'; 5 | 6 | const flatOrigins = DEFAULT_ORIGINS.map((item)=>item.origin) 7 | 8 | export const add = async() => { 9 | 10 | const { alias } = await inquirer 11 | .prompt([ 12 | { 13 | type: 'input', 14 | name: 'alias', 15 | message: '请输入别名', 16 | }]) 17 | 18 | const userList = getAllUserConfigs() 19 | const aliasExisted = userList.some((user)=>user.alias === alias) 20 | if (aliasExisted) { 21 | console.error(pc.red('别名已存在,请调整后重试哦~')) 22 | process.exit(1) 23 | } 24 | 25 | inquirer 26 | .prompt([ 27 | { 28 | type: 'input', 29 | name: 'name', 30 | message: '请输入用户名', 31 | }, 32 | { 33 | type: 'input', 34 | name: 'email', 35 | message: '请输入邮箱', 36 | }, 37 | { 38 | type: 'list', 39 | name: 'origin', 40 | message: '请选择适用于当前配置的 git 远程域名', 41 | choices: [ 42 | ...flatOrigins, 43 | 'custom' 44 | ] 45 | } 46 | ]) 47 | .then(async(answers) => { 48 | 49 | let origin = answers.origin 50 | 51 | if (answers.origin === 'custom') { 52 | const { customOrigin } = await inquirer.prompt([ 53 | { 54 | type: 'input', 55 | name: 'customOrigin', 56 | message: '请输入需要应用当前配置的 git 远程域名(如 github.com )', 57 | } 58 | ]) 59 | origin = customOrigin 60 | } 61 | 62 | createEmptyJsonWhenNeeds() 63 | addConfig({ 64 | alias: alias, 65 | name: answers.name, 66 | email: answers.email, 67 | origin: origin 68 | }) 69 | }) 70 | .catch((error) => { 71 | if (error.isTtyError) { 72 | // 当前运行环境不支持 73 | console.error(`Prompt couldn't be rendered in the current environment, please check your platform`) 74 | } else { 75 | console.error('error', error) 76 | process.exit(1) 77 | } 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /packages/cli/src/libs/config.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import inquirer from "inquirer" 3 | import { configJsonPath, readConfigs } from "@lexmin0412/gcm-api" 4 | import pc from 'picocolors' 5 | 6 | export const getConfig = async (type: 'sync') => { 7 | if (type !== 'sync') { 8 | console.error('不支持的配置类型, 请检查你的命令是否正确') 9 | process.exit(1) 10 | } 11 | const configContent = readConfigs() 12 | if (!configContent.sync) { 13 | console.log(pc.red('未找到同步配置')) 14 | process.exit(1) 15 | } 16 | console.log(pc.green('同步配置如下:')) 17 | console.table([{ 18 | type: configContent.sync.type, 19 | repoUrl: configContent.sync.repoUrl, 20 | dir: configContent.sync.dir, 21 | filename: configContent.sync.filename 22 | }]) 23 | process.exit(0) 24 | 25 | } 26 | 27 | export const setConfig = async (type: 'sync') => { 28 | // 获取命令中携带的标识参数 29 | if (type !== 'sync') { 30 | console.error('不支持的配置类型, 请检查你的命令是否正确') 31 | process.exit(1) 32 | } 33 | const { 34 | storageType, 35 | repoUrl, 36 | dir, 37 | filename 38 | } = await inquirer.prompt([ 39 | { 40 | type: 'list', 41 | choices: [ 42 | { 43 | name: 'Github 仓库', 44 | value: 'github' 45 | } 46 | ], 47 | name: 'storageType', 48 | message: '请选择存储类型' 49 | }, 50 | { 51 | type: 'input', 52 | name: 'repoUrl', 53 | message: '请输入仓库地址(支持 https 和 ssh)' 54 | }, 55 | { 56 | type: 'input', 57 | name: 'dir', 58 | message: '请输入子目录', 59 | default: 'gcm' 60 | }, 61 | { 62 | type: 'input', 63 | name: 'filename', 64 | message: '请输入文件名称', 65 | default: 'config.json' 66 | }, 67 | ]) 68 | const configContent = readConfigs() 69 | configContent.sync = { 70 | type: storageType, 71 | repoUrl, 72 | dir, 73 | filename 74 | } 75 | fs.writeFileSync(configJsonPath, JSON.stringify(configContent, null, 2)) 76 | console.log(pc.green('同步配置写入成功:')) 77 | const newConfigContent = readConfigs() 78 | console.table([{ 79 | type: newConfigContent.sync.type, 80 | repoUrl: newConfigContent.sync.repoUrl, 81 | dir: newConfigContent.sync.dir, 82 | filename: newConfigContent.sync.filename 83 | }]) 84 | 85 | } 86 | -------------------------------------------------------------------------------- /packages/cli/src/libs/current.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentConfig, getAllUserConfigs } from '@lexmin0412/gcm-api' 2 | import { UserConfig } from './../types' 3 | import pc from 'picocolors' 4 | 5 | export const current = () => { 6 | const allConfigs = getAllUserConfigs() 7 | const currentConfig = getCurrentConfig() 8 | const currentInConfigJson = allConfigs.find((config: UserConfig)=>{ 9 | return config.name === currentConfig.name && config.email === config.email 10 | }) 11 | if ( currentInConfigJson ) { 12 | console.log(pc.green(`当前使用的配置: 13 | user.name: ${currentConfig.name} 14 | user.email: ${currentConfig.email}`)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/cli/src/libs/doctor.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process' 2 | import { getAllUserConfigs, getCurrentConfig } from '@lexmin0412/gcm-api' 3 | import { UserConfig } from '../types' 4 | 5 | 6 | export const doctor = () => { 7 | const currentRemoteOrigin = execSync('git remote -v').toString().trim() 8 | if ( !currentRemoteOrigin ) { 9 | console.log('未检测到配置远程仓库,请确认是否位于项目根目录') 10 | } 11 | 12 | const currentRemote = currentRemoteOrigin.split('\n')[0] 13 | const allConfigs = getAllUserConfigs() 14 | const currentConfig = getCurrentConfig() 15 | const curMatchedItem: UserConfig | undefined = allConfigs.find((config: UserConfig) => currentRemote.includes(config.origin)) 16 | if ( curMatchedItem ) { 17 | if ( currentConfig.name === curMatchedItem.name && currentConfig.email === curMatchedItem.email ) { 18 | console.log(`当前配置 ${curMatchedItem.alias} 正确 19 | user.name: ${currentConfig.name} 20 | user.email: ${currentConfig.email} 21 | supported origin: ${curMatchedItem.origin} 22 | remote: ${currentRemote}`) 23 | } else { 24 | console.log(`当前配置 ${curMatchedItem.alias} 错误,请检查: 25 | user.name 应为 ${curMatchedItem.name},实际为 ${currentConfig.name} 26 | user.email 应为 ${curMatchedItem.email},实际为 ${currentConfig.email}`) 27 | } 28 | } else { 29 | console.log(`远程地址 ${currentRemote} 不存在于用户配置列表中,请使用 \`gcm list\` 查看所有配置,使用 \`gcm add\` 添加`) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/cli/src/libs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './add' 2 | export * from './current' 3 | export * from './doctor' 4 | export * from './list' 5 | export * from './remove' 6 | export * from './scan' 7 | export * from './upgrade' 8 | export * from './use' 9 | -------------------------------------------------------------------------------- /packages/cli/src/libs/list.ts: -------------------------------------------------------------------------------- 1 | import pc from 'picocolors' 2 | import { getAllUserConfigs, createEmptyJsonWhenNeeds, getCurrentConfig } from '@lexmin0412/gcm-api' 3 | import { UserConfig } from './../types' 4 | 5 | export const list = () => { 6 | createEmptyJsonWhenNeeds() 7 | const configs = getAllUserConfigs() 8 | const currentConfig = getCurrentConfig() 9 | console.log(`共 ${configs.length} 个配置`) 10 | configs.forEach((config: UserConfig) => { 11 | const configStr = ` 12 | alias: ${config.alias} 13 | name: ${config.name} 14 | email: ${config.email} 15 | ` 16 | if (config.name === currentConfig.name && config.email === currentConfig.email) { 17 | console.log(pc.green(configStr)); 18 | } else { 19 | console.log(configStr) 20 | } 21 | }); 22 | console.log(pc.yellow('可通过 `gcm add` 添加配置,通过 `gcm use ` 快速切换配置')) 23 | } 24 | -------------------------------------------------------------------------------- /packages/cli/src/libs/remove.ts: -------------------------------------------------------------------------------- 1 | import inquirer from 'inquirer'; 2 | import { removeConfig, createEmptyJsonWhenNeeds } from '@lexmin0412/gcm-api' 3 | 4 | export const remove = () => { 5 | inquirer 6 | .prompt([ 7 | { 8 | type: 'input', 9 | name: 'alias', 10 | message: '请输入别名', 11 | } 12 | ]) 13 | .then((answers) => { 14 | createEmptyJsonWhenNeeds() 15 | removeConfig(answers.alias) 16 | }) 17 | .catch((error) => { 18 | if (error.isTtyError) { 19 | // 当前运行环境不支持 20 | console.error(`Prompt couldn't be rendered in the current environment, please check your platform`) 21 | } else { 22 | console.error('error', error) 23 | process.exit(1) 24 | } 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/cli/src/libs/scan.ts: -------------------------------------------------------------------------------- 1 | 2 | import pc from 'picocolors' 3 | import inquirer from 'inquirer' 4 | import { readdirSync } from 'fs' 5 | import { getProjectConfig, getAllUserConfigs, getCurrentConfig } from '@lexmin0412/gcm-api' 6 | import { isDirectory } from '../utils' 7 | 8 | const homeDir = process.env.HOME 9 | const ignoredDirs = ['node_modules', 'dist', '.Trash'] 10 | const ignoredFiles = ['package-lock.json', 'yarn.lock', '.gitignore', '.git', '.DS_Store', '.vscode', 'package.json'] 11 | const ignoredPrefix = ['.'] 12 | const ignoredNoPermissionPaths = ['Library', 'Application Support', 'Pictures'] 13 | 14 | export const isValidUserConfig = (configStr: string) => { 15 | const allConfigs = getAllUserConfigs() 16 | return allConfigs.some((item)=>{ 17 | return `${item.name}<${item.email}>` === configStr 18 | }) 19 | } 20 | 21 | export const isCurrentConfig = (configStr: string) => { 22 | const currentConfig = getCurrentConfig() 23 | return `${currentConfig.name}<${currentConfig.email}>` === configStr 24 | } 25 | 26 | 27 | export const scan = () => { 28 | inquirer.prompt([ 29 | { 30 | type: 'input', 31 | name: 'dirPath', 32 | message: '请输入需要扫描的文件夹路径(建议指定目录以提升扫描效率)', 33 | default: homeDir 34 | } 35 | ]).then((answers) => { 36 | const dirPath = answers.dirPath 37 | 38 | let userConfigs: { 39 | [key: string]: number 40 | } = {} 41 | 42 | const readConfig = (filePath: string) => { 43 | if (isDirectory(filePath)) { 44 | const files = readdirSync(filePath) 45 | 46 | const isGitDir = () => { 47 | return files.some((file) => file === '.git') 48 | } 49 | 50 | if (isGitDir()) { 51 | try { 52 | const {name, email} = getProjectConfig(filePath) 53 | const stringifyConfig = `${name}<${email}>` 54 | if (!userConfigs[stringifyConfig]) { 55 | userConfigs[stringifyConfig] = 1 56 | } else { 57 | userConfigs[stringifyConfig] += 1 58 | } 59 | console.log(`${pc.green('扫描目录')} ${filePath} Git配置: ${stringifyConfig}`) 60 | } catch (error) { 61 | console.log('error', error) 62 | } 63 | } else { 64 | files.forEach((fileName: string) => { 65 | const fullPath = `${filePath}/${fileName}` 66 | if (![...ignoredDirs, ...ignoredFiles].includes(fileName) && !ignoredPrefix.some((prefix) => fileName.startsWith(prefix)) && !ignoredNoPermissionPaths.some((path) => fullPath.includes(path))) { 67 | readConfig(fullPath) 68 | } 69 | }) 70 | } 71 | } 72 | } 73 | 74 | readConfig(dirPath) 75 | 76 | const getAnalyzeRes = (userConfigs: Record) => { 77 | let res = '\n统计结果\n' 78 | Object.keys(userConfigs).forEach((key) => { 79 | 80 | let currentKey = key 81 | if (!isValidUserConfig(key)) { 82 | currentKey = `${pc.red(key)} 不在配置列表中,请检查是否正确` 83 | } 84 | if (isCurrentConfig(key)) { 85 | currentKey = `${pc.green(key)} ${pc.green('(当前配置)')}` 86 | } 87 | res = `${res}${pc.green(userConfigs[key])} 个目录使用了配置 ${currentKey}\n` 88 | }) 89 | return res 90 | } 91 | 92 | console.log(getAnalyzeRes(userConfigs)) 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /packages/cli/src/libs/sync/const.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import os from 'os' 3 | 4 | 5 | /** 6 | * GCM 配置目录 7 | */ 8 | const GCM_CONFIG_DIR = path.join(os.homedir(), ".gcm") 9 | /** 10 | * 临时工作目录名称 11 | */ 12 | export const TEMP_FOLDER_NAME = "sync_temp_workspace"; 13 | /** 14 | * 临时工作目录,用于处理配置同步工作 15 | */ 16 | export const TEMP_SYNC_DIR = path.join(GCM_CONFIG_DIR, TEMP_FOLDER_NAME); 17 | /** 18 | * 配置仓库名称 19 | */ 20 | export const CONFIG_REPO_NAME = "config"; 21 | /** 22 | * 仓库临时存放目录路径 23 | */ 24 | export const REPO_FOLDER_PATH = path.join(TEMP_SYNC_DIR, CONFIG_REPO_NAME); 25 | -------------------------------------------------------------------------------- /packages/cli/src/libs/sync/index.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { getAllUserConfigs, readConfigs } from "@lexmin0412/gcm-api"; 3 | import { REPO_FOLDER_PATH } from "./const"; 4 | import { cloneConfigRepo, pushConfig, writeConfigIntoLocalRepo } from "./steps"; 5 | import { isEqual } from "./utils"; 6 | import { createServerAndOpenPage } from "./server"; 7 | import path from "path"; 8 | 9 | // 获取本地用户配置 10 | const localUserConfigs = getAllUserConfigs(); 11 | 12 | /** 13 | * 同步配置主函数 14 | */ 15 | export const sync = async () => { 16 | // clone 用户配置仓库 17 | await cloneConfigRepo(); 18 | // 判断本地配置是否与远程配置一致 19 | const { sync } = readConfigs() 20 | const GCM_CONFIG_FILE_PATH = path.join(REPO_FOLDER_PATH, sync.dir, sync.filename); 21 | const remoteUserConfigs = JSON.parse(fs.readFileSync(GCM_CONFIG_FILE_PATH, "utf8")); 22 | if (!remoteUserConfigs?.length) { 23 | // 远程配置不存在,直接写入本地配置 24 | writeConfigIntoLocalRepo(localUserConfigs); 25 | await pushConfig(); 26 | } else if (isEqual(localUserConfigs, remoteUserConfigs)) { 27 | console.log("本地配置与远程配置一致,无需同步"); 28 | } else { 29 | console.log("本地配置与远程配置存在冲突,即将打开浏览器,请前往处理"); 30 | // 打开冲突处理页面 31 | const mergedConfig = await createServerAndOpenPage({ 32 | localConfig: localUserConfigs, 33 | remoteConfig: remoteUserConfigs, 34 | }) 35 | // 写入本地配置 36 | writeConfigIntoLocalRepo(mergedConfig); 37 | // 推送配置 38 | await pushConfig(); 39 | } 40 | process.exit(0); 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/cli/src/libs/sync/server.ts: -------------------------------------------------------------------------------- 1 | import http from "http"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | import puppeteer from "puppeteer"; 5 | import { UserConfig } from "../../types"; 6 | 7 | export const createServerAndOpenPage = async (options: { 8 | localConfig: UserConfig[], 9 | remoteConfig: UserConfig[], 10 | }) => { 11 | return new Promise((resolve, reject) => { 12 | // 创建HTTP服务器 13 | const server = http.createServer((req, res) => { 14 | // 处理根路径请求 15 | const purePath = req.url?.slice(0, req.url.indexOf("?")); 16 | if (!purePath || purePath === "/") { 17 | const html = fs.readFileSync( 18 | path.join(__dirname, "../../../server/index.html") 19 | ); 20 | res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); 21 | return res.end(html); 22 | } 23 | 24 | // 处理其他静态文件 25 | const filePath = path.join(__dirname, req.url as string); 26 | if (fs.existsSync(filePath)) { 27 | const content = fs.readFileSync(filePath); 28 | res.end(content); 29 | } else { 30 | res.writeHead(404); 31 | res.end("Not found"); 32 | } 33 | }); 34 | 35 | const port = 56789; 36 | const baseURL = `http://localhost:${port}`; 37 | const url = `${baseURL}?ws=ws://localhost:${port}`; 38 | 39 | /** 40 | * 打开冲突处理页面 41 | */ 42 | const openConflictWebPage = async (cb: (type: 'success', data: any) => void) => { 43 | const browser = await puppeteer.launch({ 44 | headless: false, 45 | }); 46 | const page = await browser.newPage(); 47 | await page.goto(url); 48 | // 给页面注入全局变量 49 | await page.addScriptTag({ 50 | content: ` 51 | window.GCM = { 52 | localConfig: ${JSON.stringify(options.localConfig)}, 53 | remoteConfig: ${JSON.stringify(options.remoteConfig)}, 54 | }; 55 | document.querySelector('#localConfig').value = JSON.stringify(window.GCM.localConfig, null, 2); 56 | document.querySelector('#remoteConfig').value = JSON.stringify(window.GCM.remoteConfig, null, 2); 57 | `, 58 | }); 59 | // 监听页面中的点击事件 60 | await page.exposeFunction("onClickEvent", (event: any) => { 61 | console.log("点击事件发生:", event); 62 | if (event.target === 'submit_btn') { 63 | 64 | // TODO 校验配置 65 | let parsedData = [] 66 | try { 67 | parsedData = JSON.parse(event.data) 68 | } catch (e) { 69 | console.log('解析失败', e) 70 | } 71 | if (!parsedData?.length) { 72 | console.error('配置为空,请检查配置是否正确') 73 | process.exit(1); 74 | } 75 | cb('success', parsedData) 76 | browser.close(); 77 | } 78 | }); 79 | 80 | // 在页面中注入 JavaScript 代码来监听点击事件 81 | await page.evaluate(() => { 82 | 83 | document.addEventListener("click", (event) => { 84 | console.log('合并输入框', document.querySelector('#mergedConfig')) 85 | console.log('合并输入框的值', document.querySelector('#mergedConfig')?.innerHTML) 86 | // 调用 Node.js 中暴露的函数 87 | // @ts-ignore 88 | window.onClickEvent({ 89 | // @ts-ignore 90 | target: event.target?.id, 91 | // @ts-ignore 92 | data: document.querySelector('#mergedConfig')?.value 93 | }); 94 | }); 95 | }); 96 | }; 97 | 98 | // 启动服务 99 | server.listen(port, async () => { 100 | console.log(`Server running at ${baseURL}`); 101 | // 获取本地配置内容 102 | openConflictWebPage((type, data) => { 103 | if (type == 'success') { 104 | // TODO 提交配置 105 | console.log('提交配置', data) 106 | resolve(data); 107 | } 108 | }); 109 | }); 110 | 111 | }) 112 | }; 113 | -------------------------------------------------------------------------------- /packages/cli/src/libs/sync/steps.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import inquirer from "inquirer"; 3 | import { CONFIG_REPO_NAME, REPO_FOLDER_PATH, TEMP_SYNC_DIR } from "./const"; 4 | import { execSync, type ExecSyncOptionsWithBufferEncoding } from "child_process"; 5 | import { UserConfig } from '../../types'; 6 | import { readConfigs } from '@lexmin0412/gcm-api'; 7 | import { setConfig as setSyncConfig } from '../config' 8 | import path from 'path'; 9 | 10 | /** 11 | * 执行命令行脚本的统一选项 12 | */ 13 | const EXEC_OPTIONS: ExecSyncOptionsWithBufferEncoding = { 14 | cwd: REPO_FOLDER_PATH, 15 | } 16 | 17 | /** 18 | * clone 远程配置仓库到本地 19 | */ 20 | export const cloneConfigRepo = async () => { 21 | console.log("开始下载远程配置"); 22 | 23 | // 如果已存在,则递归清除目录 24 | if (fs.existsSync(TEMP_SYNC_DIR)) { 25 | fs.rmSync(TEMP_SYNC_DIR, { recursive: true }); 26 | } 27 | // 创建临时工作目录 28 | fs.mkdirSync(TEMP_SYNC_DIR); 29 | 30 | // 获取用户配置仓库地址 31 | const { sync } = readConfigs() 32 | if (!sync?.repoUrl) { 33 | const { isContinue } = await inquirer.prompt([ 34 | { 35 | type: "confirm", 36 | name: "isContinue", 37 | message: "检测到同步配置不存在, 是否立即配置?", 38 | default: true, 39 | } 40 | ]) 41 | if (!isContinue) { 42 | console.log("已取消同步配置") 43 | process.exit(0) 44 | } 45 | await setSyncConfig('sync') 46 | } 47 | const { sync: newSyncConfig } = readConfigs() 48 | execSync(`git clone ${newSyncConfig.repoUrl} ${CONFIG_REPO_NAME}`, { 49 | cwd: TEMP_SYNC_DIR, 50 | }); 51 | console.log("下载远程配置成功"); 52 | }; 53 | 54 | /** 55 | * 将本地用户配置写入到临时仓库的配置文件中 56 | */ 57 | export const writeConfigIntoLocalRepo = async (config: UserConfig[]) => { 58 | console.log("开始写入配置到本地仓库"); 59 | const { sync } = readConfigs() 60 | const CONFIG_FOLDER_PATH = path.join(REPO_FOLDER_PATH, sync.dir); 61 | if (!fs.existsSync(CONFIG_FOLDER_PATH)) { 62 | fs.mkdirSync(CONFIG_FOLDER_PATH); 63 | } 64 | const GCM_CONFIG_FILE_PATH = path.join(CONFIG_FOLDER_PATH, sync.filename); 65 | // 判断 configPath 是否存在,否则新建文件 66 | if (!fs.existsSync(GCM_CONFIG_FILE_PATH)) { 67 | fs.writeFileSync(GCM_CONFIG_FILE_PATH, "{}"); 68 | } 69 | fs.writeFileSync(GCM_CONFIG_FILE_PATH, JSON.stringify(config, null, 2)); 70 | console.log("写入配置到本地仓库成功"); 71 | }; 72 | 73 | /** 74 | * 推送配置到远程 75 | */ 76 | export const pushConfig = async () => { 77 | console.log("开始推送配置到远程"); 78 | // 初始化正确的 git 配置 79 | const { name, email } = await inquirer.prompt([ 80 | { 81 | type: "input", 82 | name: "name", 83 | message: "请输入你的 git 用户名", 84 | }, 85 | { 86 | type: "input", 87 | name: "email", 88 | message: "请输入你的 git 邮箱", 89 | }, 90 | ]); 91 | console.log(`用户: ${name}, ${email}`) 92 | execSync(`git config user.name ${name}`, EXEC_OPTIONS); 93 | execSync(`git config user.email ${email}`, EXEC_OPTIONS); 94 | execSync("git add .", EXEC_OPTIONS); 95 | execSync('git commit -m "update gcm config"', EXEC_OPTIONS); 96 | execSync("git push origin main", EXEC_OPTIONS); 97 | console.log("推送配置到远程成功"); 98 | }; 99 | 100 | -------------------------------------------------------------------------------- /packages/cli/src/libs/sync/utils.ts: -------------------------------------------------------------------------------- 1 | import { UserConfig } from "../../types"; 2 | 3 | // 对比两份 GCM 配置是否一致 4 | export const isEqual = (local: UserConfig[], remote: UserConfig[]) => { 5 | const hash = (arr: UserConfig[]) => 6 | arr.map(c => `${c.alias}:${c.name}:${c.email}:${c.origin}`).join('|'); 7 | return hash(local) === hash(remote); 8 | }; 9 | -------------------------------------------------------------------------------- /packages/cli/src/libs/upgrade.ts: -------------------------------------------------------------------------------- 1 | import { runCmdSync } from '@lexmin0412/run' 2 | import latestVersion from "latest-version" 3 | import pc from 'picocolors' 4 | import inquirer from 'inquirer' 5 | import { gt } from 'semver' 6 | import { getPkgJson } from '@lexmin0412/gcm-api' 7 | 8 | export const upgrade = async() => { 9 | const pkgJson = getPkgJson() 10 | const newVersion = await latestVersion(pkgJson.name) 11 | const currentVersion = pkgJson.version 12 | 13 | if ( newVersion === currentVersion ) { 14 | console.log(pc.green(`当前已是最新版本: ${currentVersion}`)) 15 | process.exit(0) 16 | } 17 | 18 | if ( gt(newVersion, currentVersion) ) { 19 | inquirer.prompt([ 20 | { 21 | type: 'confirm', 22 | name: 'confirmed', 23 | message: '是否更新', 24 | default: false 25 | } 26 | ]).then((answers) => { 27 | const confirmed = answers.confirmed 28 | if (confirmed) { 29 | runCmdSync(`npm install ${pkgJson.name}@${newVersion} -g`); 30 | console.log(pc.green(`✓ 成功升级到 v${newVersion}`)); 31 | } else { 32 | console.log('canceled') 33 | process.exit(0) 34 | } 35 | }) 36 | } else { 37 | console.error('当前安装版本异常,请检查: ') 38 | console.log(`当前版本 ${currentVersion}`) 39 | console.log(`最新版本 ${newVersion}`) 40 | process.exit(1) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/cli/src/libs/use.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import { addConfig, createEmptyJsonWhenNeeds, getProjectConfig, configJsonPath } from '@lexmin0412/gcm-api' 3 | import { runCmdSync } from '@lexmin0412/run' 4 | import pc from 'picocolors' 5 | 6 | export const readConfigs = () => { 7 | const configs = JSON.parse(fs.readFileSync(configJsonPath, 'utf8')) 8 | return configs 9 | } 10 | 11 | export const getConfigByAlias = (alias: string) => { 12 | const configs = readConfigs() 13 | const config = configs.users.find((config: UserConfig)=>config.alias === alias) 14 | return config || null 15 | } 16 | 17 | interface UserConfig { 18 | alias: string 19 | name: string 20 | email: string 21 | } 22 | 23 | export const use = (alias: string) => { 24 | 25 | createEmptyJsonWhenNeeds() 26 | 27 | if ( !fs.existsSync(configJsonPath) ) { 28 | const currentConfig = getProjectConfig() 29 | console.error(`配置文件不存在,当前 git 配置为: 30 | user.name: ${currentConfig.name} 31 | user.email: ${currentConfig.email} 32 | 此配置将被写为默认配置,别名为 default 33 | 执行 \`gcm ls\` 查看配置列表`) 34 | 35 | const defaultConfig = { 36 | alias: 'default', 37 | email: currentConfig.email, 38 | name: currentConfig.name, 39 | origin: 'github.com' 40 | } 41 | addConfig(defaultConfig) 42 | process.exit(1) 43 | } else { 44 | const config = getConfigByAlias(alias) 45 | if ( !config ) { 46 | console.error(pc.red(`配置别名 ${alias} 不存在`)) 47 | process.exit(1) 48 | } else { 49 | setConfig(config) 50 | const currentConfig = getProjectConfig() 51 | console.log(pc.green(`当前 git 配置为: 52 | user.name: ${currentConfig.name} 53 | user.email: ${currentConfig.email}`)) 54 | } 55 | } 56 | } 57 | 58 | export const setConfig = (config: UserConfig) => { 59 | runCmdSync(`git config user.name ${config.name}`) 60 | runCmdSync(`git config user.email ${config.email}`) 61 | } 62 | -------------------------------------------------------------------------------- /packages/cli/src/notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": "为了保证您的用户体验,请升级至 v1.4.0 以上版本" 3 | } 4 | -------------------------------------------------------------------------------- /packages/cli/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface UserConfig { 2 | alias: string 3 | name: string 4 | email: string 5 | origin: string 6 | } 7 | 8 | export interface Origin { 9 | origin: string 10 | } 11 | -------------------------------------------------------------------------------- /packages/cli/src/utils/fs.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | /** 4 | * 判断一个path是否是文件夹 5 | */ 6 | export const isDirectory = (path: string) => { 7 | return fs.existsSync(path) && fs.lstatSync(path).isDirectory() 8 | } 9 | -------------------------------------------------------------------------------- /packages/cli/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as fs from 'fs' 3 | import * as os from 'os' 4 | import { execSync } from 'child_process' 5 | import { UserConfig } from './../types' 6 | import pc from 'picocolors' 7 | 8 | export * from './fs' 9 | 10 | const USER_HOME = os.homedir() 11 | 12 | export const rootPath = path.resolve(USER_HOME, '.gcm') 13 | 14 | if ( !fs.existsSync(rootPath) ) { 15 | fs.mkdirSync(rootPath) 16 | } 17 | 18 | export const configJsonPath = path.resolve(rootPath, 'config.json') 19 | 20 | export const createEmptyJsonWhenNeeds = () => { 21 | if (!fs.existsSync(configJsonPath)) { 22 | const users: UserConfig[] = [] 23 | fs.writeFileSync(configJsonPath, JSON.stringify({users}, null, 2)) 24 | } 25 | } 26 | 27 | export const getPkgJson = () => { 28 | return require(path.resolve(__dirname, '..', '..', 'package.json')) 29 | } 30 | 31 | export const getCurrentConfig = () => { 32 | const currentUserName = execSync('git config --get user.name').toString().trim() 33 | const currentUserEmail = execSync('git config --get user.email').toString().trim() 34 | return { 35 | name: currentUserName, 36 | email: currentUserEmail 37 | } 38 | } 39 | 40 | export const addConfig = (config: UserConfig) => { 41 | createEmptyJsonWhenNeeds() 42 | const configJson = require(configJsonPath) 43 | configJson.users.push(config) 44 | fs.writeFileSync(configJsonPath, JSON.stringify(configJson, null, 2)) 45 | console.log(pc.green('添加成功✅')) 46 | } 47 | 48 | export const removeConfig = (alias: string) => { 49 | if (!isConfigJsonExists()) { 50 | console.error(pc.red('配置文件不存在')) 51 | process.exit(1) 52 | } 53 | const configJson = require(configJsonPath) 54 | const targetUserConfigIndex = configJson.users.findIndex(((config: UserConfig)=>config.alias === alias)) 55 | if (targetUserConfigIndex === -1 ) { 56 | console.error(pc.red(`不存在别名为 ${alias} 的配置`)) 57 | process.exit(1) 58 | } 59 | configJson.users.splice(targetUserConfigIndex, 1) 60 | fs.writeFileSync(configJsonPath, JSON.stringify(configJson, null, 2)) 61 | console.log(pc.green(`配置 ${alias} 删除成功`)) 62 | } 63 | 64 | export const isConfigJsonExists = () => { 65 | const isExists = fs.existsSync(configJsonPath) 66 | return isExists 67 | } 68 | 69 | export const readConfigs = (): { 70 | users: UserConfig[] 71 | } => { 72 | createEmptyJsonWhenNeeds() 73 | const configs = JSON.parse(fs.readFileSync(configJsonPath, 'utf8')) 74 | return configs 75 | } 76 | 77 | export const getAllUserConfigs = () => { 78 | const configs = readConfigs() 79 | return configs.users 80 | } 81 | 82 | export const getConfigByAlias = (alias: string) => { 83 | const configs = readConfigs() 84 | const config = configs.users.find((config: UserConfig) => config.alias === alias) 85 | return config || null 86 | } 87 | 88 | export const getProjectConfig = (projectPath: string = process.cwd()) => { 89 | const currentUserName = execSync('git config --get user.name', { 90 | cwd: projectPath 91 | }).toString().trim() 92 | const currentUserEmail = execSync('git config --get user.email', { 93 | cwd: projectPath 94 | }).toString().trim() 95 | return { 96 | name: currentUserName, 97 | email: currentUserEmail 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "esModuleInterop": true, 5 | "lib": ["es6", "dom"], 6 | "alwaysStrict": true, 7 | "strictNullChecks": true, 8 | "noImplicitAny": true, 9 | "resolveJsonModule": true, 10 | "skipDefaultLibCheck": true, 11 | "skipLibCheck": true 12 | }, 13 | "includes": ["src/*"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/vsc-ext/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/vsc-ext/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | -------------------------------------------------------------------------------- /packages/vsc-ext/.npmrc: -------------------------------------------------------------------------------- 1 | enable-pre-post-scripts = true -------------------------------------------------------------------------------- /packages/vsc-ext/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/vsc-ext/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/test/**/*.js" 30 | ], 31 | "preLaunchTask": "${defaultBuildTask}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/vsc-ext/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /packages/vsc-ext/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/vsc-ext/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | src/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | -------------------------------------------------------------------------------- /packages/vsc-ext/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.8.0](https://github.com/lexmin0412/git-config-manager/compare/v1.7.1...v1.8.0) (2025-02-20) 7 | 8 | 9 | ### Features 10 | 11 | * **packages:** 迁移 gcm-vscode 仓库作为子包, 新增支持交互式切换配置 ([f08a999](https://github.com/lexmin0412/git-config-manager/commit/f08a99948d23adfebc4c595a1b0898c1174805b2)) 12 | * **vsc-ext:** add open command ([385a65a](https://github.com/lexmin0412/git-config-manager/commit/385a65aa92364fa82cef37975a470031acb5af0a)) 13 | 14 | 15 | 16 | 17 | 18 | ## 1.7.3 (2025-02-20) 19 | 20 | **Note:** Version bump only for package gcm-vscode 21 | 22 | 23 | 24 | 25 | 26 | ## 1.7.2 (2025-02-20) 27 | 28 | **Note:** Version bump only for package gcm-vscode 29 | 30 | 31 | 32 | 33 | 34 | # Change Log 35 | 36 | All notable changes to the "gcm-vscode" extension will be documented in this file. 37 | 38 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 39 | 40 | ## [Unreleased] 41 | 42 | - Initial release 43 | -------------------------------------------------------------------------------- /packages/vsc-ext/README.md: -------------------------------------------------------------------------------- 1 | # GCM VSCode 2 | 3 | GCM's VSCode extension. 4 | 5 | As a VSCode extension, `gcm-vscode` now has been published on marketplace, [click me](https://marketplace.visualstudio.com/items?itemName=lexmin0412.gcm-vscode) or type `lexmin0412.gcm-vscode` in search input in your VSCode's extension tab to install it. 6 | 7 | ![marketplace](./marketplace.png) 8 | 9 | Then click `Install` button and wait a second, your private git user config manager would be ready. 10 | 11 | ![preview](./preview.png) 12 | -------------------------------------------------------------------------------- /packages/vsc-ext/marketplace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexmin0412/git-config-manager/91b832f821c108489894807dd6096cc68362ef79/packages/vsc-ext/marketplace.png -------------------------------------------------------------------------------- /packages/vsc-ext/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gcm-vscode", 3 | "displayName": "GCM VSCode", 4 | "description": "GCM 的 VSCode 插件", 5 | "private": true, 6 | "version": "1.8.0", 7 | "publisher": "lexmin0412", 8 | "engines": { 9 | "vscode": "^1.75.0" 10 | }, 11 | "categories": [ 12 | "Other" 13 | ], 14 | "repository": { 15 | "type": "github", 16 | "url": "https://github.com/lexmin0412/git-config-manager", 17 | "directory": "packages/vsc-ext" 18 | }, 19 | "activationEvents": [ 20 | "*" 21 | ], 22 | "main": "./out/extension.js", 23 | "contributes": { 24 | "commands": [ 25 | { 26 | "command": "gcm-vscode.use", 27 | "title": "GCM: Use defined config" 28 | }, 29 | { 30 | "command": "gcm-vscode.open", 31 | "title": "GCM: Open current file on remote" 32 | } 33 | ] 34 | }, 35 | "scripts": { 36 | "vsce:package": "vsce package", 37 | "vsce:publish": "vsce publish", 38 | "vscode:prepublish": "pnpm run compile", 39 | "compile": "tsc -p ./", 40 | "watch": "tsc -watch -p ./", 41 | "pretest": "pnpm run compile && pnpm run lint", 42 | "lint": "eslint src --ext ts", 43 | "test": "node ./out/test/runTest.js" 44 | }, 45 | "devDependencies": { 46 | "@types/glob": "^8.1.0", 47 | "@types/mocha": "^10.0.1", 48 | "@types/node": "16.x", 49 | "@types/vscode": "^1.75.0", 50 | "@typescript-eslint/eslint-plugin": "^5.56.0", 51 | "@typescript-eslint/parser": "^5.56.0", 52 | "@vscode/test-electron": "^2.3.0", 53 | "@vscode/vsce": "^2.26.1", 54 | "eslint": "^8.36.0", 55 | "glob": "^8.1.0", 56 | "mocha": "^10.2.0", 57 | "typescript": "^4.9.5" 58 | }, 59 | "dependencies": { 60 | "@lexmin0412/gcm-api": "workspace:*" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/vsc-ext/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | devDependencies: 8 | '@types/glob': 9 | specifier: ^8.1.0 10 | version: 8.1.0 11 | '@types/mocha': 12 | specifier: ^10.0.1 13 | version: 10.0.1 14 | '@types/node': 15 | specifier: 16.x 16 | version: 16.18.23 17 | '@types/vscode': 18 | specifier: ^1.75.0 19 | version: 1.77.0 20 | '@typescript-eslint/eslint-plugin': 21 | specifier: ^5.56.0 22 | version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@4.9.5) 23 | '@typescript-eslint/parser': 24 | specifier: ^5.56.0 25 | version: 5.57.1(eslint@8.37.0)(typescript@4.9.5) 26 | '@vscode/test-electron': 27 | specifier: ^2.3.0 28 | version: 2.3.0 29 | eslint: 30 | specifier: ^8.36.0 31 | version: 8.37.0 32 | glob: 33 | specifier: ^8.1.0 34 | version: 8.1.0 35 | mocha: 36 | specifier: ^10.2.0 37 | version: 10.2.0 38 | typescript: 39 | specifier: ^4.9.5 40 | version: 4.9.5 41 | 42 | packages: 43 | 44 | /@eslint-community/eslint-utils@4.4.0(eslint@8.37.0): 45 | resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} 46 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 47 | peerDependencies: 48 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 49 | dependencies: 50 | eslint: 8.37.0 51 | eslint-visitor-keys: 3.4.0 52 | dev: true 53 | 54 | /@eslint-community/regexpp@4.5.0: 55 | resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} 56 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 57 | dev: true 58 | 59 | /@eslint/eslintrc@2.0.2: 60 | resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==} 61 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 62 | dependencies: 63 | ajv: 6.12.6 64 | debug: 4.3.4(supports-color@8.1.1) 65 | espree: 9.5.1 66 | globals: 13.20.0 67 | ignore: 5.2.4 68 | import-fresh: 3.3.0 69 | js-yaml: 4.1.0 70 | minimatch: 3.1.2 71 | strip-json-comments: 3.1.1 72 | transitivePeerDependencies: 73 | - supports-color 74 | dev: true 75 | 76 | /@eslint/js@8.37.0: 77 | resolution: {integrity: sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==} 78 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 79 | dev: true 80 | 81 | /@humanwhocodes/config-array@0.11.8: 82 | resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} 83 | engines: {node: '>=10.10.0'} 84 | dependencies: 85 | '@humanwhocodes/object-schema': 1.2.1 86 | debug: 4.3.4(supports-color@8.1.1) 87 | minimatch: 3.1.2 88 | transitivePeerDependencies: 89 | - supports-color 90 | dev: true 91 | 92 | /@humanwhocodes/module-importer@1.0.1: 93 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 94 | engines: {node: '>=12.22'} 95 | dev: true 96 | 97 | /@humanwhocodes/object-schema@1.2.1: 98 | resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} 99 | dev: true 100 | 101 | /@nodelib/fs.scandir@2.1.5: 102 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 103 | engines: {node: '>= 8'} 104 | dependencies: 105 | '@nodelib/fs.stat': 2.0.5 106 | run-parallel: 1.2.0 107 | dev: true 108 | 109 | /@nodelib/fs.stat@2.0.5: 110 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 111 | engines: {node: '>= 8'} 112 | dev: true 113 | 114 | /@nodelib/fs.walk@1.2.8: 115 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 116 | engines: {node: '>= 8'} 117 | dependencies: 118 | '@nodelib/fs.scandir': 2.1.5 119 | fastq: 1.15.0 120 | dev: true 121 | 122 | /@tootallnate/once@1.1.2: 123 | resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} 124 | engines: {node: '>= 6'} 125 | dev: true 126 | 127 | /@types/glob@8.1.0: 128 | resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} 129 | dependencies: 130 | '@types/minimatch': 5.1.2 131 | '@types/node': 16.18.23 132 | dev: true 133 | 134 | /@types/json-schema@7.0.11: 135 | resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} 136 | dev: true 137 | 138 | /@types/minimatch@5.1.2: 139 | resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} 140 | dev: true 141 | 142 | /@types/mocha@10.0.1: 143 | resolution: {integrity: sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==} 144 | dev: true 145 | 146 | /@types/node@16.18.23: 147 | resolution: {integrity: sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==} 148 | dev: true 149 | 150 | /@types/semver@7.3.13: 151 | resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} 152 | dev: true 153 | 154 | /@types/vscode@1.77.0: 155 | resolution: {integrity: sha512-MWFN5R7a33n8eJZJmdVlifjig3LWUNRrPeO1xemIcZ0ae0TEQuRc7G2xV0LUX78RZFECY1plYBn+dP/Acc3L0Q==} 156 | dev: true 157 | 158 | /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@4.9.5): 159 | resolution: {integrity: sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ==} 160 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 161 | peerDependencies: 162 | '@typescript-eslint/parser': ^5.0.0 163 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 164 | typescript: '*' 165 | peerDependenciesMeta: 166 | typescript: 167 | optional: true 168 | dependencies: 169 | '@eslint-community/regexpp': 4.5.0 170 | '@typescript-eslint/parser': 5.57.1(eslint@8.37.0)(typescript@4.9.5) 171 | '@typescript-eslint/scope-manager': 5.57.1 172 | '@typescript-eslint/type-utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) 173 | '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) 174 | debug: 4.3.4(supports-color@8.1.1) 175 | eslint: 8.37.0 176 | grapheme-splitter: 1.0.4 177 | ignore: 5.2.4 178 | natural-compare-lite: 1.4.0 179 | semver: 7.3.8 180 | tsutils: 3.21.0(typescript@4.9.5) 181 | typescript: 4.9.5 182 | transitivePeerDependencies: 183 | - supports-color 184 | dev: true 185 | 186 | /@typescript-eslint/parser@5.57.1(eslint@8.37.0)(typescript@4.9.5): 187 | resolution: {integrity: sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA==} 188 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 189 | peerDependencies: 190 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 191 | typescript: '*' 192 | peerDependenciesMeta: 193 | typescript: 194 | optional: true 195 | dependencies: 196 | '@typescript-eslint/scope-manager': 5.57.1 197 | '@typescript-eslint/types': 5.57.1 198 | '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) 199 | debug: 4.3.4(supports-color@8.1.1) 200 | eslint: 8.37.0 201 | typescript: 4.9.5 202 | transitivePeerDependencies: 203 | - supports-color 204 | dev: true 205 | 206 | /@typescript-eslint/scope-manager@5.57.1: 207 | resolution: {integrity: sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==} 208 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 209 | dependencies: 210 | '@typescript-eslint/types': 5.57.1 211 | '@typescript-eslint/visitor-keys': 5.57.1 212 | dev: true 213 | 214 | /@typescript-eslint/type-utils@5.57.1(eslint@8.37.0)(typescript@4.9.5): 215 | resolution: {integrity: sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw==} 216 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 217 | peerDependencies: 218 | eslint: '*' 219 | typescript: '*' 220 | peerDependenciesMeta: 221 | typescript: 222 | optional: true 223 | dependencies: 224 | '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) 225 | '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) 226 | debug: 4.3.4(supports-color@8.1.1) 227 | eslint: 8.37.0 228 | tsutils: 3.21.0(typescript@4.9.5) 229 | typescript: 4.9.5 230 | transitivePeerDependencies: 231 | - supports-color 232 | dev: true 233 | 234 | /@typescript-eslint/types@5.57.1: 235 | resolution: {integrity: sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==} 236 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 237 | dev: true 238 | 239 | /@typescript-eslint/typescript-estree@5.57.1(typescript@4.9.5): 240 | resolution: {integrity: sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==} 241 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 242 | peerDependencies: 243 | typescript: '*' 244 | peerDependenciesMeta: 245 | typescript: 246 | optional: true 247 | dependencies: 248 | '@typescript-eslint/types': 5.57.1 249 | '@typescript-eslint/visitor-keys': 5.57.1 250 | debug: 4.3.4(supports-color@8.1.1) 251 | globby: 11.1.0 252 | is-glob: 4.0.3 253 | semver: 7.3.8 254 | tsutils: 3.21.0(typescript@4.9.5) 255 | typescript: 4.9.5 256 | transitivePeerDependencies: 257 | - supports-color 258 | dev: true 259 | 260 | /@typescript-eslint/utils@5.57.1(eslint@8.37.0)(typescript@4.9.5): 261 | resolution: {integrity: sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==} 262 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 263 | peerDependencies: 264 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 265 | dependencies: 266 | '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0) 267 | '@types/json-schema': 7.0.11 268 | '@types/semver': 7.3.13 269 | '@typescript-eslint/scope-manager': 5.57.1 270 | '@typescript-eslint/types': 5.57.1 271 | '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) 272 | eslint: 8.37.0 273 | eslint-scope: 5.1.1 274 | semver: 7.3.8 275 | transitivePeerDependencies: 276 | - supports-color 277 | - typescript 278 | dev: true 279 | 280 | /@typescript-eslint/visitor-keys@5.57.1: 281 | resolution: {integrity: sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==} 282 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 283 | dependencies: 284 | '@typescript-eslint/types': 5.57.1 285 | eslint-visitor-keys: 3.4.0 286 | dev: true 287 | 288 | /@vscode/test-electron@2.3.0: 289 | resolution: {integrity: sha512-fwzA9RtazH1GT/sckYlbxu6t5e4VaMXwCVtyLv4UAG0hP6NTfnMaaG25XCfWqlVwFhBMcQXHBCy5dmz2eLUnkw==} 290 | engines: {node: '>=16'} 291 | dependencies: 292 | http-proxy-agent: 4.0.1 293 | https-proxy-agent: 5.0.1 294 | jszip: 3.10.1 295 | semver: 7.3.8 296 | transitivePeerDependencies: 297 | - supports-color 298 | dev: true 299 | 300 | /acorn-jsx@5.3.2(acorn@8.8.2): 301 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 302 | peerDependencies: 303 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 304 | dependencies: 305 | acorn: 8.8.2 306 | dev: true 307 | 308 | /acorn@8.8.2: 309 | resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} 310 | engines: {node: '>=0.4.0'} 311 | hasBin: true 312 | dev: true 313 | 314 | /agent-base@6.0.2: 315 | resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} 316 | engines: {node: '>= 6.0.0'} 317 | dependencies: 318 | debug: 4.3.4(supports-color@8.1.1) 319 | transitivePeerDependencies: 320 | - supports-color 321 | dev: true 322 | 323 | /ajv@6.12.6: 324 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 325 | dependencies: 326 | fast-deep-equal: 3.1.3 327 | fast-json-stable-stringify: 2.1.0 328 | json-schema-traverse: 0.4.1 329 | uri-js: 4.4.1 330 | dev: true 331 | 332 | /ansi-colors@4.1.1: 333 | resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} 334 | engines: {node: '>=6'} 335 | dev: true 336 | 337 | /ansi-regex@5.0.1: 338 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 339 | engines: {node: '>=8'} 340 | dev: true 341 | 342 | /ansi-styles@4.3.0: 343 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 344 | engines: {node: '>=8'} 345 | dependencies: 346 | color-convert: 2.0.1 347 | dev: true 348 | 349 | /anymatch@3.1.3: 350 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 351 | engines: {node: '>= 8'} 352 | dependencies: 353 | normalize-path: 3.0.0 354 | picomatch: 2.3.1 355 | dev: true 356 | 357 | /argparse@2.0.1: 358 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 359 | dev: true 360 | 361 | /array-union@2.1.0: 362 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 363 | engines: {node: '>=8'} 364 | dev: true 365 | 366 | /balanced-match@1.0.2: 367 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 368 | dev: true 369 | 370 | /binary-extensions@2.2.0: 371 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 372 | engines: {node: '>=8'} 373 | dev: true 374 | 375 | /brace-expansion@1.1.11: 376 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 377 | dependencies: 378 | balanced-match: 1.0.2 379 | concat-map: 0.0.1 380 | dev: true 381 | 382 | /brace-expansion@2.0.1: 383 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 384 | dependencies: 385 | balanced-match: 1.0.2 386 | dev: true 387 | 388 | /braces@3.0.2: 389 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 390 | engines: {node: '>=8'} 391 | dependencies: 392 | fill-range: 7.0.1 393 | dev: true 394 | 395 | /browser-stdout@1.3.1: 396 | resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} 397 | dev: true 398 | 399 | /callsites@3.1.0: 400 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 401 | engines: {node: '>=6'} 402 | dev: true 403 | 404 | /camelcase@6.3.0: 405 | resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} 406 | engines: {node: '>=10'} 407 | dev: true 408 | 409 | /chalk@4.1.2: 410 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 411 | engines: {node: '>=10'} 412 | dependencies: 413 | ansi-styles: 4.3.0 414 | supports-color: 7.2.0 415 | dev: true 416 | 417 | /chokidar@3.5.3: 418 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 419 | engines: {node: '>= 8.10.0'} 420 | dependencies: 421 | anymatch: 3.1.3 422 | braces: 3.0.2 423 | glob-parent: 5.1.2 424 | is-binary-path: 2.1.0 425 | is-glob: 4.0.3 426 | normalize-path: 3.0.0 427 | readdirp: 3.6.0 428 | optionalDependencies: 429 | fsevents: 2.3.2 430 | dev: true 431 | 432 | /cliui@7.0.4: 433 | resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 434 | dependencies: 435 | string-width: 4.2.3 436 | strip-ansi: 6.0.1 437 | wrap-ansi: 7.0.0 438 | dev: true 439 | 440 | /color-convert@2.0.1: 441 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 442 | engines: {node: '>=7.0.0'} 443 | dependencies: 444 | color-name: 1.1.4 445 | dev: true 446 | 447 | /color-name@1.1.4: 448 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 449 | dev: true 450 | 451 | /concat-map@0.0.1: 452 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 453 | dev: true 454 | 455 | /core-util-is@1.0.3: 456 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 457 | dev: true 458 | 459 | /cross-spawn@7.0.3: 460 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 461 | engines: {node: '>= 8'} 462 | dependencies: 463 | path-key: 3.1.1 464 | shebang-command: 2.0.0 465 | which: 2.0.2 466 | dev: true 467 | 468 | /debug@4.3.4(supports-color@8.1.1): 469 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 470 | engines: {node: '>=6.0'} 471 | peerDependencies: 472 | supports-color: '*' 473 | peerDependenciesMeta: 474 | supports-color: 475 | optional: true 476 | dependencies: 477 | ms: 2.1.2 478 | supports-color: 8.1.1 479 | dev: true 480 | 481 | /decamelize@4.0.0: 482 | resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} 483 | engines: {node: '>=10'} 484 | dev: true 485 | 486 | /deep-is@0.1.4: 487 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 488 | dev: true 489 | 490 | /diff@5.0.0: 491 | resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} 492 | engines: {node: '>=0.3.1'} 493 | dev: true 494 | 495 | /dir-glob@3.0.1: 496 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 497 | engines: {node: '>=8'} 498 | dependencies: 499 | path-type: 4.0.0 500 | dev: true 501 | 502 | /doctrine@3.0.0: 503 | resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 504 | engines: {node: '>=6.0.0'} 505 | dependencies: 506 | esutils: 2.0.3 507 | dev: true 508 | 509 | /emoji-regex@8.0.0: 510 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 511 | dev: true 512 | 513 | /escalade@3.1.1: 514 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 515 | engines: {node: '>=6'} 516 | dev: true 517 | 518 | /escape-string-regexp@4.0.0: 519 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 520 | engines: {node: '>=10'} 521 | dev: true 522 | 523 | /eslint-scope@5.1.1: 524 | resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 525 | engines: {node: '>=8.0.0'} 526 | dependencies: 527 | esrecurse: 4.3.0 528 | estraverse: 4.3.0 529 | dev: true 530 | 531 | /eslint-scope@7.1.1: 532 | resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} 533 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 534 | dependencies: 535 | esrecurse: 4.3.0 536 | estraverse: 5.3.0 537 | dev: true 538 | 539 | /eslint-visitor-keys@3.4.0: 540 | resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} 541 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 542 | dev: true 543 | 544 | /eslint@8.37.0: 545 | resolution: {integrity: sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==} 546 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 547 | hasBin: true 548 | dependencies: 549 | '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0) 550 | '@eslint-community/regexpp': 4.5.0 551 | '@eslint/eslintrc': 2.0.2 552 | '@eslint/js': 8.37.0 553 | '@humanwhocodes/config-array': 0.11.8 554 | '@humanwhocodes/module-importer': 1.0.1 555 | '@nodelib/fs.walk': 1.2.8 556 | ajv: 6.12.6 557 | chalk: 4.1.2 558 | cross-spawn: 7.0.3 559 | debug: 4.3.4(supports-color@8.1.1) 560 | doctrine: 3.0.0 561 | escape-string-regexp: 4.0.0 562 | eslint-scope: 7.1.1 563 | eslint-visitor-keys: 3.4.0 564 | espree: 9.5.1 565 | esquery: 1.5.0 566 | esutils: 2.0.3 567 | fast-deep-equal: 3.1.3 568 | file-entry-cache: 6.0.1 569 | find-up: 5.0.0 570 | glob-parent: 6.0.2 571 | globals: 13.20.0 572 | grapheme-splitter: 1.0.4 573 | ignore: 5.2.4 574 | import-fresh: 3.3.0 575 | imurmurhash: 0.1.4 576 | is-glob: 4.0.3 577 | is-path-inside: 3.0.3 578 | js-sdsl: 4.4.0 579 | js-yaml: 4.1.0 580 | json-stable-stringify-without-jsonify: 1.0.1 581 | levn: 0.4.1 582 | lodash.merge: 4.6.2 583 | minimatch: 3.1.2 584 | natural-compare: 1.4.0 585 | optionator: 0.9.1 586 | strip-ansi: 6.0.1 587 | strip-json-comments: 3.1.1 588 | text-table: 0.2.0 589 | transitivePeerDependencies: 590 | - supports-color 591 | dev: true 592 | 593 | /espree@9.5.1: 594 | resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} 595 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 596 | dependencies: 597 | acorn: 8.8.2 598 | acorn-jsx: 5.3.2(acorn@8.8.2) 599 | eslint-visitor-keys: 3.4.0 600 | dev: true 601 | 602 | /esquery@1.5.0: 603 | resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} 604 | engines: {node: '>=0.10'} 605 | dependencies: 606 | estraverse: 5.3.0 607 | dev: true 608 | 609 | /esrecurse@4.3.0: 610 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 611 | engines: {node: '>=4.0'} 612 | dependencies: 613 | estraverse: 5.3.0 614 | dev: true 615 | 616 | /estraverse@4.3.0: 617 | resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} 618 | engines: {node: '>=4.0'} 619 | dev: true 620 | 621 | /estraverse@5.3.0: 622 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 623 | engines: {node: '>=4.0'} 624 | dev: true 625 | 626 | /esutils@2.0.3: 627 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 628 | engines: {node: '>=0.10.0'} 629 | dev: true 630 | 631 | /fast-deep-equal@3.1.3: 632 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 633 | dev: true 634 | 635 | /fast-glob@3.2.12: 636 | resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} 637 | engines: {node: '>=8.6.0'} 638 | dependencies: 639 | '@nodelib/fs.stat': 2.0.5 640 | '@nodelib/fs.walk': 1.2.8 641 | glob-parent: 5.1.2 642 | merge2: 1.4.1 643 | micromatch: 4.0.5 644 | dev: true 645 | 646 | /fast-json-stable-stringify@2.1.0: 647 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 648 | dev: true 649 | 650 | /fast-levenshtein@2.0.6: 651 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 652 | dev: true 653 | 654 | /fastq@1.15.0: 655 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 656 | dependencies: 657 | reusify: 1.0.4 658 | dev: true 659 | 660 | /file-entry-cache@6.0.1: 661 | resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 662 | engines: {node: ^10.12.0 || >=12.0.0} 663 | dependencies: 664 | flat-cache: 3.0.4 665 | dev: true 666 | 667 | /fill-range@7.0.1: 668 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 669 | engines: {node: '>=8'} 670 | dependencies: 671 | to-regex-range: 5.0.1 672 | dev: true 673 | 674 | /find-up@5.0.0: 675 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 676 | engines: {node: '>=10'} 677 | dependencies: 678 | locate-path: 6.0.0 679 | path-exists: 4.0.0 680 | dev: true 681 | 682 | /flat-cache@3.0.4: 683 | resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} 684 | engines: {node: ^10.12.0 || >=12.0.0} 685 | dependencies: 686 | flatted: 3.2.7 687 | rimraf: 3.0.2 688 | dev: true 689 | 690 | /flat@5.0.2: 691 | resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} 692 | hasBin: true 693 | dev: true 694 | 695 | /flatted@3.2.7: 696 | resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} 697 | dev: true 698 | 699 | /fs.realpath@1.0.0: 700 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 701 | dev: true 702 | 703 | /fsevents@2.3.2: 704 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 705 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 706 | os: [darwin] 707 | requiresBuild: true 708 | dev: true 709 | optional: true 710 | 711 | /get-caller-file@2.0.5: 712 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 713 | engines: {node: 6.* || 8.* || >= 10.*} 714 | dev: true 715 | 716 | /glob-parent@5.1.2: 717 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 718 | engines: {node: '>= 6'} 719 | dependencies: 720 | is-glob: 4.0.3 721 | dev: true 722 | 723 | /glob-parent@6.0.2: 724 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 725 | engines: {node: '>=10.13.0'} 726 | dependencies: 727 | is-glob: 4.0.3 728 | dev: true 729 | 730 | /glob@7.2.0: 731 | resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} 732 | dependencies: 733 | fs.realpath: 1.0.0 734 | inflight: 1.0.6 735 | inherits: 2.0.4 736 | minimatch: 3.1.2 737 | once: 1.4.0 738 | path-is-absolute: 1.0.1 739 | dev: true 740 | 741 | /glob@8.1.0: 742 | resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} 743 | engines: {node: '>=12'} 744 | dependencies: 745 | fs.realpath: 1.0.0 746 | inflight: 1.0.6 747 | inherits: 2.0.4 748 | minimatch: 5.1.6 749 | once: 1.4.0 750 | dev: true 751 | 752 | /globals@13.20.0: 753 | resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} 754 | engines: {node: '>=8'} 755 | dependencies: 756 | type-fest: 0.20.2 757 | dev: true 758 | 759 | /globby@11.1.0: 760 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 761 | engines: {node: '>=10'} 762 | dependencies: 763 | array-union: 2.1.0 764 | dir-glob: 3.0.1 765 | fast-glob: 3.2.12 766 | ignore: 5.2.4 767 | merge2: 1.4.1 768 | slash: 3.0.0 769 | dev: true 770 | 771 | /grapheme-splitter@1.0.4: 772 | resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} 773 | dev: true 774 | 775 | /has-flag@4.0.0: 776 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 777 | engines: {node: '>=8'} 778 | dev: true 779 | 780 | /he@1.2.0: 781 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 782 | hasBin: true 783 | dev: true 784 | 785 | /http-proxy-agent@4.0.1: 786 | resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} 787 | engines: {node: '>= 6'} 788 | dependencies: 789 | '@tootallnate/once': 1.1.2 790 | agent-base: 6.0.2 791 | debug: 4.3.4(supports-color@8.1.1) 792 | transitivePeerDependencies: 793 | - supports-color 794 | dev: true 795 | 796 | /https-proxy-agent@5.0.1: 797 | resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} 798 | engines: {node: '>= 6'} 799 | dependencies: 800 | agent-base: 6.0.2 801 | debug: 4.3.4(supports-color@8.1.1) 802 | transitivePeerDependencies: 803 | - supports-color 804 | dev: true 805 | 806 | /ignore@5.2.4: 807 | resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} 808 | engines: {node: '>= 4'} 809 | dev: true 810 | 811 | /immediate@3.0.6: 812 | resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} 813 | dev: true 814 | 815 | /import-fresh@3.3.0: 816 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 817 | engines: {node: '>=6'} 818 | dependencies: 819 | parent-module: 1.0.1 820 | resolve-from: 4.0.0 821 | dev: true 822 | 823 | /imurmurhash@0.1.4: 824 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 825 | engines: {node: '>=0.8.19'} 826 | dev: true 827 | 828 | /inflight@1.0.6: 829 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 830 | dependencies: 831 | once: 1.4.0 832 | wrappy: 1.0.2 833 | dev: true 834 | 835 | /inherits@2.0.4: 836 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 837 | dev: true 838 | 839 | /is-binary-path@2.1.0: 840 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 841 | engines: {node: '>=8'} 842 | dependencies: 843 | binary-extensions: 2.2.0 844 | dev: true 845 | 846 | /is-extglob@2.1.1: 847 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 848 | engines: {node: '>=0.10.0'} 849 | dev: true 850 | 851 | /is-fullwidth-code-point@3.0.0: 852 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 853 | engines: {node: '>=8'} 854 | dev: true 855 | 856 | /is-glob@4.0.3: 857 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 858 | engines: {node: '>=0.10.0'} 859 | dependencies: 860 | is-extglob: 2.1.1 861 | dev: true 862 | 863 | /is-number@7.0.0: 864 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 865 | engines: {node: '>=0.12.0'} 866 | dev: true 867 | 868 | /is-path-inside@3.0.3: 869 | resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} 870 | engines: {node: '>=8'} 871 | dev: true 872 | 873 | /is-plain-obj@2.1.0: 874 | resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} 875 | engines: {node: '>=8'} 876 | dev: true 877 | 878 | /is-unicode-supported@0.1.0: 879 | resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} 880 | engines: {node: '>=10'} 881 | dev: true 882 | 883 | /isarray@1.0.0: 884 | resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 885 | dev: true 886 | 887 | /isexe@2.0.0: 888 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 889 | dev: true 890 | 891 | /js-sdsl@4.4.0: 892 | resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} 893 | dev: true 894 | 895 | /js-yaml@4.1.0: 896 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 897 | hasBin: true 898 | dependencies: 899 | argparse: 2.0.1 900 | dev: true 901 | 902 | /json-schema-traverse@0.4.1: 903 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 904 | dev: true 905 | 906 | /json-stable-stringify-without-jsonify@1.0.1: 907 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 908 | dev: true 909 | 910 | /jszip@3.10.1: 911 | resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} 912 | dependencies: 913 | lie: 3.3.0 914 | pako: 1.0.11 915 | readable-stream: 2.3.8 916 | setimmediate: 1.0.5 917 | dev: true 918 | 919 | /levn@0.4.1: 920 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 921 | engines: {node: '>= 0.8.0'} 922 | dependencies: 923 | prelude-ls: 1.2.1 924 | type-check: 0.4.0 925 | dev: true 926 | 927 | /lie@3.3.0: 928 | resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} 929 | dependencies: 930 | immediate: 3.0.6 931 | dev: true 932 | 933 | /locate-path@6.0.0: 934 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 935 | engines: {node: '>=10'} 936 | dependencies: 937 | p-locate: 5.0.0 938 | dev: true 939 | 940 | /lodash.merge@4.6.2: 941 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 942 | dev: true 943 | 944 | /log-symbols@4.1.0: 945 | resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} 946 | engines: {node: '>=10'} 947 | dependencies: 948 | chalk: 4.1.2 949 | is-unicode-supported: 0.1.0 950 | dev: true 951 | 952 | /lru-cache@6.0.0: 953 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 954 | engines: {node: '>=10'} 955 | dependencies: 956 | yallist: 4.0.0 957 | dev: true 958 | 959 | /merge2@1.4.1: 960 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 961 | engines: {node: '>= 8'} 962 | dev: true 963 | 964 | /micromatch@4.0.5: 965 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 966 | engines: {node: '>=8.6'} 967 | dependencies: 968 | braces: 3.0.2 969 | picomatch: 2.3.1 970 | dev: true 971 | 972 | /minimatch@3.1.2: 973 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 974 | dependencies: 975 | brace-expansion: 1.1.11 976 | dev: true 977 | 978 | /minimatch@5.0.1: 979 | resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} 980 | engines: {node: '>=10'} 981 | dependencies: 982 | brace-expansion: 2.0.1 983 | dev: true 984 | 985 | /minimatch@5.1.6: 986 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 987 | engines: {node: '>=10'} 988 | dependencies: 989 | brace-expansion: 2.0.1 990 | dev: true 991 | 992 | /mocha@10.2.0: 993 | resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} 994 | engines: {node: '>= 14.0.0'} 995 | hasBin: true 996 | dependencies: 997 | ansi-colors: 4.1.1 998 | browser-stdout: 1.3.1 999 | chokidar: 3.5.3 1000 | debug: 4.3.4(supports-color@8.1.1) 1001 | diff: 5.0.0 1002 | escape-string-regexp: 4.0.0 1003 | find-up: 5.0.0 1004 | glob: 7.2.0 1005 | he: 1.2.0 1006 | js-yaml: 4.1.0 1007 | log-symbols: 4.1.0 1008 | minimatch: 5.0.1 1009 | ms: 2.1.3 1010 | nanoid: 3.3.3 1011 | serialize-javascript: 6.0.0 1012 | strip-json-comments: 3.1.1 1013 | supports-color: 8.1.1 1014 | workerpool: 6.2.1 1015 | yargs: 16.2.0 1016 | yargs-parser: 20.2.4 1017 | yargs-unparser: 2.0.0 1018 | dev: true 1019 | 1020 | /ms@2.1.2: 1021 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 1022 | dev: true 1023 | 1024 | /ms@2.1.3: 1025 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1026 | dev: true 1027 | 1028 | /nanoid@3.3.3: 1029 | resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} 1030 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1031 | hasBin: true 1032 | dev: true 1033 | 1034 | /natural-compare-lite@1.4.0: 1035 | resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} 1036 | dev: true 1037 | 1038 | /natural-compare@1.4.0: 1039 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1040 | dev: true 1041 | 1042 | /normalize-path@3.0.0: 1043 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1044 | engines: {node: '>=0.10.0'} 1045 | dev: true 1046 | 1047 | /once@1.4.0: 1048 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 1049 | dependencies: 1050 | wrappy: 1.0.2 1051 | dev: true 1052 | 1053 | /optionator@0.9.1: 1054 | resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} 1055 | engines: {node: '>= 0.8.0'} 1056 | dependencies: 1057 | deep-is: 0.1.4 1058 | fast-levenshtein: 2.0.6 1059 | levn: 0.4.1 1060 | prelude-ls: 1.2.1 1061 | type-check: 0.4.0 1062 | word-wrap: 1.2.3 1063 | dev: true 1064 | 1065 | /p-limit@3.1.0: 1066 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1067 | engines: {node: '>=10'} 1068 | dependencies: 1069 | yocto-queue: 0.1.0 1070 | dev: true 1071 | 1072 | /p-locate@5.0.0: 1073 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1074 | engines: {node: '>=10'} 1075 | dependencies: 1076 | p-limit: 3.1.0 1077 | dev: true 1078 | 1079 | /pako@1.0.11: 1080 | resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} 1081 | dev: true 1082 | 1083 | /parent-module@1.0.1: 1084 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1085 | engines: {node: '>=6'} 1086 | dependencies: 1087 | callsites: 3.1.0 1088 | dev: true 1089 | 1090 | /path-exists@4.0.0: 1091 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1092 | engines: {node: '>=8'} 1093 | dev: true 1094 | 1095 | /path-is-absolute@1.0.1: 1096 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 1097 | engines: {node: '>=0.10.0'} 1098 | dev: true 1099 | 1100 | /path-key@3.1.1: 1101 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1102 | engines: {node: '>=8'} 1103 | dev: true 1104 | 1105 | /path-type@4.0.0: 1106 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 1107 | engines: {node: '>=8'} 1108 | dev: true 1109 | 1110 | /picomatch@2.3.1: 1111 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1112 | engines: {node: '>=8.6'} 1113 | dev: true 1114 | 1115 | /prelude-ls@1.2.1: 1116 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 1117 | engines: {node: '>= 0.8.0'} 1118 | dev: true 1119 | 1120 | /process-nextick-args@2.0.1: 1121 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 1122 | dev: true 1123 | 1124 | /punycode@2.3.0: 1125 | resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} 1126 | engines: {node: '>=6'} 1127 | dev: true 1128 | 1129 | /queue-microtask@1.2.3: 1130 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1131 | dev: true 1132 | 1133 | /randombytes@2.1.0: 1134 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 1135 | dependencies: 1136 | safe-buffer: 5.2.1 1137 | dev: true 1138 | 1139 | /readable-stream@2.3.8: 1140 | resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} 1141 | dependencies: 1142 | core-util-is: 1.0.3 1143 | inherits: 2.0.4 1144 | isarray: 1.0.0 1145 | process-nextick-args: 2.0.1 1146 | safe-buffer: 5.1.2 1147 | string_decoder: 1.1.1 1148 | util-deprecate: 1.0.2 1149 | dev: true 1150 | 1151 | /readdirp@3.6.0: 1152 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1153 | engines: {node: '>=8.10.0'} 1154 | dependencies: 1155 | picomatch: 2.3.1 1156 | dev: true 1157 | 1158 | /require-directory@2.1.1: 1159 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 1160 | engines: {node: '>=0.10.0'} 1161 | dev: true 1162 | 1163 | /resolve-from@4.0.0: 1164 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1165 | engines: {node: '>=4'} 1166 | dev: true 1167 | 1168 | /reusify@1.0.4: 1169 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1170 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1171 | dev: true 1172 | 1173 | /rimraf@3.0.2: 1174 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 1175 | hasBin: true 1176 | dependencies: 1177 | glob: 7.2.0 1178 | dev: true 1179 | 1180 | /run-parallel@1.2.0: 1181 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1182 | dependencies: 1183 | queue-microtask: 1.2.3 1184 | dev: true 1185 | 1186 | /safe-buffer@5.1.2: 1187 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 1188 | dev: true 1189 | 1190 | /safe-buffer@5.2.1: 1191 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 1192 | dev: true 1193 | 1194 | /semver@7.3.8: 1195 | resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} 1196 | engines: {node: '>=10'} 1197 | hasBin: true 1198 | dependencies: 1199 | lru-cache: 6.0.0 1200 | dev: true 1201 | 1202 | /serialize-javascript@6.0.0: 1203 | resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} 1204 | dependencies: 1205 | randombytes: 2.1.0 1206 | dev: true 1207 | 1208 | /setimmediate@1.0.5: 1209 | resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} 1210 | dev: true 1211 | 1212 | /shebang-command@2.0.0: 1213 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1214 | engines: {node: '>=8'} 1215 | dependencies: 1216 | shebang-regex: 3.0.0 1217 | dev: true 1218 | 1219 | /shebang-regex@3.0.0: 1220 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1221 | engines: {node: '>=8'} 1222 | dev: true 1223 | 1224 | /slash@3.0.0: 1225 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1226 | engines: {node: '>=8'} 1227 | dev: true 1228 | 1229 | /string-width@4.2.3: 1230 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1231 | engines: {node: '>=8'} 1232 | dependencies: 1233 | emoji-regex: 8.0.0 1234 | is-fullwidth-code-point: 3.0.0 1235 | strip-ansi: 6.0.1 1236 | dev: true 1237 | 1238 | /string_decoder@1.1.1: 1239 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 1240 | dependencies: 1241 | safe-buffer: 5.1.2 1242 | dev: true 1243 | 1244 | /strip-ansi@6.0.1: 1245 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1246 | engines: {node: '>=8'} 1247 | dependencies: 1248 | ansi-regex: 5.0.1 1249 | dev: true 1250 | 1251 | /strip-json-comments@3.1.1: 1252 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1253 | engines: {node: '>=8'} 1254 | dev: true 1255 | 1256 | /supports-color@7.2.0: 1257 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1258 | engines: {node: '>=8'} 1259 | dependencies: 1260 | has-flag: 4.0.0 1261 | dev: true 1262 | 1263 | /supports-color@8.1.1: 1264 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 1265 | engines: {node: '>=10'} 1266 | dependencies: 1267 | has-flag: 4.0.0 1268 | dev: true 1269 | 1270 | /text-table@0.2.0: 1271 | resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} 1272 | dev: true 1273 | 1274 | /to-regex-range@5.0.1: 1275 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1276 | engines: {node: '>=8.0'} 1277 | dependencies: 1278 | is-number: 7.0.0 1279 | dev: true 1280 | 1281 | /tslib@1.14.1: 1282 | resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} 1283 | dev: true 1284 | 1285 | /tsutils@3.21.0(typescript@4.9.5): 1286 | resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} 1287 | engines: {node: '>= 6'} 1288 | peerDependencies: 1289 | typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' 1290 | dependencies: 1291 | tslib: 1.14.1 1292 | typescript: 4.9.5 1293 | dev: true 1294 | 1295 | /type-check@0.4.0: 1296 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1297 | engines: {node: '>= 0.8.0'} 1298 | dependencies: 1299 | prelude-ls: 1.2.1 1300 | dev: true 1301 | 1302 | /type-fest@0.20.2: 1303 | resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 1304 | engines: {node: '>=10'} 1305 | dev: true 1306 | 1307 | /typescript@4.9.5: 1308 | resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} 1309 | engines: {node: '>=4.2.0'} 1310 | hasBin: true 1311 | dev: true 1312 | 1313 | /uri-js@4.4.1: 1314 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1315 | dependencies: 1316 | punycode: 2.3.0 1317 | dev: true 1318 | 1319 | /util-deprecate@1.0.2: 1320 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1321 | dev: true 1322 | 1323 | /which@2.0.2: 1324 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1325 | engines: {node: '>= 8'} 1326 | hasBin: true 1327 | dependencies: 1328 | isexe: 2.0.0 1329 | dev: true 1330 | 1331 | /word-wrap@1.2.3: 1332 | resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} 1333 | engines: {node: '>=0.10.0'} 1334 | dev: true 1335 | 1336 | /workerpool@6.2.1: 1337 | resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} 1338 | dev: true 1339 | 1340 | /wrap-ansi@7.0.0: 1341 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1342 | engines: {node: '>=10'} 1343 | dependencies: 1344 | ansi-styles: 4.3.0 1345 | string-width: 4.2.3 1346 | strip-ansi: 6.0.1 1347 | dev: true 1348 | 1349 | /wrappy@1.0.2: 1350 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1351 | dev: true 1352 | 1353 | /y18n@5.0.8: 1354 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 1355 | engines: {node: '>=10'} 1356 | dev: true 1357 | 1358 | /yallist@4.0.0: 1359 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 1360 | dev: true 1361 | 1362 | /yargs-parser@20.2.4: 1363 | resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} 1364 | engines: {node: '>=10'} 1365 | dev: true 1366 | 1367 | /yargs-unparser@2.0.0: 1368 | resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} 1369 | engines: {node: '>=10'} 1370 | dependencies: 1371 | camelcase: 6.3.0 1372 | decamelize: 4.0.0 1373 | flat: 5.0.2 1374 | is-plain-obj: 2.1.0 1375 | dev: true 1376 | 1377 | /yargs@16.2.0: 1378 | resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 1379 | engines: {node: '>=10'} 1380 | dependencies: 1381 | cliui: 7.0.4 1382 | escalade: 3.1.1 1383 | get-caller-file: 2.0.5 1384 | require-directory: 2.1.1 1385 | string-width: 4.2.3 1386 | y18n: 5.0.8 1387 | yargs-parser: 20.2.4 1388 | dev: true 1389 | 1390 | /yocto-queue@0.1.0: 1391 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1392 | engines: {node: '>=10'} 1393 | dev: true 1394 | -------------------------------------------------------------------------------- /packages/vsc-ext/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexmin0412/git-config-manager/91b832f821c108489894807dd6096cc68362ef79/packages/vsc-ext/preview.png -------------------------------------------------------------------------------- /packages/vsc-ext/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { execSync } from 'child_process'; 3 | import { getAllUserConfigs } from '@lexmin0412/gcm-api' 4 | 5 | let myStatusBarItem: vscode.StatusBarItem; 6 | const EVENTS = { 7 | use: 'gcm-vscode.use', 8 | open: 'gcm-vscode.open', 9 | }; 10 | 11 | // 初始化状态栏 12 | const initStatusBar = async (context: vscode.ExtensionContext) => { 13 | const { subscriptions } = context; 14 | const currentEditorPath = vscode.window.activeTextEditor?.document.uri.path 15 | const workDir = currentEditorPath?.slice(0, currentEditorPath.lastIndexOf('/')) 16 | const userName = await execSync('git config user.name', { 17 | cwd: workDir 18 | }).toString().trim(); 19 | const userEmail = await execSync('git config user.email', { 20 | cwd: workDir 21 | }).toString().trim(); 22 | myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 300); 23 | myStatusBarItem.command = EVENTS.use; // 点击时执行命令 24 | myStatusBarItem.text = `${userName}, ${userEmail}`; 25 | subscriptions.push(myStatusBarItem); 26 | myStatusBarItem.show(); 27 | }; 28 | 29 | // 注册事件 30 | const registerCommand = (context: vscode.ExtensionContext) => { 31 | let disposable = vscode.commands.registerCommand(EVENTS.use, async () => { 32 | 33 | // 打开一个选择框,让用户选择 34 | const userConfig = getAllUserConfigs() 35 | const selectOptions = userConfig.map((item=>item.alias)) 36 | const selected = await vscode.window.showQuickPick(selectOptions, { 37 | placeHolder: '请选择你的配置', 38 | }) 39 | const selectedItem = userConfig.find(item=>item.alias === selected) 40 | // 更新已经存在的状态栏文字 41 | myStatusBarItem.text = `${selectedItem?.name}, ${selectedItem?.email}` 42 | 43 | vscode.window.showInformationMessage('gcm use 执行成功!'); 44 | }); 45 | context.subscriptions.push(disposable); 46 | }; 47 | 48 | const registerOpenFileCommand = (context: vscode.ExtensionContext) => { 49 | let disposable = vscode.commands.registerCommand(EVENTS.open, async() => { 50 | 51 | // 获取当前聚焦的文件路径 52 | const currentEditorPath = vscode.window.activeTextEditor?.document.uri.path 53 | console.log('currentEditorPath', currentEditorPath) 54 | 55 | // 获取仓库 base 56 | const repoDomain = await execSync('git remote get-url origin', {}).toString().trim() 57 | console.log('repoDomain', repoDomain) 58 | 59 | // 打开一个外部链接 60 | vscode.env.openExternal(vscode.Uri.parse(`https://github.com/lexmin0412`)) 61 | }) 62 | context.subscriptions.push(disposable); 63 | } 64 | 65 | // 扩展激活 66 | export async function activate(context: vscode.ExtensionContext) { 67 | registerCommand(context); 68 | registerOpenFileCommand(context); 69 | await initStatusBar(context); 70 | } 71 | 72 | // 扩展销毁 73 | export function deactivate() {} 74 | -------------------------------------------------------------------------------- /packages/vsc-ext/src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests', err); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /packages/vsc-ext/src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/vsc-ext/src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /packages/vsc-ext/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2020" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/vsc-ext/vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | ## Explore the API 25 | 26 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 27 | 28 | ## Run tests 29 | 30 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 31 | * Press `F5` to run the tests in a new window with your extension loaded. 32 | * See the output of the test result in the debug console. 33 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. 34 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 35 | * You can create folders inside the `test` folder to structure your tests any way you want. 36 | 37 | ## Go further 38 | 39 | * [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns. 40 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). 41 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace. 42 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 43 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "esModuleInterop": true, 5 | "lib": ["es6", "dom"], 6 | "alwaysStrict": true, 7 | "strictNullChecks": true, 8 | "noImplicitAny": true, 9 | "resolveJsonModule": true 10 | }, 11 | "includes": ["packages/*"] 12 | } 13 | --------------------------------------------------------------------------------