From f382af56cc1c23088e61b0ebb9c43d6945b6780f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 11 Oct 2024 16:03:59 -0400 Subject: [PATCH 1/3] Warn if mime type is not CSS --- index.html | 2 ++ src/fetch.ts | 28 ++++++++++++++++++++++++---- tests/helpers.ts | 5 +++++ tests/unit/fetch.test.ts | 6 +++--- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index ae319786..abc8143f 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,8 @@ defer > + + diff --git a/src/fetch.ts b/src/fetch.ts index 7d414e88..c9efc876 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -2,6 +2,8 @@ import { nanoid } from 'nanoid/non-secure'; import { type StyleData } from './utils.js'; +const INVALID_MIME_TYPE_ERROR = 'InvalidMimeType'; + export function isStyleLink(link: HTMLLinkElement): link is HTMLLinkElement { return Boolean( (link.type === 'text/css' || link.rel === 'stylesheet') && link.href, @@ -18,17 +20,35 @@ function getStylesheetUrl(link: HTMLLinkElement): URL | undefined { async function fetchLinkedStylesheets( sources: Partial[], ): Promise { - return Promise.all( + const results = await Promise.all( sources.map(async (data) => { if (!data.url) { return data as StyleData; } // fetch css and add to array - const response = await fetch(data.url.toString()); - const css = await response.text(); - return { ...data, css } as StyleData; + try { + const response = await fetch(data.url.toString()); + const type = response.headers.get('content-type'); + if (type !== 'text/css') { + const error = new Error( + `Error loading ${data.url}: expected content-type "text/css", got "${type}".`, + ); + error.name = INVALID_MIME_TYPE_ERROR; + throw error; + } + const css = await response.text(); + return { ...data, css } as StyleData; + } catch (error) { + if (error instanceof Error && error.name === INVALID_MIME_TYPE_ERROR) { + // eslint-disable-next-line no-console + console.warn(error); + return null; + } + throw error; + } }), ); + return results.filter((loaded) => loaded !== null); } // Searches for all elements with inline style attributes that include `anchor`. diff --git a/tests/helpers.ts b/tests/helpers.ts index 29688cd9..da1718ca 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -25,3 +25,8 @@ export function cascadeCSSForTest(css: string) { cascadeCSS([styleObj]); return styleObj.css; } + +export const requestWithCSSType = (css: string) => ({ + body: css, + headers: { 'Content-Type': 'text/css' }, +}); diff --git a/tests/unit/fetch.test.ts b/tests/unit/fetch.test.ts index 6bfb1f8d..105df490 100644 --- a/tests/unit/fetch.test.ts +++ b/tests/unit/fetch.test.ts @@ -1,7 +1,7 @@ import fetchMock from 'fetch-mock'; import { fetchCSS } from '../../src/fetch.js'; -import { getSampleCSS } from '../helpers.js'; +import { getSampleCSS, requestWithCSSType } from '../helpers.js'; describe('fetch stylesheet', () => { beforeAll(() => { @@ -22,7 +22,7 @@ describe('fetch stylesheet', () => { it('fetches CSS', async () => { const css = getSampleCSS('anchor-positioning'); - fetchMock.getOnce('end:sample.css', css); + fetchMock.getOnce('end:sample.css', requestWithCSSType(css)); const styleData = await fetchCSS(); expect(styleData).toHaveLength(2); @@ -81,7 +81,7 @@ describe('fetch inline styles', () => { it('fetch returns inline CSS', async () => { const css = getSampleCSS('anchor-positioning'); - fetchMock.getOnce('end:sample.css', css); + fetchMock.getOnce('end:sample.css', requestWithCSSType(css)); const styleData = await fetchCSS(); expect(styleData).toHaveLength(4); From 9a66ad517e847ce67eee42481bae5fc80f4099f2 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 11 Oct 2024 16:15:10 -0400 Subject: [PATCH 2/3] Broader mimetype check --- src/fetch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fetch.ts b/src/fetch.ts index c9efc876..ed974a5b 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -29,7 +29,7 @@ async function fetchLinkedStylesheets( try { const response = await fetch(data.url.toString()); const type = response.headers.get('content-type'); - if (type !== 'text/css') { + if (!type?.startsWith('text/css')) { const error = new Error( `Error loading ${data.url}: expected content-type "text/css", got "${type}".`, ); From a199f0fa942236f41df7a2a47ad0512d0c112da1 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 14 Oct 2024 11:36:42 -0400 Subject: [PATCH 3/3] Update index.html Co-authored-by: Jonny Gerig Meyer --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index abc8143f..7fd4e44f 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ defer > - +