From 88ff3739a0b2ad267caab0fac5bd0d70af4eb230 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 21 Jan 2022 10:37:31 -0500 Subject: [PATCH 1/5] Fix HMR in static build + @import HMR --- packages/astro/src/core/ssr/index.ts | 15 ++++++++++++++- packages/astro/src/vite-plugin-astro/compile.ts | 6 ++++++ packages/astro/src/vite-plugin-astro/index.ts | 17 ++++++++++++++++- .../astro/vendor/vite/dist/client/client.mjs | 11 +++++++++-- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts index 421e2dcce752..82d9d378534d 100644 --- a/packages/astro/src/core/ssr/index.ts +++ b/packages/astro/src/core/ssr/index.ts @@ -219,6 +219,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`); if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`); + // Add hoisted script tags const scripts = astroConfig.buildOptions.experimentalStaticBuild ? new Set( @@ -229,6 +230,18 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO ) : new Set(); + // Inject HMR scripts + if(mode === 'development' && astroConfig.buildOptions.experimentalStaticBuild) { + scripts.add({ + props: { type: 'module', src: '/@vite/client' }, + children: '', + }); + scripts.add({ + props: { type: 'module', src: new URL('../../runtime/client/hmr.js', import.meta.url).pathname }, + children: '', + }); + } + const result = createResult({ astroConfig, logging, origin, params, pathname, renderers, scripts }); // Resolves specifiers in the inline hydrated scripts, such as "@astrojs/renderer-preact/client.js" result.resolve = async (s: string) => { @@ -249,7 +262,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO const tags: vite.HtmlTagDescriptor[] = []; // dev only: inject Astro HMR client - if (mode === 'development') { + if (mode === 'development' && !astroConfig.buildOptions.experimentalStaticBuild) { tags.push({ tag: 'script', attrs: { type: 'module' }, diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 4c6ceccd7a01..36a17cc9c9c9 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -50,6 +50,12 @@ async function compile(config: AstroConfig, filename: string, source: string, vi experimentalStaticExtraction: config.buildOptions.experimentalStaticBuild, // TODO add experimental flag here preprocessStyle: async (value: string, attrs: Record) => { + // When using this flag CSS because and therefore goes through Vite's + // CSS pipeline. We don't need to transform here. + if(config.buildOptions.experimentalStaticBuild) { + return { code: value }; + } + const lang = `.${attrs?.lang || 'css'}`.toLowerCase(); try { const result = await transformWithVite({ diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 418c7925a6d5..a0d0834126db 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -3,6 +3,7 @@ import type { AstroConfig } from '../@types/astro'; import type { LogOptions } from '../core/logger'; import esbuild from 'esbuild'; +import npath from 'path'; import { fileURLToPath } from 'url'; import { AstroDevServer } from '../core/dev/index.js'; import { getViteTransform, TransformHook } from './styles.js'; @@ -29,6 +30,11 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu } let viteTransform: TransformHook; + + // Variables for determing if an id starts with /src... + const srcRootWeb = config.src.pathname.slice(config.projectRoot.pathname.length - 1); + const isBrowserPath = (path: string) => path.startsWith(srcRootWeb); + return { name: '@astrojs/vite-plugin-astro', enforce: 'pre', // run transforms before other plugins can @@ -38,7 +44,16 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu // note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.) async resolveId(id) { // serve sub-part requests (*?astro) as virtual modules - if (parseAstroRequest(id).query.astro) { + const { query } = parseAstroRequest(id); + if (query.astro) { + // 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)) { + const outId = npath.posix.join(config.projectRoot.pathname, id); + return outId; + } + return id; } }, diff --git a/packages/astro/vendor/vite/dist/client/client.mjs b/packages/astro/vendor/vite/dist/client/client.mjs index 8a10e7ee1ddc..a3e164385e97 100755 --- a/packages/astro/vendor/vite/dist/client/client.mjs +++ b/packages/astro/vendor/vite/dist/client/client.mjs @@ -199,6 +199,11 @@ function warnFailedFetch(err, path) { socket.addEventListener('message', async ({ data }) => { handleMessage(JSON.parse(data)); }); +function cleanUrl(pathname) { + let url = new URL(pathname, location); + url.searchParams.delete('direct'); + return url.pathname + url.search; +} let isFirstUpdate = true; async function handleMessage(payload) { switch (payload.type) { @@ -230,11 +235,13 @@ async function handleMessage(payload) { // css-update // this is only sent when a css file referenced with is updated let { path, timestamp } = update; - path = path.replace(/\?.*/, ''); + let searchUrl = cleanUrl(path); // can't use querySelector with `[href*=]` here since the link may be // using relative paths so we need to use link.href to grab the full // URL for the include check. - const el = [].slice.call(document.querySelectorAll(`link`)).find((e) => e.href.includes(path)); + const el = [].slice.call(document.querySelectorAll(`link`)).find((e) => { + return cleanUrl(e.href).includes(searchUrl) + }); if (el) { const newPath = `${base}${path.slice(1)}${path.includes('?') ? '&' : '?'}t=${timestamp}`; el.href = new URL(newPath, el.href).href; From 838b48611148b8c67d59f0fe69c72804dad9beb7 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 21 Jan 2022 13:27:21 -0500 Subject: [PATCH 2/5] Changeset --- .changeset/angry-apricots-invite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/angry-apricots-invite.md diff --git a/.changeset/angry-apricots-invite.md b/.changeset/angry-apricots-invite.md new file mode 100644 index 000000000000..18db7ab25249 --- /dev/null +++ b/.changeset/angry-apricots-invite.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes HMR of CSS that is imported from astro, when using the static build flag From adf3350d89c15d2676c07435c63d5829f1c72d7c Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 21 Jan 2022 14:45:20 -0500 Subject: [PATCH 3/5] Add a comment on what cleanUrl is doing --- packages/astro/vendor/vite/dist/client/client.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/astro/vendor/vite/dist/client/client.mjs b/packages/astro/vendor/vite/dist/client/client.mjs index a3e164385e97..53609982f9d2 100755 --- a/packages/astro/vendor/vite/dist/client/client.mjs +++ b/packages/astro/vendor/vite/dist/client/client.mjs @@ -199,6 +199,11 @@ function warnFailedFetch(err, path) { socket.addEventListener('message', async ({ data }) => { handleMessage(JSON.parse(data)); }); + +/** + * This cleans up the query params and removes the `direct` param which is internal. + * Other query params are preserved. + */ function cleanUrl(pathname) { let url = new URL(pathname, location); url.searchParams.delete('direct'); From c3b4279d7f7afbce99634b8dcf521c10e912b884 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 21 Jan 2022 14:46:34 -0500 Subject: [PATCH 4/5] Running prettier --- packages/astro/src/core/ssr/index.ts | 3 +-- packages/astro/src/vite-plugin-astro/compile.ts | 2 +- packages/astro/src/vite-plugin-astro/index.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts index 82d9d378534d..e7299d20a011 100644 --- a/packages/astro/src/core/ssr/index.ts +++ b/packages/astro/src/core/ssr/index.ts @@ -219,7 +219,6 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`); if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`); - // Add hoisted script tags const scripts = astroConfig.buildOptions.experimentalStaticBuild ? new Set( @@ -231,7 +230,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO : new Set(); // Inject HMR scripts - if(mode === 'development' && astroConfig.buildOptions.experimentalStaticBuild) { + if (mode === 'development' && astroConfig.buildOptions.experimentalStaticBuild) { scripts.add({ props: { type: 'module', src: '/@vite/client' }, children: '', diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 36a17cc9c9c9..1a55c19d4594 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -52,7 +52,7 @@ async function compile(config: AstroConfig, filename: string, source: string, vi preprocessStyle: async (value: string, attrs: Record) => { // When using this flag CSS because and therefore goes through Vite's // CSS pipeline. We don't need to transform here. - if(config.buildOptions.experimentalStaticBuild) { + if (config.buildOptions.experimentalStaticBuild) { return { code: value }; } diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index a0d0834126db..a252a8e30979 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -49,7 +49,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu // 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)) { + if (query.type === 'style' && isBrowserPath(id)) { const outId = npath.posix.join(config.projectRoot.pathname, id); return outId; } From 289985689b20a7691d14ceda0a360740c5cd66aa Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 24 Jan 2022 11:51:41 -0500 Subject: [PATCH 5/5] Improve comments on how the static build compilation works differently. --- packages/astro/src/vite-plugin-astro/compile.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 1a55c19d4594..d99c96f4a618 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -50,8 +50,9 @@ async function compile(config: AstroConfig, filename: string, source: string, vi experimentalStaticExtraction: config.buildOptions.experimentalStaticBuild, // TODO add experimental flag here preprocessStyle: async (value: string, attrs: Record) => { - // When using this flag CSS because and therefore goes through Vite's - // CSS pipeline. We don't need to transform here. + // When using this flag CSS is added via and therefore goes + // through Vite's CSS pipeline. We don't need to transform here, it will be + // transformed on CSS requests. if (config.buildOptions.experimentalStaticBuild) { return { code: value }; }