Skip to content

Conversation

@sapphi-red
Copy link
Member

@sapphi-red sapphi-red commented Jul 10, 2025

Description

This PR is a revised version of #18972.

The dependencies registered by this.addWatchFile in CSS is now added to the module graph with type: 'asset'. In past, it was added with type: 'js'. This made the JS dep and non-JS dep to be mixed and caused other issues (#3929, #4127, #9512)

fixes #9512
fixes #19786
fixes #19563
refs #8356

@sapphi-red sapphi-red added feat: hmr p3-minor-bug An edge case that only affects very specific usage (priority) labels Jul 10, 2025
if (url.startsWith('data:')) {
depModules.add(moduleGraph.createFileOnlyEntry(file))
} else {
depModules.add(await moduleGraph.ensureEntryFromUrl(url))
Copy link
Member Author

@sapphi-red sapphi-red Jul 10, 2025

Choose a reason for hiding this comment

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

Since this line is removed, #19563 and #19786 will be fixed.

Comment on lines 692 to 695
const isClientHtmlChange =
file.endsWith('.html') && environment.name === 'client'

if (needFullReload || isClientHtmlChange) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@hi-ogawa hi-ogawa Jul 28, 2025

Choose a reason for hiding this comment

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

I understand the intent of this change with respect to the old code, but I just wondered what if html raw import. I think new code forces this to be full reload on html change, but previously the parent module can hmr with the code like this?

import html from "./some-file.html?raw";

export function MyComponent() {
  console.log(html)
  return ...
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I've pushed a commit to fix that case (9eea422). The downside is that if the index.html is imported (import html from './index.html?raw'), the full-reload won't happen even if it should. But I think that's fine as that was the previous behavior and this is really an edge case.

Comment on lines -770 to -781
// additionally check for CSS importers, since a PostCSS plugin like
// Tailwind JIT may register any file as a dependency to a CSS file.
for (const importer of node.importers) {
if (isCSSRequest(importer.url) && !currentChain.includes(importer)) {
propagateUpdate(
importer,
traversedModules,
boundaries,
currentChain.concat(importer),
)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This part is no longer needed. type=asset module is a separate module from type=js, so updateModules is called separately for CSS files.

@sapphi-red
Copy link
Member Author

/ecosystem-ci run

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jul 10, 2025

Open in StackBlitz

npm i https://pkg.pr.new/vite@20391

commit: 02f1fe2

@vite-ecosystem-ci
Copy link

@sapphi-red sapphi-red marked this pull request as draft July 10, 2025 09:32
@sapphi-red
Copy link
Member Author

I need to check the plugin-vue failure.

@sapphi-red
Copy link
Member Author

It was a bug in plugin-vue: vitejs/vite-plugin-vue#625

Comment on lines 692 to 695
const isClientHtmlChange =
file.endsWith('.html') && environment.name === 'client'

if (needFullReload || isClientHtmlChange) {
Copy link
Contributor

@hi-ogawa hi-ogawa Jul 28, 2025

Choose a reason for hiding this comment

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

I understand the intent of this change with respect to the old code, but I just wondered what if html raw import. I think new code forces this to be full reload on html change, but previously the parent module can hmr with the code like this?

import html from "./some-file.html?raw";

export function MyComponent() {
  console.log(html)
  return ...
}

depModules.add(await moduleGraph.ensureEntryFromUrl(url))
}
}
depModules.add(moduleGraph.createFileOnlyEntry(file))
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great! I just explored the case in vitejs/vite-plugin-react#616, so let me share what I found. Client hot update looks like this when changing server component:

before

🔷 [hotUpdate] client [
  {
    environment: 'client',
    url: '/src/routes/root.tsx',
    id: '/home/hiroshi/code/others/vite-plugin-react/packages/plugin-rsc/examples/basic/src/routes/root.tsx',
    file: '/home/hiroshi/code/others/vite-plugin-react/packages/plugin-rsc/examples/basic/src/routes/root.tsx',
    type: 'js',
    info: undefined,
    meta: undefined,
    importers: [ '/src/styles.css?direct', '/src/styles.css' ],

after

🔷 [hotUpdate] client [
  {
    environment: 'client',
    url: '/@fs//home/hiroshi/code/others/vite-plugin-react/packages/plugin-rsc/examples/basic/src/routes/root.tsx',
    id: null,
    file: '/home/hiroshi/code/others/vite-plugin-react/packages/plugin-rsc/examples/basic/src/routes/root.tsx',
    type: 'asset',
    info: undefined,
    meta: undefined,
    importers: [ '/src/styles.css?direct', '/src/styles.css' ],

In my case, I already had filter based on id ctx.modules.map((mod) => mod.id).filter((v) => v !== null), so id: null also helped not hitting my custom css hot update handling.

The double slash in url: '/@fs//...' looks odd, but I'd assume it's essentially a placeholder value, so it shouldn't matter much.

hi-ogawa
hi-ogawa previously approved these changes Jul 28, 2025
Copy link
Contributor

@hi-ogawa hi-ogawa left a comment

Choose a reason for hiding this comment

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

LGTM!

@sapphi-red sapphi-red requested a review from Copilot July 29, 2025 10:06
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes an issue where CSS dependencies were being incorrectly registered as JavaScript modules in the module graph, causing mixed dependencies and HMR problems. The fix ensures that CSS dependencies (like files watched via addWatchFile) are now properly registered with type: 'asset' instead of type: 'js'.

  • Changed module type from 'js' | 'css' to include 'asset' type for better categorization
  • Updated HMR logic to handle asset modules correctly and trigger appropriate reload behavior
  • Added specific handling for HTML file changes to trigger full page reloads when needed

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/vite/src/node/server/moduleGraph.ts Core changes to support 'asset' module type and ensure file-only modules are created as assets
packages/vite/src/node/server/mixedModuleGraph.ts Type definition update to include 'asset' type
packages/vite/src/node/server/hmr.ts HMR logic updates to handle asset modules and HTML file changes properly
packages/vite/src/node/plugins/css.ts Simplified CSS dependency handling to use createFileOnlyEntry for all imports
playground/tailwind/* Test case additions to verify HMR behavior with HTML file changes
playground/assets/* Test case additions to verify raw HTML import HMR functionality

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

Labels

feat: css feat: hmr p3-minor-bug An edge case that only affects very specific usage (priority) trigger: preview

Projects

None yet

2 participants