diff --git a/.changeset/tame-melons-think.md b/.changeset/tame-melons-think.md new file mode 100644 index 000000000..dd5832424 --- /dev/null +++ b/.changeset/tame-melons-think.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix CSS URLs on Windows diff --git a/packages/astro/src/core/ssr/css.ts b/packages/astro/src/core/ssr/css.ts index ab58d6ff4..afbd53461 100644 --- a/packages/astro/src/core/ssr/css.ts +++ b/packages/astro/src/core/ssr/css.ts @@ -25,7 +25,7 @@ export function getStylesForURL(filePath: URL, viteServer: vite.ViteDevServer): if (!importedModule.id || scanned.has(importedModule.id)) continue; const ext = path.extname(importedModule.id.toLowerCase()); if (STYLE_EXTENSIONS.has(ext)) { - css.add(importedModule.id); // if style file, add to list + css.add(importedModule.url || importedModule.id); // if style file, add to list } else { crawlCSS(importedModule.id, scanned); // otherwise, crawl file to see if it imports any CSS } diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts index 7974a515e..0812c5ed3 100644 --- a/packages/astro/src/core/ssr/index.ts +++ b/packages/astro/src/core/ssr/index.ts @@ -29,7 +29,7 @@ import { injectTags } from './html.js'; import { generatePaginateFunction } from './paginate.js'; import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js'; -const svelteAndVueStylesRE = /\?[^&]+&type=style&lang/; +const svelteStylesRE = /svelte\?svelte&type=style/; interface SSROptions { /** an instance of the AstroConfig */ @@ -247,7 +247,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO // inject CSS [...getStylesForURL(filePath, viteServer)].forEach((href) => { - if (mode === 'development' && svelteAndVueStylesRE.test(href)) { + if (mode === 'development' && svelteStylesRE.test(href)) { tags.push({ tag: 'script', attrs: { type: 'module', src: href }, diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 06b9c0d14..6e58dbe64 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -8,260 +8,308 @@ import { expect } from 'chai'; import cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; -describe('Styles SSR', function () { - this.timeout(30000); // test needs a little more time in CI - - let fixture; - let index$; - let bundledCSS; +let fixture; +describe('CSS', function () { before(async () => { fixture = await loadFixture({ projectRoot: './fixtures/0-css/', renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'], }); - await fixture.build(); - - // get bundled CSS (will be hashed, hence DOM query) - const html = await fixture.readFile('/index.html'); - index$ = cheerio.load(html); - const bundledCSSHREF = index$('link[rel=stylesheet][href^=assets/]').attr('href'); - bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); }); - describe('Astro styles', () => { - it('HTML and CSS scoped correctly', async () => { - const $ = index$; + // test HTML and CSS contents for accuracy + describe('build', () => { + this.timeout(30000); // test needs a little more time in CI - const el1 = $('#dynamic-class'); - const el2 = $('#dynamic-vis'); - const classes = $('#class').attr('class').split(' '); - const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name)); + let $; + let bundledCSS; - // 1. check HTML - expect(el1.attr('class')).to.equal(`blue ${scopedClass}`); - expect(el2.attr('class')).to.equal(`visible ${scopedClass}`); + before(async () => { + await fixture.build(); - // 2. check CSS - expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`); + // get bundled CSS (will be hashed, hence DOM query) + const html = await fixture.readFile('/index.html'); + $ = cheerio.load(html); + const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href'); + bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); }); - it('No + @@ -49,14 +50,14 @@ import ReactDynamic from '../components/ReactDynamic.jsx'; + + + - - - diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index eb282b4c7..702f3aef5 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -25,10 +25,15 @@ import preview from '../dist/core/preview/index.js'; * .config - Returns the final config. Will be automatically passed to the methods below: * * Build - * .build() - Async. Builds into current folder (will erase previous build) - * .readFile(path) - Async. Read a file from the build. - * .preview() - Async. Starts a preview server. Note this can’t be running in same fixture as .dev() as they share ports. Also, you must call `server.close()` before test exit - * .fetch(url) - Async. Returns a URL from the prevew server (must have called .preview() before) + * .build() - Async. Builds into current folder (will erase previous build) + * .readFile(path) - Async. Read a file from the build. + * + * Dev + * .startDevServer() - Async. Starts a dev server at an available port. Be sure to call devServer.stop() before test exit. + * .fetch(url) - Async. Returns a URL from the prevew server (must have called .preview() before) + * + * Preview + * .preview() - Async. Starts a preview server. Note this can’t be running in same fixture as .dev() as they share ports. Also, you must call `server.close()` before test exit */ export async function loadFixture(inlineConfig) { if (!inlineConfig || !inlineConfig.projectRoot) throw new Error("Must provide { projectRoot: './fixtures/...' }"); @@ -52,7 +57,11 @@ export async function loadFixture(inlineConfig) { return { build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }), - startDevServer: () => dev(config, { logging: 'error' }), + startDevServer: async (opts = {}) => { + const devServer = await dev(config, { logging: 'error', ...opts }); + inlineConfig.devOptions.port = devServer.port; // update port + return devServer; + }, config, fetch: (url, init) => fetch(`http://${config.devOptions.hostname}:${config.devOptions.port}${url.replace(/^\/?/, '/')}`, init), preview: async (opts = {}) => {