;
+
+/**
+ * @param code Module code
+ * @param id Source module specifier
+ */
+export const transformCssImports = async (
+ code: string,
+ id: string,
+ { resolveId }: { resolveId: ResolveFn },
+) => {
+ const CSS_IMPORTS = /@import\s+["'](.*?)["'];|url\(["']?(.*?)["']?\)/g;
+
+ let out = code;
+ let offset = 0;
+
+ let match;
+ while ((match = CSS_IMPORTS.exec(code))) {
+ const spec = match[1] || match[2];
+ const start = match.index + match[0].indexOf(spec) + offset;
+ const end = start + spec.length;
+
+ const resolved = await resolveId(spec, id);
+ if (typeof resolved === 'string') {
+ out = out.slice(0, start) + resolved + out.slice(end);
+ offset += resolved.length - spec.length;
+ }
+ }
+
+ return out;
+};
diff --git a/tests/utils/external-with-reference.css b/tests/utils/external-with-reference.css
new file mode 100644
index 00000000..a1b0b342
--- /dev/null
+++ b/tests/utils/external-with-reference.css
@@ -0,0 +1,8 @@
+@import './external.css';
+
+div {
+ background-image: url('./smiley.svg');
+ background-size: cover;
+ width: 500px;
+ height: 500px;
+}
diff --git a/tests/utils/loadCSS.test.ts b/tests/utils/loadCSS.test.ts
index 832f07ef..227f1b24 100644
--- a/tests/utils/loadCSS.test.ts
+++ b/tests/utils/loadCSS.test.ts
@@ -50,3 +50,76 @@ test(
);
test.todo('throws useful error message when imported file has syntax error');
+
+test(
+ 'imported stylesheet has reference to another stylesheet',
+ withBrowser(async ({ utils, screen }) => {
+ await utils.injectHTML(`
+ I'm a heading
+ `);
+ const heading = await screen.getByText(/i'm a heading/i);
+ await expect(heading).toBeVisible();
+ await utils.loadCSS('./external-with-reference.css');
+ await expect(heading).not.toBeVisible();
+ }),
+);
+
+test(
+ 'imported stylesheet has reference to static asset',
+ withBrowser(async ({ utils, page, screen }) => {
+ await page.setRequestInterception(true);
+ page.on('request', (interceptedRequest) => interceptedRequest.continue());
+ await utils.injectHTML(`
+ I have a background image
+ `);
+
+ const timeout = 500;
+
+ const stylesheet1Promise = page.waitForResponse(
+ (response) => {
+ const url = new URL(response.url());
+ if (url.pathname !== '/tests/utils/external-with-reference.css')
+ return false;
+ // This CSS file is loaded via JS import so it needs to have a JS content-type
+ expect(response.headers()['content-type']).toEqual(
+ 'application/javascript;charset=utf-8',
+ );
+ return true;
+ },
+ { timeout },
+ );
+
+ const stylesheet2Promise = page.waitForResponse(
+ (response) => {
+ const url = new URL(response.url());
+ if (url.pathname !== '/tests/utils/external.css') return false;
+ // This CSS file is loaded via @import so it needs to have a CSS content-type
+ expect(response.headers()['content-type']).toEqual(
+ 'text/css;charset=utf-8',
+ );
+ return true;
+ },
+ { timeout },
+ );
+
+ const imagePromise = page.waitForResponse(
+ (response) => {
+ const url = new URL(response.url());
+ if (url.pathname !== '/tests/utils/smiley.svg') return false;
+ expect(response.headers()['content-type']).toEqual('image/svg+xml');
+ return true;
+ },
+ { timeout },
+ );
+
+ await utils.loadCSS('./external-with-reference.css');
+ await stylesheet1Promise;
+ await imagePromise;
+ await stylesheet2Promise;
+
+ const div = await screen.getByText(/background/i);
+ expect(
+ await div.evaluate((div) => getComputedStyle(div).backgroundImage),
+ ).toMatch(/^url\(".*\/tests\/utils\/smiley.svg"\)$/);
+ }),
+);
diff --git a/tests/utils/runJS.test.tsx b/tests/utils/runJS.test.tsx
index 21ef50a8..284ff85b 100644
--- a/tests/utils/runJS.test.tsx
+++ b/tests/utils/runJS.test.tsx
@@ -221,3 +221,16 @@ test(
await expect(heading).toHaveTextContent('Hi');
}),
);
+
+test(
+ 'Allows importing CSS into JS file',
+ withBrowser(async ({ utils, screen }) => {
+ await utils.injectHTML('This is a heading
');
+ const heading = await screen.getByRole('heading');
+ await expect(heading).toBeVisible();
+ await utils.runJS(`
+ import './external.sass'
+ `);
+ await expect(heading).not.toBeVisible();
+ }),
+);
diff --git a/tests/utils/smiley.svg b/tests/utils/smiley.svg
new file mode 100644
index 00000000..c2d8f4c9
--- /dev/null
+++ b/tests/utils/smiley.svg
@@ -0,0 +1 @@
+
\ No newline at end of file