-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Simplify HMR handling #5811
Simplify HMR handling #5811
Changes from all commits
5d9ea1f
e30d6f2
02ae325
a451570
2baba5b
2b17d90
c4d2cb0
f7ccfe6
964bb25
b005ea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'astro': patch | ||
--- | ||
|
||
Simplify HMR handling |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,20 +4,13 @@ import type { AstroSettings } from '../@types/astro'; | |
import type { LogOptions } from '../core/logger/core.js'; | ||
import type { PluginMetadata as AstroPluginMetadata } from './types'; | ||
|
||
import slash from 'slash'; | ||
import { fileURLToPath } from 'url'; | ||
import { normalizePath } from 'vite'; | ||
import { cachedCompilation, CompileProps, getCachedCompileResult } from '../core/compile/index.js'; | ||
import { | ||
isRelativePath, | ||
prependForwardSlash, | ||
removeLeadingForwardSlashWindows, | ||
startsWithForwardSlash, | ||
} from '../core/path.js'; | ||
import { viteID } from '../core/util.js'; | ||
import { normalizeFilename } from '../vite-plugin-utils/index.js'; | ||
import { isRelativePath } from '../core/path.js'; | ||
import { cachedFullCompilation } from './compile.js'; | ||
import { handleHotUpdate } from './hmr.js'; | ||
import { parseAstroRequest, ParsedRequestResult } from './query.js'; | ||
import { parseAstroRequest } from './query.js'; | ||
import { normalizeFilename } from '../vite-plugin-utils/index.js'; | ||
export { getAstroMetadata } from './metadata.js'; | ||
export type { AstroPluginMetadata }; | ||
|
||
|
@@ -27,85 +20,28 @@ interface AstroPluginOptions { | |
} | ||
|
||
/** Transform .astro files for Vite */ | ||
export default function astro({ settings, logging }: AstroPluginOptions): vite.Plugin { | ||
export default function astro({ settings, logging }: AstroPluginOptions): vite.Plugin[] { | ||
const { config } = settings; | ||
let resolvedConfig: vite.ResolvedConfig; | ||
|
||
// Variables for determining if an id starts with /src... | ||
const srcRootWeb = config.srcDir.pathname.slice(config.root.pathname.length - 1); | ||
const isBrowserPath = (path: string) => path.startsWith(srcRootWeb) && srcRootWeb !== '/'; | ||
const isFullFilePath = (path: string) => | ||
path.startsWith(prependForwardSlash(slash(fileURLToPath(config.root)))); | ||
|
||
function relativeToRoot(pathname: string) { | ||
const arg = startsWithForwardSlash(pathname) ? '.' + pathname : pathname; | ||
const url = new URL(arg, config.root); | ||
return slash(fileURLToPath(url)) + url.search; | ||
} | ||
|
||
function resolveRelativeFromAstroParent(id: string, parsedFrom: ParsedRequestResult): string { | ||
const filename = normalizeFilename(parsedFrom.filename, config); | ||
const resolvedURL = new URL(id, `file://${filename}`); | ||
const resolved = resolvedURL.pathname; | ||
if (isBrowserPath(resolved)) { | ||
return relativeToRoot(resolved + resolvedURL.search); | ||
} | ||
return slash(fileURLToPath(resolvedURL)) + resolvedURL.search; | ||
} | ||
|
||
return { | ||
const prePlugin: vite.Plugin = { | ||
name: 'astro:build', | ||
enforce: 'pre', // run transforms before other plugins can | ||
configResolved(_resolvedConfig) { | ||
resolvedConfig = _resolvedConfig; | ||
}, | ||
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.glob, etc.) | ||
async resolveId(id, from, opts) { | ||
Comment on lines
-62
to
-63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entire |
||
// If resolving from an astro subresource such as a hoisted script, | ||
// we need to resolve relative paths ourselves. | ||
if (from) { | ||
const parsedFrom = parseAstroRequest(from); | ||
const isAstroScript = parsedFrom.query.astro && parsedFrom.query.type === 'script'; | ||
if (isAstroScript && isRelativePath(id)) { | ||
return this.resolve(resolveRelativeFromAstroParent(id, parsedFrom), from, { | ||
custom: opts.custom, | ||
skipSelf: true, | ||
}); | ||
} | ||
} | ||
|
||
// serve sub-part requests (*?astro) as virtual modules | ||
const { query } = parseAstroRequest(id); | ||
if (query.astro) { | ||
// TODO: Try to remove these custom resolve so HMR is more predictable. | ||
// Convert /src/pages/index.astro?astro&type=style to /Users/name/ | ||
// Because this needs to be the id for the Vite CSS plugin to property resolve | ||
// relative @imports. | ||
if (query.type === 'style' && isBrowserPath(id)) { | ||
return relativeToRoot(id); | ||
} | ||
// Strip `/@fs` from linked dependencies outside of root so we can normalize | ||
// it in the condition below. This ensures that the style module shared the same is | ||
// part of the same "file" as the main Astro module in the module graph. | ||
// "file" refers to `moduleGraph.fileToModulesMap`. | ||
if (query.type === 'style' && id.startsWith('/@fs')) { | ||
id = removeLeadingForwardSlashWindows(id.slice(4)); | ||
} | ||
// Convert file paths to ViteID, meaning on Windows it omits the leading slash | ||
if (isFullFilePath(id)) { | ||
return viteID(new URL('file://' + id)); | ||
} | ||
return id; | ||
} | ||
}, | ||
async load(id, opts) { | ||
const parsedId = parseAstroRequest(id); | ||
const query = parsedId.query; | ||
if (!query.astro) { | ||
return null; | ||
} | ||
// For CSS / hoisted scripts, the main Astro module should already be cached | ||
const filename = normalizeFilename(parsedId.filename, config); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still needs a |
||
const filename = normalizePath(normalizeFilename(parsedId.filename, config.root)); | ||
const compileResult = getCachedCompileResult(config, filename); | ||
if (!compileResult) { | ||
return null; | ||
|
@@ -152,7 +88,7 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P | |
if (src.startsWith('/') && !isBrowserPath(src)) { | ||
const publicDir = config.publicDir.pathname.replace(/\/$/, '').split('/').pop() + '/'; | ||
throw new Error( | ||
`\n\n<script src="${src}"> references an asset in the "${publicDir}" directory. Please add the "is:inline" directive to keep this asset from being bundled.\n\nFile: ${filename}` | ||
`\n\n<script src="${src}"> references an asset in the "${publicDir}" directory. Please add the "is:inline" directive to keep this asset from being bundled.\n\nFile: ${id}` | ||
); | ||
} | ||
} | ||
|
@@ -196,20 +132,14 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P | |
return; | ||
} | ||
|
||
const filename = normalizeFilename(parsedId.filename, config); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In other diffs, you'll see that I had removed Since this is a |
||
const compileProps: CompileProps = { | ||
astroConfig: config, | ||
viteConfig: resolvedConfig, | ||
filename, | ||
id, | ||
filename: normalizePath(parsedId.filename), | ||
source, | ||
}; | ||
|
||
const transformResult = await cachedFullCompilation({ | ||
compileProps, | ||
rawId: id, | ||
logging, | ||
}); | ||
const transformResult = await cachedFullCompilation({ compileProps, logging }); | ||
|
||
for (const dep of transformResult.cssDeps) { | ||
this.addWatchFile(dep); | ||
|
@@ -242,7 +172,6 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P | |
astroConfig: config, | ||
viteConfig: resolvedConfig, | ||
filename: context.file, | ||
id: context.modules[0]?.id ?? undefined, | ||
source: await context.read(), | ||
}; | ||
const compile = () => cachedCompilation(compileProps); | ||
|
@@ -253,6 +182,19 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P | |
}); | ||
}, | ||
}; | ||
|
||
const normalPlugin: vite.Plugin = { | ||
name: 'astro:build:normal', | ||
resolveId(id) { | ||
// If Vite resolver can't resolve the Astro request, it's likely a virtual Astro file, fallback here instead | ||
const parsedId = parseAstroRequest(id); | ||
if (parsedId.query.astro) { | ||
return id; | ||
} | ||
}, | ||
}; | ||
Comment on lines
+186
to
+195
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... Continuing. This The reason we can remove the pre plugin Now we let Vite handle instead, and only catch those that don't work with a normal plugin, which runs after Vite's resolver. |
||
|
||
return [prePlugin, normalPlugin]; | ||
} | ||
|
||
function appendSourceMap(content: string, map?: string) { | ||
|
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.
I found the
filename
and themoduleId
passed are always the same (absolute paths or virtual ids). They could only differ in theresolveId
hook, but since we're compiling in thetransform
hook, it doesn't affect us.In a future compiler PR, I'll merge
moduleId
andpathname
options into one.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.
So filename is the module ID right? To do head propagation we rely on the moduleId being passed to the compiler to match what is found in the module graph. Just want to make sure that is still going to be happening here.
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.
Yup, I tested it and saw it's always identical, even for virtual files. I think the unit test should cover if not as I was able to get it to fail when I pass the wrong id during manual testing.
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.
I did another quick review, and I think this part I guess I'm slightly unsure:
astro/packages/astro/src/vite-plugin-astro/index.ts
Lines 244 to 245 in 8404121
When you added the
context.modules[0]?.id
, is it because it differs fromcontext.file
? If I understand the module graph right, Vite's code should have them equal in most case.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.
I think I did that out of "correctness" rather than a concern about
context.file
. So I think we can go with the change for now and see what happens.