fix(runtime): avoid error when vue/test-utils is not installed#1646
fix(runtime): avoid error when vue/test-utils is not installed#1646yamachi4416 wants to merge 1 commit intonuxt:mainfrom
Conversation
commit: |
d255a2b to
20ccb22
Compare
📝 WalkthroughWalkthroughThe change updates src/runtime/shared/vue-wrapper-plugin.ts: it replaces the Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/runtime/shared/vue-wrapper-plugin.ts`:
- Around line 9-18: The current import of '@vue/test-utils' swallows all errors
and hides real issues; change the dynamic import logic used to set config (the
import('@vue/test-utils') => r.config pattern) so the catch only converts
"module not found" resolution errors into undefined and rethrows any other
errors; in practice replace the blanket .catch(() => undefined) with logic that
inspects the thrown error (e.g. error.code === 'MODULE_NOT_FOUND' or
'ERR_MODULE_NOT_FOUND' or message patterns for missing module) and returns
undefined only for those cases, otherwise rethrow so getVueWrapperPlugin and its
hasNuxtPage behavior aren't masking broken installations or runtime failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c9747a13-6ab0-440a-9c58-9564e75a3028
📒 Files selected for processing (1)
src/runtime/shared/vue-wrapper-plugin.ts
| const config = await import('@vue/test-utils') | ||
| .then(r => r.config) | ||
| .catch(() => undefined) | ||
|
|
||
| export function getVueWrapperPlugin(): Options { | ||
| if (!config) { | ||
| return { | ||
| hasNuxtPage: () => false, | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the vue-wrapper-plugin.ts file to see the current state
cat -n src/runtime/shared/vue-wrapper-plugin.tsRepository: nuxt/test-utils
Length of output: 2045
🏁 Script executed:
# Now check how getVueWrapperPlugin() is consumed in nuxt.ts
cat -n src/runtime/shared/nuxt.ts | head -30Repository: nuxt/test-utils
Length of output: 938
🏁 Script executed:
# Check if there are any other usages of getVueWrapperPlugin in the codebase
rg "getVueWrapperPlugin" --type ts --type tsx -nRepository: nuxt/test-utils
Length of output: 86
🏁 Script executed:
# Retry search for getVueWrapperPlugin with simpler approach
rg "getVueWrapperPlugin" -nRepository: nuxt/test-utils
Length of output: 531
🏁 Script executed:
# Check package.json to see if vue/test-utils is optional or required
cat package.json | grep -A 5 -B 5 "test-utils"Repository: nuxt/test-utils
Length of output: 2056
🏁 Script executed:
# Check Node.js version and what error codes are typical for module not found
# Also verify how top-level await works with module initialization
python3 << 'EOF'
import json
# Document error code behavior for dynamic import failures
error_behaviors = {
"ERR_MODULE_NOT_FOUND": "Module does not exist in filesystem/registry",
"ERR_UNKNOWN_FILE_EXTENSION": "File has unrecognized extension",
"SyntaxError": "Module has syntax error",
"ReferenceError": "Module has undefined reference",
"TypeError": "Module execution throws TypeError",
}
print("Dynamic import failure scenarios:")
for code, desc in error_behaviors.items():
print(f" {code}: {desc}")
print("\nTop-level await behavior:")
print(" - Module with top-level await becomes 'async module'")
print(" - Importers of that module wait for initialization to complete")
print(" - But functions IN that module remain synchronous")
EOFRepository: nuxt/test-utils
Length of output: 557
🏁 Script executed:
# Look at the full package.json structure
head -40 package.jsonRepository: nuxt/test-utils
Length of output: 1019
🏁 Script executed:
# Verify standard Node.js error codes for module not found
python3 << 'EOF'
# Node.js module resolution error patterns
print("Node.js Module Resolution Errors:")
print()
print("When require/import fails to resolve a module:")
print(" - Error.code === 'ERR_MODULE_NOT_FOUND' (most common)")
print(" - Error.message typically includes: 'Cannot find module ...'")
print(" - Error.message typically includes: 'no such file or directory'")
print()
print("When a module exists but fails during evaluation:")
print(" - Error.code may be undefined")
print(" - Error could be SyntaxError, ReferenceError, TypeError, etc.")
print(" - These are real bugs that should NOT be silently swallowed")
print()
print("Key insight for `@vue/test-utils`:")
print(" - If package is genuinely optional, catch only resolution failures")
print(" - If package exists but is broken, that's a configuration bug")
print(" - Silently returning hasNuxtPage: () => false hides the real problem")
EOFRepository: nuxt/test-utils
Length of output: 772
🏁 Script executed:
# Check if there's any existing error handling pattern in the codebase
rg "ERR_MODULE_NOT_FOUND|error\.code" --type ts -A 3 -B 1 | head -50Repository: nuxt/test-utils
Length of output: 41
🏁 Script executed:
# Verify the exact structure of r.config to confirm the .then(r => r.config) is correct
# Look for any type definitions or usage
rg "@vue/test-utils" src/ -A 2 -B 2 --type ts | head -40Repository: nuxt/test-utils
Length of output: 1741
Distinguish between missing and broken @vue/test-utils.
The blanket .catch(() => undefined) treats all import failures identically—missing packages, broken installations, unsupported export shapes, and evaluation errors all silently become hasNuxtPage: () => false. This hides real configuration bugs. Since @vue/test-utils is optional, catch only module resolution failures and rethrow everything else.
Recommended approach
-const config = await import('@vue/test-utils')
- .then(r => r.config)
- .catch(() => undefined)
+const config = await import('@vue/test-utils')
+ .then((r) => {
+ if (!r.config) {
+ throw new Error('Unsupported `@vue/test-utils` version: missing config export')
+ }
+ return r.config
+ })
+ .catch((error: any) => {
+ const isMissingVueTestUtils =
+ error?.code === 'ERR_MODULE_NOT_FOUND'
+ && String(error.message).includes('@vue/test-utils')
+ if (isMissingVueTestUtils) return undefined
+ throw error
+ })Adjust the error predicate as needed for your target module systems, but the key point is to avoid swallowing non-resolution failures.
📝 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 config = await import('@vue/test-utils') | |
| .then(r => r.config) | |
| .catch(() => undefined) | |
| export function getVueWrapperPlugin(): Options { | |
| if (!config) { | |
| return { | |
| hasNuxtPage: () => false, | |
| } | |
| } | |
| const config = await import('@vue/test-utils') | |
| .then((r) => { | |
| if (!r.config) { | |
| throw new Error('Unsupported `@vue/test-utils` version: missing config export') | |
| } | |
| return r.config | |
| }) | |
| .catch((error: any) => { | |
| const isMissingVueTestUtils = | |
| error?.code === 'ERR_MODULE_NOT_FOUND' | |
| && String(error.message).includes('@vue/test-utils') | |
| if (isMissingVueTestUtils) return undefined | |
| throw error | |
| }) | |
| export function getVueWrapperPlugin(): Options { | |
| if (!config) { | |
| return { | |
| hasNuxtPage: () => false, | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/runtime/shared/vue-wrapper-plugin.ts` around lines 9 - 18, The current
import of '@vue/test-utils' swallows all errors and hides real issues; change
the dynamic import logic used to set config (the import('@vue/test-utils') =>
r.config pattern) so the catch only converts "module not found" resolution
errors into undefined and rethrows any other errors; in practice replace the
blanket .catch(() => undefined) with logic that inspects the thrown error (e.g.
error.code === 'MODULE_NOT_FOUND' or 'ERR_MODULE_NOT_FOUND' or message patterns
for missing module) and returns undefined only for those cases, otherwise
rethrow so getVueWrapperPlugin and its hasNuxtPage behavior aren't masking
broken installations or runtime failures.
There was a problem hiding this comment.
hasNuxtPage is only meaningful when @vue/test-utils is available and used in tests.
There was a problem hiding this comment.
@yamachi4416, that's a fair point — since hasNuxtPage() ultimately relies on VueWrapper instances that only exist when @vue/test-utils is installed and actively used in tests, returning false is always the correct semantic fallback regardless of the reason the import failed. I'll withdraw my suggestion.
🧠 Learnings used
Learnt from: Archetipo95
Repo: nuxt/test-utils PR: 0
File: :0-0
Timestamp: 2026-03-30T10:17:20.984Z
Learning: In nuxt/test-utils, the `RouterLink` stub in `src/runtime-utils/components/RouterLink.ts` uses `useLink(props)` from vue-router to derive `isActive`, `isExactActive`, `href`, `route`, and `navigate`. The custom slot provides all five. Nested-route parent-active state does not work in `mountSuspended` because VTU strips static properties (including `.useLink`) from components registered via `global.components`, so NuxtLink falls back to exact path comparison. This is tracked as a known limitation with an `it.fails` regression test.
20ccb22 to
62c7076
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/runtime/shared/vue-wrapper-plugin.ts (1)
11-13:⚠️ Potential issue | 🟠 MajorHandle only “module not found” here; don’t swallow all import errors.
The current
.catch(() => undefined)hides real failures (broken package, runtime/evaluation errors, incompatible export shape) and makes debugging much harder. Keep the optional-dependency behavior, but only suppress true resolution failures for@vue/test-utils.Proposed fix
const config = await import('@vue/test-utils') - .then(r => r.config) - .catch(() => undefined) + .then((r) => { + if (!r.config) { + throw new Error('Unsupported `@vue/test-utils` version: missing `config` export') + } + return r.config + }) + .catch((error: unknown) => { + const e = error as { code?: string; message?: string } + const isModuleNotFound = + (e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'MODULE_NOT_FOUND') + && String(e.message ?? '').includes('@vue/test-utils') + if (isModuleNotFound) return undefined + throw error + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/runtime/shared/vue-wrapper-plugin.ts` around lines 11 - 13, The import of '@vue/test-utils' assigns to the const config but the catch currently swallows all errors; change the catch on the dynamic import so it only returns undefined for a module-not-found/resolution error and rethrows any other errors (so keep the optional-dependency behavior for missing package but surface actual runtime/evaluation/shape errors). Concretely, update the promise chain around the dynamic import of '@vue/test-utils' that sets config to inspect the caught error (e.g., check err.code === 'ERR_MODULE_NOT_FOUND' or test err.message for "Cannot find module" depending on runtime) and return undefined only in that case; otherwise throw the error so functions referencing config will fail fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/runtime/shared/vue-wrapper-plugin.ts`:
- Around line 11-13: The import of '@vue/test-utils' assigns to the const config
but the catch currently swallows all errors; change the catch on the dynamic
import so it only returns undefined for a module-not-found/resolution error and
rethrows any other errors (so keep the optional-dependency behavior for missing
package but surface actual runtime/evaluation/shape errors). Concretely, update
the promise chain around the dynamic import of '@vue/test-utils' that sets
config to inspect the caught error (e.g., check err.code ===
'ERR_MODULE_NOT_FOUND' or test err.message for "Cannot find module" depending on
runtime) and return undefined only in that case; otherwise throw the error so
functions referencing config will fail fast.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 86100996-22fa-47b9-99f8-79a8fbf2bc13
📒 Files selected for processing (1)
src/runtime/shared/vue-wrapper-plugin.ts
🔗 Linked issue
📚 Description
Fixed an error that occurs when
@vue/test-utilsis not installed.Reproduction
Before:
@vue/test-utilsis not installedhttps://stackblitz.com/edit/nuxt-test-utils-pull-1646-fail?file=package.json
After:
@vue/test-utilsis not installedhttps://stackblitz.com/edit/nuxt-test-utils-pull-1646-resolve1?file=package.json
@vue/test-utilsis installedhttps://stackblitz.com/edit/nuxt-test-utils-pull-1646-resolve2?file=package.json