From ef0ba574bce54ce91b96d4f9658cf59ae7a7549a Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 11 Jan 2025 00:38:11 +0800 Subject: [PATCH 1/4] feat: replace cac with commander --- pnpm-lock.yaml | 18 ++++++++++----- tools/create-vuepress/package.json | 2 +- tools/create-vuepress/src/action.ts | 18 +++++++-------- tools/create-vuepress/src/index.ts | 29 ++++++++++++------------ tools/vp-update/package.json | 2 +- tools/vp-update/src/index.ts | 34 ++++++++++++++--------------- 6 files changed, 53 insertions(+), 50 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cf67057de..c89bf1822b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1200,9 +1200,9 @@ importers: '@inquirer/prompts': specifier: ^7.2.1 version: 7.2.1(@types/node@22.10.5) - cac: - specifier: ^6.7.14 - version: 6.7.14 + commander: + specifier: ^13.0.0 + version: 13.0.0 execa: specifier: ^9.5.2 version: 9.5.2 @@ -1268,9 +1268,9 @@ importers: tools/vp-update: dependencies: - cac: - specifier: ^6.7.14 - version: 6.7.14 + commander: + specifier: ^13.0.0 + version: 13.0.0 semver: specifier: ^7.6.3 version: 7.6.3 @@ -3927,6 +3927,10 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@13.0.0: + resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -11701,6 +11705,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@13.0.0: {} + commander@2.20.3: {} commander@7.2.0: {} diff --git a/tools/create-vuepress/package.json b/tools/create-vuepress/package.json index 0ca90957ef..23e17f449c 100644 --- a/tools/create-vuepress/package.json +++ b/tools/create-vuepress/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@inquirer/prompts": "^7.2.1", - "cac": "^6.7.14", + "commander": "^13.0.0", "execa": "^9.5.2" }, "peerDependencies": { diff --git a/tools/create-vuepress/src/action.ts b/tools/create-vuepress/src/action.ts index c69b35b4e8..af5376f193 100644 --- a/tools/create-vuepress/src/action.ts +++ b/tools/create-vuepress/src/action.ts @@ -3,6 +3,7 @@ import { existsSync, readdirSync } from 'node:fs' import { resolve } from 'node:path' import { confirm, select } from '@inquirer/prompts' +import type { Command } from 'commander' import { execaCommand, execaCommandSync } from 'execa' import { KNOWN_THEME_COMMANDS } from './config/index.js' import { createPackageJson, generateTemplate } from './flow/index.js' @@ -26,10 +27,11 @@ interface CreateOptions { const bundlers: Bundler[] = ['vite', 'webpack'] const presets: Preset[] = ['blog', 'docs'] -export const mainAction = async ( +export async function mainAction( + this: Command, targetDir: string, { bundler, preset, theme = '@vuepress/theme-default' }: CreateOptions, -): Promise => { +): Promise { // get language const { lang, locale } = await getLanguage() @@ -52,28 +54,24 @@ export const mainAction = async ( // check bundler if (bundler && !['vite', 'webpack'].includes(bundler)) { - console.error(locale.error.bundler) - return + this.error(locale.error.bundler) } // check presets if (preset && !['docs', 'blog'].includes(preset)) { - console.error(locale.error.preset) - return + this.error(locale.error.preset) } // check if the user is a noob and warn him if (!targetDir || (targetDir.startsWith('[') && targetDir.endsWith(']'))) { - console.error(locale.error.dirMissing(packageManager)) - return + this.error(locale.error.dirMissing(packageManager)) } const targetDirPath = resolve(process.cwd(), targetDir) // check if the user is trying to cover his files if (existsSync(targetDirPath) && readdirSync(targetDirPath).length) { - console.error(locale.error.dirNotEmpty(targetDir)) - return + this.error(locale.error.dirNotEmpty(targetDir)) } ensureDirExistSync(targetDirPath) diff --git a/tools/create-vuepress/src/index.ts b/tools/create-vuepress/src/index.ts index a554fe1716..1d362b472f 100644 --- a/tools/create-vuepress/src/index.ts +++ b/tools/create-vuepress/src/index.ts @@ -1,25 +1,26 @@ #!/usr/bin/env node -import { cac } from 'cac' +import { createCommand } from 'commander' import { mainAction } from './action.js' import { version } from './utils/index.js' -const cli = cac('create-vuepress') +const program = createCommand('create-vuepress') -cli - .command('[dir]', 'Generate a new vuepress project in [dir]') - .option('-t, --theme ', 'Theme to use') - .option('-p, --preset ', 'Preset to use, can be docs or blog') - .usage( +program + .argument('', 'Dir to create the template in') + .option('-t, --theme [theme]', 'Theme to use') + .option('-p, --preset [preset]', 'Preset to use, docs or blog only') + .description( `\ -[dir] +Generate a new vuepress template -Generate vuepress template in dir.`, +· pnpm create vuepress +· npm init vuepress@latest +· yarn create vuepress +`, ) - .example('vuepress-project') .action(mainAction) -cli.help() +program.version(version) +program.showHelpAfterError('add --help for additional information') -cli.version(version) - -cli.parse() +await program.parseAsync() diff --git a/tools/vp-update/package.json b/tools/vp-update/package.json index 9dfb1e5943..d5092cd72f 100644 --- a/tools/vp-update/package.json +++ b/tools/vp-update/package.json @@ -38,7 +38,7 @@ "clean": "rimraf --glob ./lib ./*.tsbuildinfo" }, "dependencies": { - "cac": "^6.7.14", + "commander": "^13.0.0", "semver": "^7.6.3" }, "devDependencies": { diff --git a/tools/vp-update/src/index.ts b/tools/vp-update/src/index.ts index 729cdd8cbd..6bd2b4ace0 100644 --- a/tools/vp-update/src/index.ts +++ b/tools/vp-update/src/index.ts @@ -3,7 +3,7 @@ import { spawnSync } from 'node:child_process' import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -import { cac } from 'cac' +import { createCommand } from 'commander' import { VERSION } from './config/index.js' import { checkTaobaoRegistry, @@ -11,21 +11,26 @@ import { updatePackages, } from './utils/index.js' -const cli = cac('vp-update') +const program = createCommand('vp-update') -cli - .command('[dir]', 'Update VuePress project') +program + .summary('Update VuePress project') + .argument('[dir]', 'Dir of VuePress project', '') .usage( - 'pnpm dlx vp-update [dir] / npx vp-update [dir] / bunx vp-update [dir]', + ` +pnpm dlx vp-update [dir] / npx vp-update [dir] / bunx vp-update [dir]\ +`, ) - .example('docs') - .action(async (targetDir: string = ''): Promise => { + .action(async (targetDir: string = ''): Promise => { console.log('Bumping deps...') + const dir = resolve(process.cwd(), targetDir) const packageJSON = resolve(dir, 'package.json') if (!existsSync(packageJSON)) - return new Error(`No package.json found in ${targetDir || 'current dir'}`) + return program.error( + `No package.json found in ${targetDir || 'current dir'}`, + ) const packageManager = getPackageManager() @@ -81,14 +86,7 @@ cli ) }) -cli.help(() => [ - { - title: - 'pnpm dlx vp-update [dir] / npx vp-update [dir] / bunx vp-update [dir]', - body: 'Update VuePress project in [dir]', - }, -]) - -cli.version(VERSION) +program.version(VERSION) +program.showHelpAfterError('add --help for additional information') -cli.parse() +await program.parseAsync() From bb1c4b5d168d5b68af448c51cbfe1bc9e5c420b8 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 11 Jan 2025 00:40:51 +0800 Subject: [PATCH 2/4] chore: tweaks --- tools/create-vuepress/rollup.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/create-vuepress/rollup.config.ts b/tools/create-vuepress/rollup.config.ts index 201287a998..53576f7a3b 100644 --- a/tools/create-vuepress/rollup.config.ts +++ b/tools/create-vuepress/rollup.config.ts @@ -1,6 +1,6 @@ import { rollupBundle } from '../../scripts/rollup.js' export default rollupBundle('index', { - external: [/^node:/, '@inquirer/prompts', 'cac', 'execa'], + external: [/^node:/, '@inquirer/prompts', 'commander', 'execa'], dts: false, }) From ac6c47d5cb5225b09c0b1e293533be0a88f80bc6 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 11 Jan 2025 01:05:10 +0800 Subject: [PATCH 3/4] feat: update redirect plugin --- docs/plugins/tools/redirect.md | 23 +- docs/zh/plugins/tools/redirect.md | 25 ++- plugins/tools/plugin-redirect/package.json | 2 +- .../tools/plugin-redirect/src/cli/index.ts | 205 +++++++++--------- pnpm-lock.yaml | 6 +- 5 files changed, 137 insertions(+), 124 deletions(-) diff --git a/docs/plugins/tools/redirect.md b/docs/plugins/tools/redirect.md index a682aebe2d..79008eaf00 100644 --- a/docs/plugins/tools/redirect.md +++ b/docs/plugins/tools/redirect.md @@ -127,19 +127,24 @@ Sometimes you may change `base` or use new domain for your site, so you may want To solve this, the plugin provide `vp-redirect` cli. -```shell -Usage: - $ vp-redirect generate [sourceDir] +```plain +Usage: vp-redirect [options] [output] + +Generate redirect site for current VuePress project + +Arguments: + source Source directory of VuePress project + output Output folder (default: .vuepress/redirect relative to source folder) Options: - --hostname Hostname to redirect to (E.g.: https://new.example.com/) (default: /) - -c, --config Set path to config file - -o, --output Set the output directory (default: .vuepress/redirect) - --cache Set the directory of the cache files - -t, --temp Set the directory of the temporary files + --hostname Hostname to redirect to (E.g.: https://new.example.com/) (default: "/") + -c, --config [config] Set path to config file + --cache [cache] Set the directory of the cache files + --temp [temp] Set the directory of the temporary files --clean-cache Clean the cache files before generation --clean-temp Clean the temporary files before generation - -h, --help Display this message + -V, --version output the version number + -h, --help display help for command ``` You need to pass in VuePress project source dir and also set the `hostname` option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory. diff --git a/docs/zh/plugins/tools/redirect.md b/docs/zh/plugins/tools/redirect.md index edad560222..e16a30f9d4 100644 --- a/docs/zh/plugins/tools/redirect.md +++ b/docs/zh/plugins/tools/redirect.md @@ -125,19 +125,26 @@ redirect({ 为了解决这个问题,插件提供了 `vp-redirect` 脚手架。 -```shell -使用: - $ vp-redirect generate [源文件夹] +```plain +使用: vp-redirect [选项] <源文件夹> [输出文件夹] -Options: +为当前 VuePress 项目生成重定向站点 + +Generate redirect site for current VuePress project + +参数: + 源文件夹 VuePress 项目的源文件夹 + 输出文件夹 输出文件夹 (默认: .vuepress/redirect 相对于源文件夹) + +选项: --hostname 重定向到的域名 (例如: https://new.example.com/) (默认: /) - -c, --config 设置配置文件路径 - -o, --output 设置输出目录 (默认: .vuepress/redirect) - --cache 设置缓存文件的目录 - -t, --temp 设置临时文件的目录 + -c, --config [config] 设置配置文件路径 + --cache [cache] 设置缓存文件的目录 + --temp [temp] 设置临时文件的目录 --clean-cache 生成前清理缓存文件 --clean-temp 生成前清理临时文件 - -h, --help 显示此消息 + -V, --version 输出版本号 + -h, --help 显示帮助信息 ``` 你需要传入 VuePress 项目源目录并设置 `hostname` 选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。 diff --git a/plugins/tools/plugin-redirect/package.json b/plugins/tools/plugin-redirect/package.json index d4b7c97800..f5c25d2b12 100644 --- a/plugins/tools/plugin-redirect/package.json +++ b/plugins/tools/plugin-redirect/package.json @@ -46,7 +46,7 @@ "dependencies": { "@vuepress/helper": "workspace:*", "@vueuse/core": "^12.3.0", - "cac": "^6.7.14", + "commander": "^13.0.0", "vue": "^3.5.13" }, "peerDependencies": { diff --git a/plugins/tools/plugin-redirect/src/cli/index.ts b/plugins/tools/plugin-redirect/src/cli/index.ts index 6fdb122e0a..43efceec17 100644 --- a/plugins/tools/plugin-redirect/src/cli/index.ts +++ b/plugins/tools/plugin-redirect/src/cli/index.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import { removeEndingSlash, removeLeadingSlash } from '@vuepress/helper' -import { cac } from 'cac' +import { createCommand } from 'commander' import { loadUserConfig, resolveAppConfig, @@ -16,117 +16,118 @@ import { getRedirectHTML } from '../node/generate/getRedirectHTML.js' interface RedirectCommandOptions { hostname: string - output?: string config?: string - cache: string + cache?: string temp?: string cleanCache?: boolean cleanTemp?: boolean } -const cli = cac('vp-redirect') +const program = createCommand('vp-redirect') -cli - .command( - 'generate [source-dir]', - 'Generate redirect site using VuePress project under source folder', - ) +program + .summary('Generate redirect site') + .description('Generate redirect site for current VuePress project') .option( '--hostname ', 'Hostname to redirect to (E.g.: https://new.example.com/)', - { default: '/' }, - ) - .option('-c, --config ', 'Set path to config file') - .option( - '-o, --output ', - 'Set the output directory (default: .vuepress/redirect)', + '/', ) - .option('--cache ', 'Set the directory of the cache files') - .option('-t, --temp ', 'Set the directory of the temporary files') + .option('-c, --config [config]', 'Set path to config file') + .option('--cache [cache]', 'Set the directory of the cache files') + .option('--temp [temp]', 'Set the directory of the temporary files') .option('--clean-cache', 'Clean the cache files before generation') .option('--clean-temp', 'Clean the temporary files before generation') - .action(async (sourceDir: string, commandOptions: RedirectCommandOptions) => { - if (!sourceDir) { - cli.outputHelp() - return - } - - // ensure NODE_ENV is set - process.env.NODE_ENV ??= 'production' - - // resolve app config from cli options - const cliAppConfig = resolveCliAppConfig(sourceDir, {}) - - // resolve user config file - const userConfigPath = commandOptions.config - ? resolveUserConfigPath(commandOptions.config) - : resolveUserConfigConventionalPath(cliAppConfig.source) - - const { userConfig } = await loadUserConfig(userConfigPath) - - // resolve the final app config to use - const appConfig = resolveAppConfig({ - defaultAppConfig: {}, - cliAppConfig, - userConfig, - }) - - if (appConfig === null) return - - // create vuepress app - const app = createBuildApp(appConfig) - - // use user-config plugin - app.use(transformUserConfigToPlugin(userConfig, cliAppConfig.source)) - - // clean temp and cache - if (commandOptions.cleanTemp === true) { - logger.info('Cleaning temp...') - await fs.remove(app.dir.temp()) - } - if (commandOptions.cleanCache === true) { - logger.info('Cleaning cache...') - await fs.remove(app.dir.cache()) - } - - const outputFolder = commandOptions.output - ? path.join(process.cwd(), commandOptions.output) - : path.join(app.dir.source(), '.vuepress', 'redirect') - - // empty output directory - await fs.emptyDir(outputFolder) - - // initialize vuepress app to get pages - logger.info('Initializing VuePress and preparing data...') - - // initialize vuepress app to get pages - await app.init() - - logger.info('Generating redirect pages...') - - // redirect all pages - await Promise.all( - app.pages.map((page) => { - const redirectUrl = `${removeEndingSlash(commandOptions.hostname)}${ - app.siteData.base - }${removeLeadingSlash(page.path)}` - const destLocation = path.join( - outputFolder, - removeLeadingSlash(page.path.replace(/\/$/, '/index.html')), - ) - - return fs - .ensureDir(path.dirname(destLocation)) - .then(() => fs.writeFile(destLocation, getRedirectHTML(redirectUrl))) - }), - ) - }) - -cli.command('').action(() => { - cli.outputHelp() -}) - -cli.help() -cli.version(pkg.version) - -cli.parse() + .argument('', 'Source directory of VuePress project') + .argument( + '[output]', + 'Output folder (default: .vuepress/redirect relative to source folder)', + ) + .action( + async ( + sourceDir: string, + output: string | undefined, + commandOptions: RedirectCommandOptions, + ) => { + // ensure NODE_ENV is set + process.env.NODE_ENV ??= 'production' + + if (!fs.existsSync(sourceDir)) { + program.error(`Source directory ${sourceDir} does not exist!`) + } + + // resolve app config from cli options + const cliAppConfig = resolveCliAppConfig(sourceDir, {}) + + // resolve user config file + const userConfigPath = commandOptions.config + ? resolveUserConfigPath(commandOptions.config) + : resolveUserConfigConventionalPath(cliAppConfig.source) + + const { userConfig } = await loadUserConfig(userConfigPath) + + // resolve the final app config to use + const appConfig = resolveAppConfig({ + defaultAppConfig: {}, + cliAppConfig, + userConfig, + }) + + if (appConfig === null) return + + // create vuepress app + const app = createBuildApp(appConfig) + + // use user-config plugin + app.use(transformUserConfigToPlugin(userConfig, cliAppConfig.source)) + + // clean temp and cache + if (commandOptions.cleanTemp === true) { + logger.info('Cleaning temp...') + await fs.remove(app.dir.temp()) + } + if (commandOptions.cleanCache === true) { + logger.info('Cleaning cache...') + await fs.remove(app.dir.cache()) + } + + const outputFolder = output + ? path.join(process.cwd(), output) + : path.join(app.dir.source(), '.vuepress', 'redirect') + + // empty output directory + await fs.emptyDir(outputFolder) + + // initialize vuepress app to get pages + logger.info('Initializing VuePress and preparing data...') + + // initialize vuepress app to get pages + await app.init() + + logger.info('Generating redirect pages...') + + // redirect all pages + await Promise.all( + app.pages.map((page) => { + const redirectUrl = `${removeEndingSlash(commandOptions.hostname)}${ + app.siteData.base + }${removeLeadingSlash(page.path)}` + const destLocation = path.join( + outputFolder, + removeLeadingSlash(page.path.replace(/\/$/, '/index.html')), + ) + + return fs + .ensureDir(path.dirname(destLocation)) + .then(() => + fs.writeFile(destLocation, getRedirectHTML(redirectUrl)), + ) + }), + ) + }, + ) + +program.version(pkg.version) +program.showHelpAfterError('add --help for additional information') + +await program.parseAsync() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c89bf1822b..d57e0a6e0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1110,9 +1110,9 @@ importers: '@vueuse/core': specifier: ^12.3.0 version: 12.3.0(typescript@5.7.3) - cac: - specifier: ^6.7.14 - version: 6.7.14 + commander: + specifier: ^13.0.0 + version: 13.0.0 vue: specifier: ^3.5.13 version: 3.5.13(typescript@5.7.3) From 2af5303e6f79babe78e1eca242208cfdc3da58c4 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 11 Jan 2025 01:06:03 +0800 Subject: [PATCH 4/4] chore: tweaks --- plugins/tools/plugin-redirect/rollup.config.ts | 2 +- tools/vp-update/rollup.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/tools/plugin-redirect/rollup.config.ts b/plugins/tools/plugin-redirect/rollup.config.ts index be5ec76467..2401cfe9b0 100644 --- a/plugins/tools/plugin-redirect/rollup.config.ts +++ b/plugins/tools/plugin-redirect/rollup.config.ts @@ -2,7 +2,7 @@ import { rollupBundle } from '../../../scripts/rollup.js' export default [ ...rollupBundle('cli/index', { - external: ['cac'], + external: ['commander'], }), ...rollupBundle('node/index', { dtsExternal: ['@vuepress/helper/shared'], diff --git a/tools/vp-update/rollup.config.ts b/tools/vp-update/rollup.config.ts index 9b74749206..b1bea9f131 100644 --- a/tools/vp-update/rollup.config.ts +++ b/tools/vp-update/rollup.config.ts @@ -1,6 +1,6 @@ import { rollupBundle } from '../../scripts/rollup.js' export default rollupBundle('index', { - external: [/^node:/, 'cac', 'semver'], + external: [/^node:/, 'commander', 'semver'], dts: false, })