diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2d9cac1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: npm # See documentation for possible values + directory: / # Location of package manifests + schedule: + interval: weekly diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index df27140..04b2e9d 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -8,7 +8,7 @@ on: jobs: build: runs-on: ubuntu-latest - if: github.repository == 'EternalHeartTeam/leetcode-practice' + if: github.repository == 'EternalHeartTeam/leetcode-practice' && github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v3 # Setup .npmrc file to publish to npm diff --git a/.husky/pre-commit b/.husky/pre-commit index 992a33b..496ac5e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,13 +1,7 @@ #!/usr/bin/env sh # 导入 Husky 脚本 . "$(dirname -- "$0")/_/husky.sh" - -# 检查是否处于 rebase 状态 -if [ -z "$GIT_DIR" ]; then - # 处于 rebase 状态,退出脚本 - exit 0 -fi - -# 不处于 rebase 状态,执行 pre-commit 脚本 +# 如果要执行 rebase 需要跳过当前所有hooks +# todo-highlight HUSKY=0 git rebase ... echo "Running pre-commit script..." -npm run lint && npm run format \ No newline at end of file +npm run lint && npm run format && git add . \ No newline at end of file diff --git a/.release-it.json b/.release-it.json index 2d90a60..cece73f 100644 --- a/.release-it.json +++ b/.release-it.json @@ -5,8 +5,9 @@ "autoGenerate": true }, "git": { - "commitMessage": "feat: v${version}", + "commitMessage": "chore: release v${version}", "tagName": "cli-v${version}", + "branch": "stable-v${version}", "tag": true, "push": true, "pushArgs": ["--follow-tags"] diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b8642..31647b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,29 @@ +## [1.0.8](https://github.com/wh131462/leetcode-practice/compare/cli-v1.0.7...cli-v1.0.8) (2024-03-14) +### Bug Fixes -## [1.0.7](https://github.com/wh131462/leetcode-practice/compare/cli-v1.0.6...cli-v1.0.7) (2024-03-12) +- page size ([f3fd27c](https://github.com/wh131462/leetcode-practice/commit/f3fd27cc31a4d82dc9e8641a865a750b173f43f5)) +- page size ([1905454](https://github.com/wh131462/leetcode-practice/commit/1905454d71ece043da1badc3a604c72f23995ae5)) + +### Features + +- add fetch qurtion list ([4485182](https://github.com/wh131462/leetcode-practice/commit/44851825eb92b2f1da98f1bb78c71c3f0fe27856)) +- lk check question by file ([bec7081](https://github.com/wh131462/leetcode-practice/commit/bec708107f67a2ce6c1d361911f1f9ce2ed3ed26)) +- 获取全部学习计划题目 ([c8ec9dc](https://github.com/wh131462/leetcode-practice/commit/c8ec9dc82f7310759fa949dce771aa665906e2f8)) +## [1.0.7](https://github.com/wh131462/leetcode-practice/compare/cli-v1.0.6...cli-v1.0.7) (2024-03-12) ### Bug Fixes -* change plan list question title to cn ([57f15d5](https://github.com/wh131462/leetcode-practice/commit/57f15d5befc23d11ce6211c30e3ee7a7e8bbe52d)) -* change plan list question title to cn ([cb6fbfd](https://github.com/wh131462/leetcode-practice/commit/cb6fbfd4cd783f57d031abe3add9d85f3e271a4c)) -* change the config ([2b9bb11](https://github.com/wh131462/leetcode-practice/commit/2b9bb1149c6db0dbede22f10830ccd8661b0a83f)) -* delete hot100 add study plan list ([e94aaa1](https://github.com/wh131462/leetcode-practice/commit/e94aaa166e80e7e54f3ce5b570352cb798799b77)) -* delete hot100 add study plan list ([9d7a22f](https://github.com/wh131462/leetcode-practice/commit/9d7a22fde8cc72264766f707ea3f51e81956c111)) -* get all is not success ([9108219](https://github.com/wh131462/leetcode-practice/commit/91082196dc005b0b23314966a09304ba948416b8)) -* get all is not success ([3f5bfef](https://github.com/wh131462/leetcode-practice/commit/3f5bfef7857e6c9869c40e3d12a60d50b14be2cd)) -* update realm version to fix windows env ([00d2bc4](https://github.com/wh131462/leetcode-practice/commit/00d2bc4942b2b02f97ad41d23da64f8bedc23741)) -* use logger instead console ([fddc458](https://github.com/wh131462/leetcode-practice/commit/fddc4581efbd5186a17aa1415aa2738283f820b7)) +- change plan list question title to cn ([57f15d5](https://github.com/wh131462/leetcode-practice/commit/57f15d5befc23d11ce6211c30e3ee7a7e8bbe52d)) +- change plan list question title to cn ([cb6fbfd](https://github.com/wh131462/leetcode-practice/commit/cb6fbfd4cd783f57d031abe3add9d85f3e271a4c)) +- change the config ([2b9bb11](https://github.com/wh131462/leetcode-practice/commit/2b9bb1149c6db0dbede22f10830ccd8661b0a83f)) +- delete hot100 add study plan list ([e94aaa1](https://github.com/wh131462/leetcode-practice/commit/e94aaa166e80e7e54f3ce5b570352cb798799b77)) +- delete hot100 add study plan list ([9d7a22f](https://github.com/wh131462/leetcode-practice/commit/9d7a22fde8cc72264766f707ea3f51e81956c111)) +- get all is not success ([9108219](https://github.com/wh131462/leetcode-practice/commit/91082196dc005b0b23314966a09304ba948416b8)) +- get all is not success ([3f5bfef](https://github.com/wh131462/leetcode-practice/commit/3f5bfef7857e6c9869c40e3d12a60d50b14be2cd)) +- update realm version to fix windows env ([00d2bc4](https://github.com/wh131462/leetcode-practice/commit/00d2bc4942b2b02f97ad41d23da64f8bedc23741)) +- use logger instead console ([fddc458](https://github.com/wh131462/leetcode-practice/commit/fddc4581efbd5186a17aa1415aa2738283f820b7)) ## [1.0.6](https://github.com/wh131462/leetcode-practice/compare/cli-v1.0.5...cli-v1.0.6) (2024-03-11) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..da2a273 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,23 @@ +# CONTRIBUTORS + +If you would like to contribute to our project, please carefully read the following development conventions. Only when we have a consensus can our project get better and better. + +## Contributing Code + +### 1. Branch Management + +Do not develop on the `master` branch. The `master` branch is only for merging pull requests and releasing versions. For daily development, please use the `dev` branch. For feature development, please create a feature branch named `feat-xxx`. Similarly, for bug fixes, please create a fix branch named `fix-xxx`. + +### 2. Commit Process + +After completing development and committing on the `dev` branch or a `feat-xxx` branch, please use `git rebase origin/master` for local merging. After resolving all code conflicts locally, submit a pull request to members with permission for timely merging. + +## 3. About Feature and Dev Branches + +By default, feature and fix branches will be deleted after being merged into `master` via pull request. + +The `dev` branch will not be deleted. + +## References + +Reference Link: [Branch Management](https://www.ruanyifeng.com/blog/2012/07/git.html) diff --git a/CONTRIBUTORS_CN.md b/CONTRIBUTORS_CN.md new file mode 100644 index 0000000..b63f050 --- /dev/null +++ b/CONTRIBUTORS_CN.md @@ -0,0 +1,23 @@ +# CONTRIBUTORS + +如果你也想参与我们的项目共建,请详细阅读以下开发约定,只有大家拥有一个共识,我们的项目才会越来越好, + +## 贡献代码 + +### 1. 分支管理 + +不要在`master`分支进行开发, `master`分支只进行`pr合并`与`发布版本`, 如果是日常开发, 请在`dev`分支进行开发, 如果是`新特性`开发,请创建特性分支`feat-xxx`,同理,`修复bug`, 请创建修复分支`fix-xxx`. + +### 2. 提交流程 + +在`dev`分支或者`feat-xxx`分支`开发完成`并`提交commit`之后, 请使用`git rebase origin/master`进行`本地合并`,在本地解决完成所有的代码冲突之后,再进行`pr请求`,`发送请求`到有权限的成员,会及时进行合并. + +## 3. 关于特性分支和dev分支 + +默认`特性分支`和`修复分支`提交`pr`合并到`master`之后,会进行`删除`分支. + +`dev`分支不会进行删除. + +## 参考信息 + +参考链接: [分支管理](https://www.ruanyifeng.com/blog/2012/07/git.html) diff --git a/README.md b/README.md index c337ed5..50be8a3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Leetcode practice -[中文文档](./README_CN.md)|**English Document**||[日本語の文書](./README_JP.md) +[中文](./README_CN.md) · **English** ·[日本語](./README_JP.md) ## I. Project Information @@ -282,7 +282,11 @@ The development and improvement of the project would not be possible without the <a href="https://github.com/EternalHeartTeam/leetcode-practice/graphs/contributors" target="_blank"><img src="https://raw.githubusercontent.com/EternalHeartTeam/leetcode-practice/svg/images/contributors.png" /></a> -### 2. Feedback +### 2. How to Contribute + +If you share a passion for open source and would like to contribute to our open source initiative, please refer to our [contribution guidelines](./CONTRIBUTORS.md). + +### 3. Feedback If you have any questions about usage or would like to offer some suggestions, feel free to join our feedback group! @@ -290,6 +294,6 @@ Engage in face-to-face discussions with developers in the group, hoping to reson  -### 3. Star Trend Chart +### 4. Star Trend Chart [](https://star-history.com/#EternalHeartTeam/leetcode-practice&Date) diff --git a/README_CN.md b/README_CN.md index 0d364a1..3171959 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,6 +1,6 @@ # Leetcode practice -**中文文档**|[English Document](./README.md)|[日本語の文書](./README_JP.md) +**中文** · [English](./README.md) · [日本語](./README_JP.md) ## I.项目信息 @@ -287,7 +287,11 @@ npm i --save-dev leetcode-practice <a href="https://github.com/EternalHeartTeam/leetcode-practice/graphs/contributors" target="_blank"><img src="https://raw.githubusercontent.com/EternalHeartTeam/leetcode-practice/svg/images/contributors.png" /></a> -### 2.使用反馈 +### 2.如何贡献 + +如果你也有一颗热爱开源的心,想要为我们的开源事业贡献一份力量,那么请参考我们的[贡献手册](./CONTRIBUTORS.md)。 + +### 3.使用反馈 如果你有使用上的问题需要解惑,或者一些好的建议想要提出,可以加我们的使用反馈群进行反馈! @@ -295,6 +299,6 @@ npm i --save-dev leetcode-practice  -### 3.Star趋势图 +### 4.Star趋势图 [](https://star-history.com/#EternalHeartTeam/leetcode-practice&Date) diff --git a/README_JP.md b/README_JP.md index ae0b4dd..f856320 100644 --- a/README_JP.md +++ b/README_JP.md @@ -1,6 +1,6 @@ # Leetcode practice -[中文文档](./README_CN.md)|[English Document](./README.md)|**日本語の文書** +[中文](./README_CN.md) · [English](./README.md) · **日本語** ## I.プロジェクト情報 @@ -278,7 +278,11 @@ npm i --save-dev leetcode-practice <a href="https://github.com/EternalHeartTeam/leetcode-practice/graphs/contributors" target="_blank"><img src="https://raw.githubusercontent.com/EternalHeartTeam/leetcode-practice/svg/images/contributors.png" /></a> -### 2. 使用フィードバック +### 2. 貢献方法 + +もしオープンソースに対する情熱を持ち、私たちのオープンソースイニシアチブに貢献したいとお考えでしたら、[貢献ガイドライン](./CONTRIBUTORS.md) を参照してください。 + +### 3. 使用フィードバック 使用上の問題がある場合や、提案がある場合は、使用フィードバックグループにご参加ください! @@ -286,6 +290,6 @@ npm i --save-dev leetcode-practice  -### 3. Starのトレンドチャート +### 4. Starのトレンドチャート [](https://star-history.com/#EternalHeartTeam/leetcode-practice&Date) diff --git a/bin/lc.js b/bin/lc.js index bc79697..d0000a2 100755 --- a/bin/lc.js +++ b/bin/lc.js @@ -1,27 +1,21 @@ #! /usr/bin/env node -import path from 'node:path' import { program } from 'commander' import { artFontLogo } from '#resources/text/art-font-logo.js' import { lcExamples } from '#resources/text/examples.js' import { love } from '#resources/text/love.js' import { aim } from '#resources/text/aim.js' -import { referMode } from '#common/utils/create-check/refer-mode.js' -import { getArgs } from '#common/utils/create-check/get-args.js' +import { referMode } from '#common/utils/cli-utils/referMode.js' +import { getArgs } from '#common/utils/cli-utils/getArgs.js' import { getQuestionToday } from '#common/utils/question-getter/getQuestionToday.js' import { getQuestionRandom } from '#common/utils/question-getter/getQuestionRandom.js' import { easyCreateView } from '#common/view/create.view.js' import { description } from '#resources/text/description.js' -import { easyUpdateView } from '#common/view/update.view.js' -import { getQuestionLanguage } from '#common/utils/question-handler/questionLanguage.js' -import { easyLanguageView } from '#common/view/language.view.js' import { DefaultVer } from '#common/constants/question.const.js' -import { - create, - createQuestionById -} from '#common/utils/create-check/createUtil.js' -import { logger } from '#common/utils/logger/logger.js' +import { createQuestionById } from '#common/utils/cli-utils/createQuestion.js' +import { create } from '#common/utils/cli-utils/create.js' +import { commonMode } from '#common/utils/cli-utils/commonMode.js' const version = process.env.VERSION ?? DefaultVer program @@ -43,41 +37,8 @@ program const cmdArgs = program.args const cmdOpts = program.opts() -/** - * 执行逻辑: - * 目录检测 - 设置基础目录 - * 模式检测 - 检测是不是easy mode - * [参数检测 - 执行对应参数] - */ -/** - * 语言设置 - * -带参设置语言 - * -无参获取语言 - */ -if (cmdOpts.language) { - if (cmdOpts.language !== true) { - await easyLanguageView(cmdOpts.language) - } else { - const lang = await getQuestionLanguage() - logger.info(`当前CLI语言环境为:${lang}`) - } - process.exit(0) -} -// 根据dir 参数来设置基本目录 -const baseDir = cmdOpts.directory - ? path.join(process.cwd(), cmdOpts.directory) - : process.cwd() -if (cmdOpts.easy) { - await easyCreateView() - process.exit(0) -} -// 检测更新 -if (cmdOpts.update) { - await easyUpdateView() - process.exit(0) -} -// 创建 - +// 通用参数执行 +const baseDir = await commonMode(cmdOpts, easyCreateView) // 模式对应的action export const callModeAction = { today: () => { diff --git a/bin/lf.js b/bin/lf.js index 2c7a58e..fe13730 100755 --- a/bin/lf.js +++ b/bin/lf.js @@ -1,16 +1,14 @@ #! /usr/bin/env node -import path from 'node:path' import { program } from 'commander' import { description } from '#resources/text/description.js' import { artFontLogo } from '#resources/text/art-font-logo.js' import { aim } from '#resources/text/aim.js' import { lfExamples } from '#resources/text/examples.js' import { love } from '#resources/text/love.js' -import { easyUpdateView } from '#common/view/update.view.js' -import { easyLanguageView } from '#common/view/language.view.js' -import { getQuestionLanguage } from '#common/utils/question-handler/questionLanguage.js' import { DefaultVer } from '#common/constants/question.const.js' import { easyFinderView } from '#common/view/finder.view.js' +import { commonMode } from '#common/utils/cli-utils/commonMode.js' +import { willUse } from '#common/utils/etc/willUse.js' const version = process.env.VERSION ?? DefaultVer program @@ -25,32 +23,9 @@ program ) .parse(process.argv) -// const cmdArgs = program.args +const cmdArgs = program.args const cmdOpts = program.opts() - -/** - * 语言设置 - * -带参设置语言 - * -无参获取语言 - */ -if (cmdOpts.language) { - if (cmdOpts.language !== true) { - await easyLanguageView(cmdOpts.language) - } else { - const lang = await getQuestionLanguage() - logger.info(`当前CLI语言环境为:${lang}`) - } - process.exit(0) -} -// 根据dir 参数来设置基本目录 -const baseDir = cmdOpts.directory - ? path.join(process.cwd(), cmdOpts.directory) - : process.cwd() -// 检测更新 -if (cmdOpts.update) { - await easyUpdateView() - process.exit(0) -} -// 进入视图操作 -await easyFinderView(baseDir) +// 通用参数执行 +const baseDir = await commonMode(cmdOpts, easyFinderView) +willUse(cmdArgs, baseDir) process.exit(0) diff --git a/bin/lk.js b/bin/lk.js index 931aac6..69e2245 100755 --- a/bin/lk.js +++ b/bin/lk.js @@ -2,23 +2,25 @@ import fs from 'node:fs' import path from 'node:path' import { program } from 'commander' +import select from '@inquirer/select' import { artFontLogo } from '#resources/text/art-font-logo.js' import { lkExamples } from '#resources/text/examples.js' import { love } from '#resources/text/love.js' import { aim } from '#resources/text/aim.js' -import { referMode } from '#common/utils/create-check/refer-mode.js' -import { getArgs } from '#common/utils/create-check/get-args.js' -import { checkQuestion } from '#common/utils/question-handler/checkQuestion.js' +import { referMode } from '#common/utils/cli-utils/referMode.js' +import { getArgs } from '#common/utils/cli-utils/getArgs.js' +import { checkQuestionByPath } from '#common/utils/question-handler/checkQuestionByPath.js' import { getQuestionByMode } from '#common/utils/store/controller/question.js' -import { getQuestionById } from '#common/utils/question-getter/getQuestionById.js' import { getQuestionFileName } from '#common/utils/question-handler/getQuestionFileName.js' import { getQuestionChineseName } from '#common/utils/question-handler/getQuestionChineseName.js' import { easyCheckView } from '#common/view/check.view.js' import { description } from '#resources/text/description.js' -import { easyUpdateView } from '#common/view/update.view.js' import { getQuestionFileExtension } from '#common/utils/question-handler/questionLanguage.js' -import { DefaultVer } from '#common/constants/question.const.js' +import { DefaultLang, DefaultVer } from '#common/constants/question.const.js' import { logger } from '#common/utils/logger/logger.js' +import { commonMode } from '#common/utils/cli-utils/commonMode.js' +import { getFilePathById } from '#common/utils/file/getFilePathById.js' +import { getQuestionFileInDir } from '#common/utils/file/getQuestionInDir.js' const version = process.env.VERSION ?? DefaultVer program @@ -44,43 +46,10 @@ program const cmdArgs = program.args const cmdOpts = program.opts() // 获取模式和参数 - const mode = referMode(cmdArgs, cmdOpts) const args = getArgs(mode, cmdArgs, cmdOpts) - -/** - * 执行逻辑: - * 目录检测 - 设置基础目录 - * 模式检测 - 检测是不是easy mode - * [参数检测 - 执行对应参数] - */ -/** - * 语言设置 - * -带参设置语言 - * -无参获取语言 - */ -if (cmdOpts.language) { - if (cmdOpts.language !== true) { - await easyLanguageView(cmdOpts.language) - } else { - const lang = await getQuestionLanguage() - logger.info(`当前CLI语言环境为:${lang}`) - } - process.exit(0) -} -// 根据dir 参数来设置基本目录 -const baseDir = cmdOpts.directory - ? path.join(process.cwd(), cmdOpts.directory) - : process.cwd() -if (cmdOpts.easy) { - await easyCheckView() - process.exit(0) -} -// 检测更新 -if (cmdOpts.update) { - await easyUpdateView() - process.exit(0) -} +// 通用参数执行 +const baseDir = await commonMode(cmdOpts, easyCheckView) // 检测函数 async function check(mode, question) { if (!question) { @@ -98,7 +67,7 @@ async function check(mode, question) { logger.info( `MODE: ${mode}\n题目[${getQuestionChineseName(question)}]检测结果:` ) - await checkQuestion(filePath) + await checkQuestionByPath(filePath) } return true } @@ -115,13 +84,78 @@ const callModeAction = { process.exit(0) }, identity: async (id) => { - const question = !id - ? await getQuestionByMode(mode) - : await getQuestionById(id) - await check('identity', question) + let question + if (!id) { + // 如果未指定id说明是要检测模式创建的题目 + question = await getQuestionByMode(mode) + await check('identity', question) + } else { + question = await getFilePathById(id) + const needToSelect = { + type: 'list', + name: 'need', + message: '在当前目录下存在多个题目,请选择你要检测的题目:', + choices: [] + } + /** + * 只检查一个题目 + * @param fileOrFiles + * @returns {Promise<void>} + */ + const checkOne = async (fileOrFiles) => { + const needToCheck = { + type: 'list', + name: 'check', + message: '存在多个题目文件,请选择:', + choices: [], + default: null + } + let filePath + switch (typeof fileOrFiles) { + case 'undefined': + logger.warn( + `虽然在题目目录中,但当前目录下不存在[${id}]的题目文件!` + ) + process.exit(0) + break + case 'string': + filePath = fileOrFiles + break + case 'object': + needToCheck.choices = fileOrFiles.map((o) => { + return { name: o, value: o } + }) + needToCheck.default = fileOrFiles?.find((o) => + o.endsWith(getQuestionFileExtension(DefaultLang)) + ) + filePath = await select(needToCheck) + break + } + return await checkQuestionByPath(filePath) + } + + let files + switch (typeof question) { + case 'undefined': + logger.warn(`当前目录下未找到题目id为[${id}]的题目!`) + process.exit(0) + break + case 'string': + files = getQuestionFileInDir(path.resolve(baseDir, question)) + break + case 'object': + needToSelect.choices = question.map((o) => { + return { name: o, value: o } + }) + files = getQuestionFileInDir( + path.resolve(baseDir, await select(needToSelect)) + ) + break + } + await checkOne(files) + } process.exit(0) } } - // 执行指令分发 callModeAction[mode](args) diff --git a/common/utils/cli-utils/commonMode.js b/common/utils/cli-utils/commonMode.js new file mode 100644 index 0000000..8400cd1 --- /dev/null +++ b/common/utils/cli-utils/commonMode.js @@ -0,0 +1,48 @@ +import path from 'node:path' +import { easyUpdateView } from '#common/view/update.view.js' +import { getQuestionLanguage } from '#common/utils/question-handler/questionLanguage.js' +import { easyLanguageView } from '#common/view/language.view.js' +import { logger } from '#common/utils/logger/logger.js' + +/** + * 执行逻辑: + * 目录检测 - 设置基础目录 + * 模式检测 - 检测是不是easy mode + * [参数检测 - 执行对应参数] + */ +/** + * 通用参数的执行逻辑 + * @param cmdOpts{{directory:string,language:string|boolean,easy:boolean,update:boolean,[key:string]:*}} + * @param easyCallback{(baseDir:string)=>Promise<any>} + * @returns {Promise<string>} + */ +export async function commonMode(cmdOpts, easyCallback) { + // 根据dir 参数来设置基本目录 + const baseDir = cmdOpts.directory + ? path.join(process.cwd(), cmdOpts.directory) + : process.cwd() + /** + * 语言设置 + * -带参设置语言 + * -无参获取语言 + */ + if (cmdOpts.language) { + if (cmdOpts.language !== true) { + await easyLanguageView(cmdOpts.language) + } else { + const lang = await getQuestionLanguage() + logger.info(`当前CLI语言环境为:${lang}`) + } + process.exit(0) + } + if (cmdOpts?.easy) { + await easyCallback(baseDir) + process.exit(0) + } + // 检测更新 + if (cmdOpts.update) { + await easyUpdateView() + process.exit(0) + } + return baseDir +} diff --git a/common/utils/create-check/createUtil.js b/common/utils/cli-utils/create.js similarity index 65% rename from common/utils/create-check/createUtil.js rename to common/utils/cli-utils/create.js index bfb570e..183301a 100644 --- a/common/utils/create-check/createUtil.js +++ b/common/utils/cli-utils/create.js @@ -1,14 +1,19 @@ import path from 'node:path' -import { createQuestion } from '#common/utils/question-handler/createQuestion.js' +import { logger } from '#common/utils/logger/logger.js' +import { setQuestion } from '#common/utils/store/controller/question.js' import { getQuestionFileName } from '#common/utils/question-handler/getQuestionFileName.js' +import { createQuestion } from '#common/utils/question-handler/createQuestion.js' import { createQuestionCopy } from '#common/utils/question-handler/createQuestionCopy.js' -import { setQuestion } from '#common/utils/store/controller/question.js' -import { getQuestionChineseName } from '#common/utils/question-handler/getQuestionChineseName.js' -import { getQuestionById } from '#common/utils/question-getter/getQuestionById.js' -import { getQuestionIdBySlug } from '#common/utils/question-handler/getQuestionIdBySlug.js' import { getLineNumberByContent } from '#common/utils/file/getLineNumberByContent.js' -import { logger } from '#common/utils/logger/logger.js' +import { getQuestionChineseName } from '#common/utils/question-handler/getQuestionChineseName.js' +/** + * 创建函数 + * @param mode + * @param question + * @param baseDir + * @returns {Promise<unknown>} + */ export function create(mode, question, baseDir) { logger.info(`MODE: ${mode}`) return new Promise((resolve) => { @@ -24,17 +29,3 @@ export function create(mode, question, baseDir) { }) }) } - -export async function createQuestionByTitleSlug( - titleSlug, - baseDir = process.cwd() -) { - const { question } = await getQuestionIdBySlug(titleSlug) - - await createQuestionById(question.questionId, baseDir) -} -export async function createQuestionById(id, baseDir = process.cwd()) { - const question = await getQuestionById(id) - if (!question?.id) logger.warn(`指定编号: [ ${id} ] 题目不存在.`) - await create('identity', question, baseDir) -} diff --git a/common/utils/cli-utils/createQuestion.js b/common/utils/cli-utils/createQuestion.js new file mode 100644 index 0000000..4fde403 --- /dev/null +++ b/common/utils/cli-utils/createQuestion.js @@ -0,0 +1,30 @@ +import { getQuestionById } from '#common/utils/question-getter/getQuestionById.js' +import { getQuestionIdBySlug } from '#common/utils/question-handler/getQuestionIdBySlug.js' +import { logger } from '#common/utils/logger/logger.js' +import { create } from '#common/utils/cli-utils/create.js' + +/** + * 通过指定的titleSlug创建题目 + * @param titleSlug + * @param baseDir + * @returns {Promise<void>} + */ +export async function createQuestionByTitleSlug( + titleSlug, + baseDir = process.cwd() +) { + const { question } = await getQuestionIdBySlug(titleSlug) + await createQuestionById(question.questionId, baseDir) +} + +/** + * 通过id创建题目 + * @param id + * @param baseDir + * @returns {Promise<void>} + */ +export async function createQuestionById(id, baseDir = process.cwd()) { + const question = await getQuestionById(id) + if (!question?.id) logger.warn(`指定编号: [ ${id} ] 题目不存在.`) + await create('identity', question, baseDir) +} diff --git a/common/utils/create-check/get-args.js b/common/utils/cli-utils/getArgs.js similarity index 100% rename from common/utils/create-check/get-args.js rename to common/utils/cli-utils/getArgs.js diff --git a/common/utils/create-check/refer-mode.js b/common/utils/cli-utils/referMode.js similarity index 100% rename from common/utils/create-check/refer-mode.js rename to common/utils/cli-utils/referMode.js diff --git a/common/utils/etc/willUse.js b/common/utils/etc/willUse.js new file mode 100644 index 0000000..21d4ddb --- /dev/null +++ b/common/utils/etc/willUse.js @@ -0,0 +1,8 @@ +/** + * 无用之用:标记可能会用的变量 + * @returns {*} + * @param args + */ +export function willUse(...args) { + return args +} diff --git a/common/utils/file/getQuestionInDir.js b/common/utils/file/getQuestionInDir.js new file mode 100644 index 0000000..75d46da --- /dev/null +++ b/common/utils/file/getQuestionInDir.js @@ -0,0 +1,15 @@ +import fs from 'node:fs' +import path from 'node:path' + +/** + * 在目录中查找题目文件 + * @param dir + * @returns {string|string[]} + */ +export function getQuestionFileInDir(dir) { + const list = fs.readdirSync(dir) + const questionLikes = list + .filter((name) => name.startsWith('question')) + .map((file) => path.resolve(dir, file)) + return questionLikes?.length === 1 ? questionLikes[0] : questionLikes +} diff --git a/common/utils/logger/logger.js b/common/utils/logger/logger.js index 40f442d..71494f6 100644 --- a/common/utils/logger/logger.js +++ b/common/utils/logger/logger.js @@ -1,24 +1,39 @@ import chalk from 'chalk' import { getStore } from '#common/utils/store/controller/store.js' -class Logger { - constructor(env) { - console.log( - chalk.bgGray(`[logger init] The current env is ${env ?? 'not plugin'}.`) - ) +class LOGGER { + constructor(_env) { + // console.log( + // chalk.bgGray(`[logger init] The current env is ${env ?? 'not plugin'}.`) + // ) } - info(message) { - console.log(chalk.blue(message)) + /** + * 普通消息 + * @param message{*} + * @param args{*[]} + */ + info(message, ...args) { + console.log(chalk.blue(message, ...args)) } - warn(message) { - console.log(chalk.yellow(message)) + /** + * 警告 + * @param message{*} + * @param args{*[]} + */ + warn(message, ...args) { + console.log(chalk.yellow(message, ...args)) } - error(message) { - console.log(chalk.red(message)) + /** + * 错误信息 + * @param message{*} + * @param args{*[]} + */ + error(message, ...args) { + console.log(chalk.red(message, ...args)) } } const { env = null } = (await getStore('config')) ?? {} -export const logger = new Logger(env) +export const logger = new LOGGER(env) diff --git a/common/utils/question-getter/getStudyPlanList.js b/common/utils/question-getter/getStudyPlanList.js index e1cc5fd..ad6efba 100644 --- a/common/utils/question-getter/getStudyPlanList.js +++ b/common/utils/question-getter/getStudyPlanList.js @@ -1,10 +1,12 @@ import { graphql } from '#common/utils/http/graphql.js' import { getStudyPlanListJson } from '#resources/headers/studyPlanListJson.js' -export async function getStudyPlanList() { - const res = await graphql(getStudyPlanListJson()) +export async function getStudyPlanList(type) { + const res = await graphql(getStudyPlanListJson(type)) const { - data: { studyPlansV2AdQuestionPage } + data: { + studyPlansV2ByCatalog: { studyPlans } + } } = res - return studyPlansV2AdQuestionPage + return studyPlans } diff --git a/common/utils/question-handler/checkQuestion.js b/common/utils/question-handler/checkQuestionByPath.js similarity index 91% rename from common/utils/question-handler/checkQuestion.js rename to common/utils/question-handler/checkQuestionByPath.js index 43f5393..cdbe42f 100644 --- a/common/utils/question-handler/checkQuestion.js +++ b/common/utils/question-handler/checkQuestionByPath.js @@ -18,7 +18,7 @@ export function executeScript(filePath, context) { * 执行问题检测进程 * @param path */ -export async function checkQuestion(path) { +export async function checkQuestionByPath(path) { return await executeScript( path, vm.createContext({ diff --git a/common/utils/question-handler/code.js b/common/utils/question-handler/code.js index 04469a8..125d6fb 100644 --- a/common/utils/question-handler/code.js +++ b/common/utils/question-handler/code.js @@ -14,7 +14,7 @@ import { DefaultLang } from '#common/constants/question.const.js' */ export async function getCodeBySlug(slug, lang) { const list = await getQuestionCodeList(slug) - return list.find((o) => o.langSlug === lang)?.code + return list?.find((o) => o.langSlug === lang)?.code } /** * 获取支持的代码语言 diff --git a/common/utils/question-handler/getHot100QuestionListCode.js b/common/utils/question-handler/getHot100QuestionListCode.js deleted file mode 100644 index d3a6d8b..0000000 --- a/common/utils/question-handler/getHot100QuestionListCode.js +++ /dev/null @@ -1,26 +0,0 @@ -import path from 'node:path' -import { createQuestionByTitleSlug } from '../create-check/createUtil.js' -import { getPlanQuestionList } from '#common/utils/question-getter/getPlanQuestionList.js' - -// 获取题目列表 -export async function getTitleSlugList() { - const res = await getPlanQuestionList() - const { planSubGroups } = res - return planSubGroups.reduce((acc, cur) => { - acc.push(...cur.questions.map((res) => res.titleSlug)) - return acc - }, []) -} -// 获取创建promise列表 -async function getPromiseList() { - const titleSlugList = await getTitleSlugList() - const dir = path.join(process.cwd(), 'hot100') - return titleSlugList.map((titleSlug) => - createQuestionByTitleSlug(titleSlug, dir) - ) -} - -export async function getHot100QuestionListCode() { - const promiseList = await getPromiseList() - return await Promise.all(promiseList) -} diff --git a/common/utils/question-handler/getQuestionListCodeBySlug.js b/common/utils/question-handler/getQuestionListCodeBySlug.js new file mode 100644 index 0000000..b2193b7 --- /dev/null +++ b/common/utils/question-handler/getQuestionListCodeBySlug.js @@ -0,0 +1,24 @@ +import { createQuestionByTitleSlug } from '#common/utils/cli-utils/createQuestion.js' +import { getPlanQuestionList } from '#common/utils/question-getter/getPlanQuestionList.js' + +// 获取创建promise列表 +async function createCreatePromiseList(slugList, baseDir = process.cwd()) { + return slugList.map((titleSlug) => { + return createQuestionByTitleSlug(titleSlug, baseDir) + }) +} + +/** + * 創建題目列表通過plan slug + * @param slug + * @param baseDir + */ +export async function getQuestionListCodeBySlug(slug, baseDir) { + const { planSubGroups } = await getPlanQuestionList(slug) + const questionTitleList = planSubGroups.reduce((acc, cur) => { + acc.push(...cur.questions.map((res) => res.titleSlug)) + return acc + }, []) + const promiseList = await createCreatePromiseList(questionTitleList, baseDir) + return await Promise.allSettled(promiseList) +} diff --git a/common/view/check.view.js b/common/view/check.view.js index 4894a37..f16d4d7 100644 --- a/common/view/check.view.js +++ b/common/view/check.view.js @@ -3,7 +3,7 @@ import inquirer from 'inquirer' import { getQuestionFileName } from '#common/utils/question-handler/getQuestionFileName.js' import { getQuestionById } from '#common/utils/question-getter/getQuestionById.js' import { getQuestionByMode } from '#common/utils/store/controller/question.js' -import { checkQuestion } from '#common/utils/question-handler/checkQuestion.js' +import { checkQuestionByPath } from '#common/utils/question-handler/checkQuestionByPath.js' import { getCountBySameName } from '#common/utils/file/getCountBySameName.js' import { getFileListBySameName } from '#common/utils/file/getFileListBySameName.js' import { logger } from '#common/utils/logger/logger.js' @@ -66,7 +66,7 @@ export async function easyCheckView() { ] const { newDir } = await inquirer.prompt(newDirQuestion, null) if (!newDir) { - logger.info('[LK-LOG]用户终止操作~') + logger.info('[LK-logger]用户终止操作~') process.exit(0) } questionDir = path.join( @@ -96,7 +96,7 @@ export async function easyCheckView() { ) } const filePath = path.join(questionDir, `question${question.lang}`) - await checkQuestion(filePath) + await checkQuestionByPath(filePath) logger.info(`题目[${questionFileName}]检查完成!\n文件地址为: ${filePath}`) process.exit(0) } diff --git a/common/view/create.view.js b/common/view/create.view.js index 332c9e7..0e9eacf 100644 --- a/common/view/create.view.js +++ b/common/view/create.view.js @@ -9,7 +9,7 @@ import { createQuestionCopy } from '#common/utils/question-handler/createQuestio import { setQuestion } from '#common/utils/store/controller/question.js' import { logger } from '#common/utils/logger/logger.js' -export async function easyCreateView() { +export async function easyCreateView(baseDir = process.cwd()) { const modeQuestion = [ { type: 'list', @@ -47,14 +47,13 @@ export async function easyCreateView() { if (!store) console.warn(`[create][${mode}]问题[${question.title}]未成功缓存`) // 创建题目 const questionFileName = getQuestionFileName(question) - const currentDir = process.cwd() - let questionDir = path.join(currentDir, questionFileName) + let questionDir = path.join(baseDir, questionFileName) // 创建路径确认 const pathRightQuestion = [ { type: 'confirm', name: 'dirRight', - message: `是否在目录[ ${currentDir} ]下创建题目[ ${questionFileName} ]?` + message: `是否在目录[ ${baseDir} ]下创建题目[ ${questionFileName} ]?` } ] const { dirRight } = await inquirer.prompt(pathRightQuestion, null) @@ -63,18 +62,15 @@ export async function easyCreateView() { { type: 'input', name: 'newDir', - message: `请选择新目录(基础地址为${process.cwd()})[按回车[Enter]终止操作]:` + message: `请选择新目录(基础地址为${baseDir})[按回车[Enter]终止操作]:` } ] const { newDir } = await inquirer.prompt(newDirQuestion, null) if (!newDir) { - logger.info('[LC-LOG]用户终止操作~') + logger.info('[LC-logger]用户终止操作~') process.exit(0) } - questionDir = path.join( - path.join(process.cwd(), newDir), - `${questionFileName}` - ) + questionDir = path.join(path.join(baseDir, newDir), `${questionFileName}`) } let filePath = await createQuestion(question, questionDir) if (!filePath) filePath = await createQuestionCopy(question, questionDir) diff --git a/common/view/finder.view.js b/common/view/finder.view.js index 4452e11..66d6818 100644 --- a/common/view/finder.view.js +++ b/common/view/finder.view.js @@ -1,31 +1,49 @@ -import select from '@inquirer/select' +import path from 'node:path' +import select, { Separator } from '@inquirer/select' import input from '@inquirer/input' - -// import { getHot100QuestionListCode } from '#common/utils/question-handler/getHot100QuestionListCode.js' import { createQuestionById, createQuestionByTitleSlug -} from '#common/utils/create-check/createUtil.js' +} from '#common/utils/cli-utils/createQuestion.js' import { getQuestionByKeyword } from '#common/utils/question-getter/getQuestionByKeyword.js' import { getStudyPlanList } from '#common/utils/question-getter/getStudyPlanList.js' import { getPlanQuestionList } from '#common/utils/question-getter/getPlanQuestionList.js' import { logger } from '#common/utils/logger/logger.js' +import { getQuestionListCodeBySlug } from '#common/utils/question-handler/getQuestionListCodeBySlug.js' + +function handleQuestionList(list) { + return list.map((item) => ({ + name: `${item.name}${item.premiumOnly ? '(VIP)' : ''}`, + value: item.slug + })) +} async function studyMode(baseDir = process.cwd()) { - const questionList = await getStudyPlanList() + const sprintInterviewCompanyList = await getStudyPlanList( + 'sprint-interview-company' + ) + const crackingCodingInterviewList = await getStudyPlanList( + 'cracking-coding-interview' + ) + const deepDiveTopicsList = await getStudyPlanList('deep-dive-topics') + const questionList = [ + ...handleQuestionList(sprintInterviewCompanyList), + new Separator(), + ...handleQuestionList(crackingCodingInterviewList), + new Separator(), + ...handleQuestionList(deepDiveTopicsList) + ] const planListMode = { message: '请选择学习计划', - choices: questionList.map((item) => ({ - name: `${item.name}${item.premiumOnly ? '(VIP)' : ''}`, - value: item.slug - })) + choices: questionList, + pageSize: 30 } const planSlug = await select(planListMode) const createMode = await select({ message: '拉题模式', choices: [ { name: '单个选择', value: 'single' }, - { name: '全部拉取(暂不支持)', value: 'all' } + { name: '全部拉取(不穩定)', value: 'all' } ] }) if (createMode === 'single') { @@ -46,14 +64,19 @@ async function studyMode(baseDir = process.cwd()) { choices: planList.map((res) => ({ name: res.cnTitle, value: res.enTitle - })) + })), + pageSize: 30 } const singleChoice = await select(singleMode) await createQuestionByTitleSlug(singleChoice, baseDir) } - if (createMode === 'all') logger.warn('暂不支持') - // await getHot100QuestionListCode() + if (createMode === 'all') { + await getQuestionListCodeBySlug( + planSlug, + path.resolve(baseDir, planSlug.toString()) + ) + } } async function keywordMode(baseDir = process.cwd()) { diff --git a/common/view/update.view.js b/common/view/update.view.js index c60431c..666dd90 100644 --- a/common/view/update.view.js +++ b/common/view/update.view.js @@ -18,9 +18,9 @@ export async function easyUpdateView() { const { choseEnv } = await inquirer.prompt(envQuestion, null) // 2. 检测版本更新 const { - localVersion, - npmVersion, - githubVersion, + localVersion = '未检出', + npmVersion = '未检出', + githubVersion = '未检出', isCliUpdate, isGithubUpdate } = await checkUpdate() diff --git a/package.json b/package.json index cc396e1..f636882 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "leetcode-practice", "type": "module", - "version": "1.0.7", + "version": "1.0.8", "packageManager": "yarn@1.22.0", "description": "A powerful practice platform for leetcode.Using any way you want to create questions.", "author": { @@ -43,7 +43,8 @@ "prepare": "husky install", "create-color-font": "node scripts/create-color-font.js", "release": "release-it", - "try-release": "release-it --dry-run" + "try-release": "release-it --dry-run", + "rebase": "HUSKY=0 git rebase " }, "dependencies": { "@inquirer/input": "^2.0.1", @@ -57,22 +58,22 @@ }, "devDependencies": { "@antfu/eslint-config": "^2.8.0", - "@commitlint/cli": "^17.0.3", - "@commitlint/config-conventional": "^17.0.3", + "@commitlint/cli": "^19.1.0", + "@commitlint/config-conventional": "^19.1.0", "@release-it/bumper": "^6.0.1", "@release-it/conventional-changelog": "^8.0.1", "@types/node": "^20.11.5", "@vitest/coverage-v8": "^1.2.2", "commitizen": "^4.2.5", "cz-conventional-changelog": "^3.3.0", - "eslint-plugin-prettier": "^5.1.3", "esbuild": "^0.20.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", "gradient-string": "^2.0.2", - "husky": "^8.0.1", + "husky": "^9.0.11", "lint-staged": "^15.2.2", "prettier": "^3.2.5", "release-it": "^17.1.1", diff --git a/resources/headers/studyPlanListJson.js b/resources/headers/studyPlanListJson.js index ef910a7..b33bd44 100644 --- a/resources/headers/studyPlanListJson.js +++ b/resources/headers/studyPlanListJson.js @@ -1,7 +1,10 @@ -export function getStudyPlanListJson() { +// 'sprint-interview-company' // 名企面试 · 突击备战 +// 'cracking-coding-interview' // 面试准备 · 全面通关 +// 'deep-dive-topics' // 专项计划 · 深入学习 +export function getStudyPlanListJson(type) { return { headers: { 'content-type': 'application/json' }, - body: '{"query":"\\n query GetProblemSetStudyPlanAds {\\n studyPlansV2AdQuestionPage {\\n cover\\n highlight\\n name\\n onGoing\\n premiumOnly\\n questionNum\\n slug\\n }\\n}\\n ","variables":{},"operationName":"GetProblemSetStudyPlanAds"}', + body: `{\"query\":\"\\n query GetStudyPlanByCatalog($catalogSlug: String!, $offset: Int!, $limit: Int!) {\\n studyPlansV2ByCatalog(catalogSlug: $catalogSlug, offset: $offset, limit: $limit) {\\n hasMore\\n total\\n studyPlans {\\n slug\\n questionNum\\n premiumOnly\\n onGoing\\n name\\n highlight\\n cover\\n }\\n }\\n}\\n \",\"variables\":{\"offset\":0,\"catalogSlug\":\"${type}\",\"limit\":12},\"operationName\":\"GetStudyPlanByCatalog\"}`, method: 'POST' } }