-
-
Notifications
You must be signed in to change notification settings - Fork 4
wip: support update command #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughIntroduces a new CLI command "update" to automate dependency version updates across a PNPM workspace. Adds implementation for version resolution and workspace/package.json updates, exports the handler, and wires it into the CLI. Updates package and workspace dependencies to include globby and yaml, and adds UNI_APP_DEPENDENCIES constants and types. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant CLI as CLI (src/cli/index.ts)
participant Cmd as Commands Barrel
participant Upd as handleUpdateCommand
participant VM as VersionManager
participant NPM as npm registry
participant FS as File System
User->>CLI: run "cli update [version]"
CLI->>Cmd: import handleUpdateCommand
CLI->>Upd: handleUpdateCommand(version)
activate Upd
Upd->>Upd: buildVersionMap(version)
alt version is tag (latest/alpha)
Upd->>NPM: npm view <pkg>@<tag> version
NPM-->>Upd: resolved version(s) or error
else explicit semver
Upd->>Upd: use provided version
end
Upd->>VM: new VersionManager(versionMap, UNI_APP_DEPENDENCIES)
par Update pnpm-workspace.yaml
Upd->>FS: read pnpm-workspace.yaml
alt file exists
VM->>Upd: compute catalog updates
Upd->>FS: write updated catalogs
else missing
Upd->>Upd: no-op
end
and Update package.json files
Upd->>FS: globby("package.json", "packages/**/package.json")
loop each package.json
Upd->>FS: read package.json
VM->>Upd: map deps/devDeps to target versions
Upd->>FS: write updated package.json
note over Upd,FS: On per-file error, log and continue
end
end
Upd-->>CLI: success or error propagated
CLI-->>User: report result
deactivate Upd
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (3)
src/constant.ts (1)
34-70: Make UNI_APP_DEPENDENCIES readonlyPrevents accidental mutation and improves typings.
-export const UNI_APP_DEPENDENCIES = [ +export const UNI_APP_DEPENDENCIES = [ '@dcloudio/uni-app', '@dcloudio/uni-app-plus', '@dcloudio/uni-components', '@dcloudio/uni-h5', '@dcloudio/uni-h5-vite', '@dcloudio/uni-i18n', '@dcloudio/uni-mp-360', '@dcloudio/uni-mp-alipay', '@dcloudio/uni-mp-baidu', '@dcloudio/uni-mp-harmony', '@dcloudio/uni-mp-jd', '@dcloudio/uni-mp-kuaishou', '@dcloudio/uni-mp-lark', '@dcloudio/uni-mp-qq', '@dcloudio/uni-mp-toutiao', '@dcloudio/uni-mp-vue', '@dcloudio/uni-mp-weixin', '@dcloudio/uni-mp-xhs', '@dcloudio/uni-quickapp-native', '@dcloudio/uni-quickapp-webview', '@dcloudio/uni-stacktracey', '@dcloudio/uni-stat', '@dcloudio/types', '@dcloudio/uni-automator', '@dcloudio/uni-cli-i18n', '@dcloudio/uni-cli-shared', '@dcloudio/uni-helper-json', '@dcloudio/uni-migration', '@dcloudio/uni-template-compiler', '@dcloudio/vite-plugin-uni', '@dcloudio/vue-cli-plugin-hbuilderx', '@dcloudio/vue-cli-plugin-uni', '@dcloudio/vue-cli-plugin-uni-optimize', '@dcloudio/webpack-uni-mp-loader', '@dcloudio/webpack-uni-pages-loader', -] + ] as constsrc/cli/commands/update.ts (2)
93-106: Optional: preserve YAML comments/formattingUsing parse/stringify drops comments. Consider the Document API.
- const content = await fs.readFile(workspacePath, 'utf-8') - const data: PnpmWorkspace = parse(content) + const content = await fs.readFile(workspacePath, 'utf-8') + const doc = parse(content, { prettyErrors: true }) // or parseDocument for full control + const data: PnpmWorkspace = doc as any ... - await fs.writeFile(workspacePath, stringify(data)) + await fs.writeFile(workspacePath, stringify(data))If you need full comment preservation, switch to parseDocument and doc.toString().
132-132: Nit: add trailing newline to JSON writesSome tools expect newline-terminated files.
- await fs.writeFile(filePath, JSON.stringify(data, null, 2)) + await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
package.json(1 hunks)pnpm-workspace.yaml(1 hunks)src/cli/commands/index.ts(1 hunks)src/cli/commands/update.ts(1 hunks)src/cli/index.ts(2 hunks)src/constant.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/cli/commands/update.ts (1)
src/constant.ts (1)
UNI_APP_DEPENDENCIES(34-70)
src/cli/index.ts (1)
src/cli/commands/update.ts (1)
handleUpdateCommand(141-157)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test (lts/*, windows-latest)
🔇 Additional comments (2)
src/cli/commands/index.ts (1)
7-7: Barrel export LGTMPublicly exposing the update command here is correct.
package.json (1)
53-59: Verify Node engine compatibility for new depsglobby@15 is ESM-first and may require a modern Node (often >=18). Consider adding an engines field aligned with your support matrix.
Suggested snippet:
"keywords": [ "miniapp" ], + "engines": { + "node": ">=18.18" + }, "sideEffects": false,Based on learnings
| import path from 'node:path' | ||
| import process from 'node:process' | ||
| import { globby } from 'globby' | ||
| import { exec } from 'tinyexec' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect tinyexec import
tinyexec exports x, not exec. Using exec will fail at runtime.
Based on learnings
-import { exec } from 'tinyexec'
+import { x } from 'tinyexec'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { exec } from 'tinyexec' | |
| import { x } from 'tinyexec' |
🤖 Prompt for AI Agents
In src/cli/commands/update.ts around line 5, the current import uses "exec" but
tinyexec exports "x"; change the import to pull the correct symbol from tinyexec
(import x from or import { x } depending on module style) and then update any
subsequent calls to use x(...) instead of exec(...), ensuring types/await usage
remain correct.
| private shouldUpdatePackage(name: string, currentVersion: string): boolean { | ||
| return UNI_APP_DEPENDENCIES.includes(name) && !currentVersion.startsWith('catalog') | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Skip non-registry specifiers (workspace/file/git/github/npm)
Avoid rewriting local/workspace links.
- private shouldUpdatePackage(name: string, currentVersion: string): boolean {
- return UNI_APP_DEPENDENCIES.includes(name) && !currentVersion.startsWith('catalog')
- }
+ private shouldUpdatePackage(name: string, currentVersion: string): boolean {
+ if (!UNI_APP_DEPENDENCIES.includes(name))
+ return false
+ const skipPrefixes = ['catalog:', 'workspace:', 'file:', 'link:', 'git+', 'github:', 'npm:']
+ return !skipPrefixes.some(p => currentVersion.startsWith(p))
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private shouldUpdatePackage(name: string, currentVersion: string): boolean { | |
| return UNI_APP_DEPENDENCIES.includes(name) && !currentVersion.startsWith('catalog') | |
| } | |
| private shouldUpdatePackage(name: string, currentVersion: string): boolean { | |
| if (!UNI_APP_DEPENDENCIES.includes(name)) | |
| return false | |
| const skipPrefixes = ['catalog:', 'workspace:', 'file:', 'link:', 'git+', 'github:', 'npm:'] | |
| return !skipPrefixes.some(p => currentVersion.startsWith(p)) | |
| } |
| private getTargetVersion(currentVersion: string, target: Tag | string): string { | ||
| if (target.startsWith('3') || target.startsWith('2')) { | ||
| return target | ||
| } | ||
|
|
||
| const isVue3 = currentVersion.startsWith('3') | ||
| const key = isVue3 | ||
| ? (target === 'alpha' ? 'vue3Alpha' : 'vue3Latest') | ||
| : (target === 'alpha' ? 'vue2Alpha' : 'vue2Latest') | ||
|
|
||
| return this.versionMap[key] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Robust Vue major detection
currentVersion may start with ^, ~, >=, etc., so startsWith('3')/('2') is unreliable.
- private getTargetVersion(currentVersion: string, target: Tag | string): string {
- if (target.startsWith('3') || target.startsWith('2')) {
- return target
- }
-
- const isVue3 = currentVersion.startsWith('3')
- const key = isVue3
+ private getTargetVersion(currentVersion: string, target: Tag | string): string {
+ if (/^\d/.test(target))
+ return target
+ const match = currentVersion.match(/\d+/)
+ const major = match ? Number(match[0]) : NaN
+ const isVue3 = Number.isFinite(major) ? major >= 3 : true
+ const key = isVue3
? (target === 'alpha' ? 'vue3Alpha' : 'vue3Latest')
: (target === 'alpha' ? 'vue2Alpha' : 'vue2Latest')
-
- return this.versionMap[key]
+ return this.versionMap[key]
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private getTargetVersion(currentVersion: string, target: Tag | string): string { | |
| if (target.startsWith('3') || target.startsWith('2')) { | |
| return target | |
| } | |
| const isVue3 = currentVersion.startsWith('3') | |
| const key = isVue3 | |
| ? (target === 'alpha' ? 'vue3Alpha' : 'vue3Latest') | |
| : (target === 'alpha' ? 'vue2Alpha' : 'vue2Latest') | |
| return this.versionMap[key] | |
| } | |
| private getTargetVersion(currentVersion: string, target: Tag | string): string { | |
| if (/^\d/.test(target)) | |
| return target | |
| const match = currentVersion.match(/\d+/) | |
| const major = match ? Number(match[0]) : NaN | |
| const isVue3 = Number.isFinite(major) ? major >= 3 : true | |
| const key = isVue3 | |
| ? (target === 'alpha' ? 'vue3Alpha' : 'vue3Latest') | |
| : (target === 'alpha' ? 'vue2Alpha' : 'vue2Latest') | |
| return this.versionMap[key] | |
| } |
🤖 Prompt for AI Agents
In src/cli/commands/update.ts around lines 28 to 39, the code uses
currentVersion.startsWith('3')/('2') which fails for ranges like ^3.0.0, ~2.6,
>=3.1, etc.; use a proper semver parse (e.g., semver.coerce or semver.valid) to
extract the numeric major version and compare that instead. Replace the
startsWith checks with logic that coerces/parses currentVersion to a SemVer
object, reads semver.major(parsed) (or parsed.major), falls back to a safe
default if parsing fails, and then compute key based on major === 3; ensure you
add or use the semver dependency and handle null returns from semver.coerce by
treating as non-Vue3.
| async function getLatestVersion(packageName: string, tag: Tag): Promise<string> { | ||
| const commands = [ | ||
| 'npm view', | ||
| packageName, | ||
| tag === 'alpha' ? 'version' : 'versions', | ||
| ...(tag === 'latest' ? ['--json', `| jq '.[] | select(contains("alpha") | not)' | tail -1`] : []), | ||
| ] | ||
|
|
||
| try { | ||
| const { stdout } = await exec(commands.join(' ')) | ||
| return tag === 'latest' ? JSON.parse(stdout) : stdout.trim() | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to get ${tag} version of ${packageName}`, { cause: error }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix version resolution: no shell pipes; correct alpha retrieval
- Piping to jq won’t work (no shell), and jq isn’t guaranteed.
- alpha is not a field to npm view; use package@alpha or dist-tags.
Based on learnings
-async function getLatestVersion(packageName: string, tag: Tag): Promise<string> {
- const commands = [
- 'npm view',
- packageName,
- tag === 'alpha' ? 'version' : 'versions',
- ...(tag === 'latest' ? ['--json', `| jq '.[] | select(contains("alpha") | not)' | tail -1`] : []),
- ]
-
- try {
- const { stdout } = await exec(commands.join(' '))
- return tag === 'latest' ? JSON.parse(stdout) : stdout.trim()
- }
- catch (error) {
- throw new Error(`Failed to get ${tag} version of ${packageName}`, { cause: error })
- }
-}
+async function getLatestVersion(packageName: string, tag: Tag): Promise<string> {
+ try {
+ if (tag === 'alpha') {
+ const { stdout } = await x('npm', ['view', `${packageName}@alpha`, 'version'])
+ return stdout.trim()
+ }
+ const { stdout } = await x('npm', ['view', packageName, 'versions', '--json'])
+ const versions = JSON.parse(stdout) as string[]
+ const stable = versions.filter(v => !/alpha/i.test(v)).pop()
+ if (!stable)
+ throw new Error(`No stable version found for ${packageName}`)
+ return stable
+ }
+ catch (error) {
+ throw new Error(`Failed to get ${tag} version of ${packageName}`, { cause: error })
+ }
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/cli/commands/update.ts around lines 53-68, the current getLatestVersion
builds a shell pipeline with jq (which won’t run in exec without a shell and jq
may not exist) and uses the wrong approach for alpha; replace that logic: for
alpha call npm view <package>@alpha version (exec the command and return
stdout.trim()), for latest call npm view <package> dist-tags --json then
JSON.parse(stdout).latest (or use npm view <package> version to get the latest
directly), and ensure you remove any shell pipes and parse JSON from stdout when
using --json; keep the existing try/catch and wrap thrown errors with a
descriptive message and the original error as the cause.
| const packageJsonPaths = await globby([ | ||
| 'package.json', | ||
| 'packages/**/package.json', | ||
| ], { | ||
| cwd: process.cwd(), | ||
| deep: 2, | ||
| }) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scan all workspace packages; current patterns miss docs/playground/examples
The search omits pnpm-workspace.yaml entries (docs, playground, examples/*) and deep: 2 is unnecessary.
- const packageJsonPaths = await globby([
- 'package.json',
- 'packages/**/package.json',
- ], {
- cwd: process.cwd(),
- deep: 2,
- })
+ const packageJsonPaths = await globby(
+ [
+ 'package.json',
+ 'packages/**/package.json',
+ 'examples/**/package.json',
+ 'playground/**/package.json',
+ 'docs/**/package.json',
+ ],
+ {
+ cwd: process.cwd(),
+ gitignore: true,
+ ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.output/**'],
+ },
+ )Alternatively, derive patterns from pnpm-workspace.yaml packages for zero drift.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const packageJsonPaths = await globby([ | |
| 'package.json', | |
| 'packages/**/package.json', | |
| ], { | |
| cwd: process.cwd(), | |
| deep: 2, | |
| }) | |
| const packageJsonPaths = await globby( | |
| [ | |
| 'package.json', | |
| 'packages/**/package.json', | |
| 'examples/**/package.json', | |
| 'playground/**/package.json', | |
| 'docs/**/package.json', | |
| ], | |
| { | |
| cwd: process.cwd(), | |
| gitignore: true, | |
| ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.output/**'], | |
| }, | |
| ) |
🤖 Prompt for AI Agents
In src/cli/commands/update.ts around lines 115 to 122, the globby patterns and
deep: 2 option miss workspace packages (docs, playground, examples/*) and
unnecessarily limit recursion; change to derive patterns from
pnpm-workspace.yaml by reading its "packages" array and mapping each entry to a
package.json pattern (e.g. entry ->
`${entry.replace(/\/\*\*?$/,'')}/package.json` or append '/package.json' for
globby), and if pnpm-workspace.yaml is absent fall back to broader patterns such
as '**/package.json', 'packages/**/package.json', 'examples/**/package.json',
'docs/**/package.json', 'playground/**/package.json'; remove the deep: 2 option
so globby can recurse as needed and keep cwd: process.cwd().
| cli.command('update [version]', '更新依赖版本') | ||
| .action(async (version) => { | ||
| handleUpdateCommand(version) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Await the async handler and default version to 'latest'
Avoid unhandled promise and undefined version crash.
- cli.command('update [version]', '更新依赖版本')
- .action(async (version) => {
- handleUpdateCommand(version)
- })
+ cli.command('update [version]', '更新依赖版本')
+ .action(async (versionArg) => {
+ await handleUpdateCommand(versionArg ?? 'latest')
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| cli.command('update [version]', '更新依赖版本') | |
| .action(async (version) => { | |
| handleUpdateCommand(version) | |
| }) | |
| cli.command('update [version]', '更新依赖版本') | |
| .action(async (versionArg) => { | |
| await handleUpdateCommand(versionArg ?? 'latest') | |
| }) |
🤖 Prompt for AI Agents
In src/cli/index.ts around lines 60 to 63, the command handler calls the async
function without awaiting it and doesn't handle an undefined version; change the
action to await the async handler and pass a safe default version of 'latest'
when version is falsy (e.g., await handleUpdateCommand(version || 'latest')),
and optionally wrap the await in a try/catch to surface errors instead of
leaving a rejected promise unhandled.
Description 描述
Linked Issues 关联的 Issues
Additional context 额外上下文
Summary by CodeRabbit
New Features
Chores