-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
fix(nuxt,vite): support hmr for templates, pages and page metadata #30113
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
Conversation
|
WalkthroughThe pull request introduces significant updates to the Nuxt package, primarily focusing on enhancing hot module replacement (HMR) functionality for routes and page metadata. Key changes include the addition of HMR code in the routing system, improvements to type definitions for middleware and layouts, and updates to the handling of Vite server events. New Vue components and configuration files are also introduced to support these enhancements, alongside modifications to the test suite to ensure robust testing of HMR features. Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 2
🧹 Outside diff range and nitpick comments (7)
test/hmr.test.ts (3)
43-47
: Enhance File Modification RobustnessUsing chained string replacements to modify
index.vue
may be brittle if the file structure changes. Consider using a templating approach or parsing the file into an AST (Abstract Syntax Tree) for more reliable modifications.As a suggestion, you can use a template literal or an AST manipulation library to ensure the replacements are accurate:
- let newContents = indexVue - .replace('<Title>HMR fixture</Title>', '<Title>HMR fixture HMR</Title>') - .replace('<h1>Home page</h1>', '<h1>Home page - but not as you knew it</h1>') - newContents += '<style scoped>\nh1 { color: red }\n</style>' + const newContents = ` + <template> + <Title>HMR fixture HMR</Title> + <h1>Home page - but not as you knew it</h1> + </template> + <style scoped> + h1 { color: red } + </style> + `
110-143
: Skip HMR Page Meta Test for WebpackThe
it.skipIf(isWebpack)
condition skips the test when using Webpack. Since HMR for page meta may not be fully supported with Webpack, this conditional skip is appropriate. However, consider providing implementation or alternative tests for Webpack to ensure coverage.Would you like assistance in developing Webpack-compatible HMR tests for page meta?
122-136
: Avoid Brittle Console Log AssertionsDirectly asserting the entire
consoleLogs
array may lead to fragile tests due to potential changes in logging behavior. Consider checking for specific log entries or patterns instead of strict equality.For example:
expect(consoleLogs.some(log => log.text.includes('[vite] hot updated: /pages/page-meta.vue'))).toBe(true)test/fixtures/hmr/components/islands/HmrComponent.vue (1)
6-9
: Update Template Structure for ConsistencyChanging the
<pre>
tag to a<div>
with a<span>
containing thedata-testid="hmr-id"
attribute improves semantic HTML and aligns with modern testing practices usingdata-testid
.This change enhances the clarity of the component's structure and makes it easier to select elements during testing.
test/fixtures/hmr/pages/route-rules.vue (2)
1-7
: LGTM! Consider adding more test scenarios.The route rules definition is correct and follows best practices. However, since this is a test fixture for HMR, consider adding more test scenarios:
- Different header values for testing updates
- Multiple route rules properties (e.g., redirect, ssr, cors)
Example enhancement:
defineRouteRules({ headers: { 'x-extend': 'added in routeRules', + 'cache-control': 'no-cache', }, + ssr: true, + cors: true })
9-13
: Consider adding visual feedback for HMR testing.The template is minimal, which is good for basic testing. However, for HMR testing, consider adding visual indicators that make it clear when updates occur.
Example enhancement:
<template> <div> Route rules defined inline + <pre>{{ $route.meta }}</pre> + <div>Last updated: {{ new Date().toISOString() }}</div> </div> </template>packages/vite/src/vite.ts (1)
213-217
: Excellent optimization of template invalidation!The changes significantly improve HMR performance by:
- Processing only changed templates instead of scanning all modules
- Using precise module lookup with the correct
virtual:nuxt:
prefixConsider adding error handling
While the implementation is solid, consider adding error handling for edge cases:
ctx.nuxt.hook('app:templatesGenerated', (_app, changedTemplates) => { + if (!changedTemplates?.length) return for (const template of changedTemplates) { + if (!template?.dst) continue for (const mod of server.moduleGraph.getModulesByFile(`virtual:nuxt:${template.dst}`) || []) { server.moduleGraph.invalidateModule(mod) server.reloadModule(mod) } } })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
packages/nuxt/src/pages/module.ts
(3 hunks)packages/nuxt/src/pages/plugins/page-meta.ts
(4 hunks)packages/nuxt/src/pages/runtime/plugins/router.ts
(2 hunks)packages/vite/src/vite-node.ts
(1 hunks)packages/vite/src/vite.ts
(1 hunks)test/fixtures/hmr/components/islands/HmrComponent.vue
(1 hunks)test/fixtures/hmr/nuxt.config.ts
(1 hunks)test/fixtures/hmr/package.json
(1 hunks)test/fixtures/hmr/pages/index.vue
(1 hunks)test/fixtures/hmr/pages/page-meta.vue
(1 hunks)test/fixtures/hmr/pages/route-rules.vue
(1 hunks)test/fixtures/hmr/pages/routes/index.vue
(1 hunks)test/fixtures/hmr/tsconfig.json
(1 hunks)test/hmr.test.ts
(3 hunks)test/utils.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- test/fixtures/hmr/pages/routes/index.vue
- test/fixtures/hmr/tsconfig.json
- test/fixtures/hmr/package.json
🔇 Additional comments (29)
test/hmr.test.ts (12)
8-8
: Import Updated Helper Functions
The import statement now includes expectNoErrorsOrWarnings
, expectWithPolling
, and renderPage
from './utils'
. This enhances the test suite by utilizing these helper functions for better readability and maintainability.
17-17
: Update Fixture Path to HMR Fixture
The fixture path has been updated to ./fixtures-temp/hmr
, aligning the tests with the new HMR functionality. This change ensures that the tests target the correct fixture for hot module replacement scenarios.
29-30
: Read index.vue
from Updated Fixture Path
The code now reads index.vue
from the updated fixturePath
. This ensures that any modifications to the page during tests are accurately applied to the correct file.
35-36
: Verify Initial Page Load Content
The expectations check that the page title is 'HMR fixture'
and the initial count is '1'
. This confirms that the page loads with the correct initial state before any HMR updates.
39-40
: Simulate User Interaction
Clicking the button and expecting the count to increment to '2'
verifies that user interactions are working correctly and that the application state updates accordingly.
49-49
: Ensure HMR Updates Page Title
Using expectWithPolling
to check for the updated page title 'HMR fixture HMR'
confirms that the HMR is correctly updating the page without a full reload.
52-53
: Verify Updated Heading Text
The test correctly verifies that the heading text has been updated to 'Home page - but not as you knew it'
, ensuring that content changes are reflected via HMR.
56-57
: Check Updated Style Application
Verifying the color of the <h1>
element ensures that style changes are hot reloaded. The expected color "rgb(255, 0, 0)"
confirms that the scoped style is applied.
60-60
: Consolidate Error Checking with expectNoErrorsOrWarnings
The use of expectNoErrorsOrWarnings(consoleLogs)
streamlines error and warning checks, improving test clarity and reducing repetitive code.
76-82
: Ensure Route Rules HMR Functionality
The test modifies the content of route-rules.vue
and checks if the custom header 'x-extend'
is updated accordingly. This verifies that route rules are hot reloaded as expected.
86-108
: Test HMR for Islands Components
The HMR test for island components effectively changes the hmrId
and verifies that the updates are reflected in the rendered output. This ensures that isolated components support HMR.
145-165
: Test HMR for Dynamic Routes
The test adds a new route dynamically and verifies navigation to it without a full page reload. This confirms that HMR updates the router configuration appropriately.
packages/nuxt/src/pages/runtime/plugins/router.ts (2)
20-20
: Import handleHotUpdate
for HMR Support
The import statement for handleHotUpdate
from #build/routes
introduces HMR capabilities to the router. This enhancement allows the router to update its routes dynamically during development.
90-91
: Invoke handleHotUpdate
After Router Initialization
Calling handleHotUpdate(router)
ensures that the router instance is correctly passed to the HMR handler. This setup is necessary for the router to respond to route changes in real-time.
packages/nuxt/src/pages/module.ts (3)
458-459
: Pass isPage
and routesPath
to PageMetaPlugin
Providing isPage
and routesPath
to the PageMetaPlugin
enhances its ability to accurately process page metadata and supports the new HMR functionality.
504-510
: Include HMR Code in Routes Template
The addition of ROUTES_HMR_CODE
to the routes template incorporates the necessary HMR logic directly into the generated routes file. This integration ensures that route changes are hot reloaded without a full page refresh.
615-637
: Implement HMR Logic for Routes
The ROUTES_HMR_CODE
constant defines the HMR behavior for routes, including accepting hot updates, clearing existing routes, and adding new ones. The handleHotUpdate
function ensures the router instance is available for HMR.
test/fixtures/hmr/pages/page-meta.vue (2)
1-5
: Define Page Meta for Testing HMR
The use of definePageMeta
with some: 'stuff'
sets up the component for testing HMR of page metadata. This is a valid addition and aligns with the objectives of enhancing HMR capabilities.
9-9
: Display Route Meta Information
Rendering {{ $route.meta }}
within the data-testid="meta"
element allows for testing whether HMR updates to page metadata are reflected in the application. This implementation is appropriate for the test case.
test/fixtures/hmr/nuxt.config.ts (1)
1-10
: Verify the compatibility date and experimental features.
A few points to consider:
- The compatibility date is set to a future date ('2024-06-28'). This might cause issues if the features aren't ready by then.
- The experimental features are crucial for HMR testing, but enabling all of them might mask specific HMR-related issues.
Let's check the compatibility date usage across the codebase:
✅ Verification successful
Let me gather more information about the experimental features and compatibility date to make a more informed decision.
Based on the results, I can now generate a final response as we have sufficient information about both the compatibility date and experimental features.
The compatibility date and experimental features are correctly configured.
The compatibility date '2024-06-28' is consistently used across test fixtures and is handled properly by the framework:
- The framework has a fallback mechanism for compatibility dates
- It prompts developers to update the date in development environments
- The date is used consistently across test fixtures for standardization
The inlineRouteRules
experimental feature is properly integrated:
- It's used in the pages module for route rule definitions
- The implementation includes proper import handling and Nitro integration
- Its usage in the test fixture is appropriate for HMR testing
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other compatibility date references
rg --type ts "compatibilityDate.*2024-06-28"
# Check if there are any related experimental features in the codebase
rg --type ts "experimental.*inlineRouteRules"
Length of output: 851
Script:
#!/bin/bash
# Check for compatibility date documentation or configuration
rg -A 5 "compatibilityDate" packages/
# Check for inline route rules documentation or implementation
rg -A 10 "inlineRouteRules" packages/nuxt/src/pages/module.ts
# Look for experimental features documentation
fd "README.md" packages/ -x rg -l "experimental"
Length of output: 4388
test/utils.ts (2)
60-64
: LGTM: Enhanced error checking and cleanup in expectNoClientErrors
The changes improve test reliability by:
- Adding explicit array emptiness check for page errors
- Reusing the new
expectNoErrorsOrWarnings
function - Ensuring proper cleanup by closing the page after checks
66-72
: LGTM: Well-structured error checking utility
The new expectNoErrorsOrWarnings
function provides a clean, reusable way to verify both error and warning logs. The implementation correctly filters by log type and uses strict equality checks.
packages/vite/src/vite-node.ts (2)
42-43
: LGTM: Clean middleware setup
The middleware registration is clear and correctly uses the Vite node app with invalidation tracking.
44-51
: Verify line number continuity in template invalidation logic
While the template invalidation logic is correct, there appears to be a gap in line numbering (missing line 48). This could indicate:
- A potential missing line in the implementation
- A formatting issue
packages/nuxt/src/pages/plugins/page-meta.ts (5)
16-17
: LGTM: Enhanced plugin configuration options
The new options improve flexibility:
isPage
: Allows custom page detection logicroutesPath
: Enables custom routes path configuration
27-31
: LGTM: Improved dev mode support
The new CODE_DEV_EMPTY
constant provides better defaults for development mode by initializing with an empty object instead of null.
164-173
: LGTM: Enhanced HMR handling
The changes improve HMR support by:
- Using post-order handling for better compatibility
- Properly invalidating both macro and routes modules
- Correctly handling page-specific updates
157-157
: LGTM: Consistent fallback behavior
The fallback to dev-mode empty content maintains consistency with the rest of the implementation.
99-103
: Verify error handling for empty files
The error handling for empty files includes logging, but we should verify that the error message is properly displayed in the console and doesn't break the development experience.
✅ Verification successful
Let me gather more context about the error handling implementation.
Let me check the actual implementation of these constants and the logger usage in the file.
Let me check the logger implementation and test coverage.
Based on the gathered information, I can now provide the final response:
Error handling for empty files is properly implemented
The implementation is robust and safe:
- Empty files are handled gracefully by providing a fallback empty page meta object (
CODE_DEV_EMPTY
in dev mode,CODE_EMPTY
in production) - The error message is logged through the standard Nuxt logger (consola), which ensures proper console output
- HMR support is maintained even for empty files through
CODE_HMR
- The error doesn't break the development experience as the code continues to execute with a valid fallback
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for similar error handling patterns in the codebase
rg -A 2 "logger.error.*not.*valid.*page" packages/
Length of output: 391
Script:
#!/bin/bash
# Check the implementation of logger and its error handling
ast-grep --pattern 'const logger = $_'
# Check for any test cases related to empty page handling
rg -l "empty.*page|page.*empty" packages/ -g "*.test.*"
# Look for the constants used in error handling
rg "CODE_DEV_EMPTY|CODE_EMPTY|CODE_HMR" packages/
Length of output: 1647
Script:
#!/bin/bash
# Get the full content of page-meta.ts to understand the error handling flow
cat packages/nuxt/src/pages/plugins/page-meta.ts
# Check if there are any related error handling tests
rg -A 5 "logger.*error" packages/kit/src/logger.test.ts
Length of output: 6831
Script:
#!/bin/bash
# Check the logger implementation
cat packages/kit/src/logger.ts
# Check for any test files that might cover empty page scenarios
rg -l "empty.*page|page.*empty" packages/ -g "*.spec.*"
rg -l "empty.*page|page.*empty" packages/ -g "*.test.*"
# Check if there are any related issues or discussions
rg -i "empty page|invalid page" packages/ -g "*.md"
Length of output: 458
<script setup lang="ts"> | ||
definePageMeta({ | ||
some: 'stuff', | ||
}) | ||
const count = ref(1) | ||
</script> |
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
Consider expanding page metadata for comprehensive HMR testing.
The current page metadata is minimal. Since this PR focuses on HMR for page metadata, consider adding more metadata properties to test different update scenarios.
Example enhancement:
definePageMeta({
some: 'stuff',
+ layout: 'default',
+ middleware: ['auth'],
+ pageTransition: {
+ name: 'fade',
+ mode: 'out-in'
+ }
})
📝 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.
<script setup lang="ts"> | |
definePageMeta({ | |
some: 'stuff', | |
}) | |
const count = ref(1) | |
</script> | |
<script setup lang="ts"> | |
definePageMeta({ | |
some: 'stuff', | |
layout: 'default', | |
middleware: ['auth'], | |
pageTransition: { | |
name: 'fade', | |
mode: 'out-in' | |
} | |
}) | |
const count = ref(1) | |
</script> |
<template> | ||
<div> | ||
<Title>HMR fixture</Title> | ||
<h1>Home page</h1> | ||
<div> | ||
Count: | ||
<span data-testid="count">{{ count }}</span> | ||
</div> | ||
<button @click="count++"> | ||
Increment | ||
</button> | ||
<pre>{{ $route.meta }}</pre> | ||
</div> | ||
</template> |
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
Add HMR-specific test scenarios and visual indicators.
While the component includes basic reactivity testing, it could benefit from more HMR-specific test scenarios:
- The test ID is only on the count display
- No visual feedback for meta updates
- No way to trigger template-only changes for HMR testing
Example enhancement:
<template>
<div>
<Title>HMR fixture</Title>
- <h1>Home page</h1>
+ <h1 data-testid="title">Home page</h1>
<div>
Count:
<span data-testid="count">{{ count }}</span>
</div>
<button @click="count++">
Increment
</button>
- <pre>{{ $route.meta }}</pre>
+ <pre data-testid="meta">{{ $route.meta }}</pre>
+ <div data-testid="hmr-update">
+ Last HMR update: {{ new Date().toISOString() }}
+ </div>
</div>
</template>
📝 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.
<template> | |
<div> | |
<Title>HMR fixture</Title> | |
<h1>Home page</h1> | |
<div> | |
Count: | |
<span data-testid="count">{{ count }}</span> | |
</div> | |
<button @click="count++"> | |
Increment | |
</button> | |
<pre>{{ $route.meta }}</pre> | |
</div> | |
</template> | |
<template> | |
<div> | |
<Title>HMR fixture</Title> | |
<h1 data-testid="title">Home page</h1> | |
<div> | |
Count: | |
<span data-testid="count">{{ count }}</span> | |
</div> | |
<button @click="count++"> | |
Increment | |
</button> | |
<pre data-testid="meta">{{ $route.meta }}</pre> | |
<div data-testid="hmr-update"> | |
Last HMR update: {{ new Date().toISOString() }} | |
</div> | |
</div> | |
</template> |
🔗 Linked issue
resolves #27985
resolves #24794
resolves #21690
resolves #26417
📚 Description
This bundles a number of changes related to HMR (with vite).
First, it reduces the templates that are invalidated, using the new
changedTemplates
option passed toapp:templateGenerated, which should improve performance in dev mode. It also corrects the path to add the
virtual:nuxt:` prefix - otherwise these files were not invalidated.Second, it adds HMR handling for route updates + page meta updates, based on patterns from https://github.com/posva/unplugin-vue-router (thanks to @posva for the initial tip!)
Third, it resolves an unrelated issue where a null page meta would fail on HMR as Object.assign could not be called on it. (This was only occuring for tsx as HMR was broken in other situations 🤣)
Finally, I refactored the HMR test suite to add tests for these changes and also split it out into a separate fixture to improve performance when testing.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation
Tests