Skip to content

Conversation

@chouchouji
Copy link
Member

@chouchouji chouchouji commented Oct 14, 2025

Description 描述

Linked Issues 关联的 Issues

Additional context 额外上下文

Summary by CodeRabbit

  • New Features

    • Added a CLI command: update [version] to automatically update UNI app dependencies across the workspace.
    • Supports explicit versions or tags (latest/alpha) and handles Vue 2 and Vue 3 variants.
    • Updates pnpm workspace catalogs and all package.json files in one run.
    • Provides clear success and error logs, with resilient handling of missing files and per-file issues.
  • Chores

    • Updated dependency catalog to include required packages for the new update workflow.

@coderabbitai
Copy link

coderabbitai bot commented Oct 14, 2025

Walkthrough

Introduces 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

Cohort / File(s) Summary
Workspace and dependency catalog updates
package.json, pnpm-workspace.yaml
Added globby and yaml dependencies and catalog entries; adjusted tinyexec from dev to prod deps; reconfigured unconfig catalog entries.
CLI command surfacing
src/cli/index.ts, src/cli/commands/index.ts
Exposed and wired a new update command; added re-export from ./update; CLI now accepts update [version] and routes to handler.
Update command implementation
src/cli/commands/update.ts
Added handler to compute target versions (latest/alpha/explicit), update pnpm-workspace.yaml catalogs and package.json dependencies across workspace; includes npm view querying, error handling, and concurrent updates.
Constants and types
src/constant.ts
Added UNI_APP_DEPENDENCIES array and exported Platform and PlatformDescriptions types.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop through catalogs, tidy and bright,
Nudging versions to latest delight.
With globby paws and yaml dreams,
I stitch the workspace at its seams.
Updates done—carrot-high five! 🥕
(_/) All dependencies now thrive.
(•‿•) Onward, maintainers—push to live!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly describes the main feature introduced by this PR—adding support for an “update” command in the CLI—making the primary change immediately apparent.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 readonly

Prevents 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 const
src/cli/commands/update.ts (2)

93-106: Optional: preserve YAML comments/formatting

Using 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 writes

Some 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

📥 Commits

Reviewing files that changed from the base of the PR and between e68b2a3 and 587894a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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 LGTM

Publicly exposing the update command here is correct.

package.json (1)

53-59: Verify Node engine compatibility for new deps

globby@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'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Comment on lines +24 to +26
private shouldUpdatePackage(name: string, currentVersion: string): boolean {
return UNI_APP_DEPENDENCIES.includes(name) && !currentVersion.startsWith('catalog')
}
Copy link

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.

Suggested change
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))
}

Comment on lines +28 to +39
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]
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +53 to +68
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 })
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Comment on lines +115 to +122
const packageJsonPaths = await globby([
'package.json',
'packages/**/package.json',
], {
cwd: process.cwd(),
deep: 2,
})

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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().

Comment on lines +60 to +63
cli.command('update [version]', '更新依赖版本')
.action(async (version) => {
handleUpdateCommand(version)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant