diff --git a/packages/scramjet/packages/core/src/client/dom/css.ts b/packages/scramjet/packages/core/src/client/dom/css.ts index 3429ab0f..0f693b7c 100644 --- a/packages/scramjet/packages/core/src/client/dom/css.ts +++ b/packages/scramjet/packages/core/src/client/dom/css.ts @@ -14,7 +14,7 @@ export default function (client: ScramjetClient) { apply(ctx) { const v = ctx.call(); if (!v) return v; - ctx.return(unrewriteCss(v)); + ctx.return(unrewriteCss(v, client.context)); }, }); @@ -23,7 +23,7 @@ export default function (client: ScramjetClient) { ctx.set(rewriteCss(value, client.context, client.meta)); }, get(ctx) { - return unrewriteCss(ctx.get()); + return unrewriteCss(ctx.get(), client.context); }, }); @@ -50,7 +50,7 @@ export default function (client: ScramjetClient) { ctx.set(rewriteCss(value, client.context, client.meta)); }, get(ctx) { - return unrewriteCss(ctx.get()); + return unrewriteCss(ctx.get(), client.context); }, }); @@ -83,7 +83,7 @@ export default function (client: ScramjetClient) { if (prop in CSSStyleDeclaration.prototype) return value; if (!value) return value; - return unrewriteCss(value); + return unrewriteCss(value, client.context); }, set(target, prop, value) { if (prop == "cssText" || value == "" || typeof value !== "string") { diff --git a/packages/scramjet/packages/core/src/client/dom/element.ts b/packages/scramjet/packages/core/src/client/dom/element.ts index 15dccb0e..3974d210 100644 --- a/packages/scramjet/packages/core/src/client/dom/element.ts +++ b/packages/scramjet/packages/core/src/client/dom/element.ts @@ -442,7 +442,7 @@ export default function (client: ScramjetClient, self: typeof window) { return ctx.get(); } if (client.box.instanceof(ctx.this, "HTMLStyleElement")) { - return unrewriteCss(ctx.get() as string); + return unrewriteCss(ctx.get() as string, client.context); } return ctx.get(); @@ -565,7 +565,7 @@ export default function (client: ScramjetClient, self: typeof window) { client.Trap("Text.prototype.wholeText", { get(ctx) { if (ctx.this.parentElement?.tagName === "STYLE") { - return unrewriteCss(ctx.get() as string); + return unrewriteCss(ctx.get() as string, client.context); } return ctx.get(); diff --git a/packages/scramjet/packages/core/src/shared/rewriters/css.ts b/packages/scramjet/packages/core/src/shared/rewriters/css.ts index 54f52462..ddbaa37b 100644 --- a/packages/scramjet/packages/core/src/shared/rewriters/css.ts +++ b/packages/scramjet/packages/core/src/shared/rewriters/css.ts @@ -10,14 +10,14 @@ export function rewriteCss( return handleCss("rewrite", css, context, meta); } -export function unrewriteCss(css: string) { - return handleCss("unrewrite", css); +export function unrewriteCss(css: string, context: ScramjetContext) { + return handleCss("unrewrite", css, context); } function handleCss( type: "rewrite" | "unrewrite", css: string, - context?: ScramjetContext, + context: ScramjetContext, meta?: URLMeta ) { // regex from vk6 (https://github.com/ading2210) @@ -25,26 +25,26 @@ function handleCss( const Atruleregex = /@import\s+((?i:url)\s*?\(.{0,9999}?\)|['"].{0,9999}?['"]|.{0,9999}?)($|\s|;)/gm; css = String(css); - css = css.replace(urlRegex, (match, url) => { + css = css.replace(urlRegex, (match, url: string) => { const encodedUrl = type === "rewrite" - ? rewriteUrl(url.trim(), context, meta) + ? rewriteUrl(url.trim(), context, meta!) : unrewriteUrl(url.trim(), context); return match.replace(url, encodedUrl); }); - css = css.replace(Atruleregex, (match, importStatement) => { + css = css.replace(Atruleregex, (match, importStatement: string) => { return match.replace( importStatement, importStatement.replace( /^(url\(['"]?|['"]|)(.+?)(['"]|['"]?\)|)$/gm, - (match, firstQuote, url, endQuote) => { + (match: string, firstQuote: string, url: string, endQuote: string) => { if (firstQuote.startsWith("url")) { return match; } const encodedUrl = type === "rewrite" - ? rewriteUrl(url.trim(), context, meta) + ? rewriteUrl(url.trim(), context, meta!) : unrewriteUrl(url.trim(), context); return `${firstQuote}${encodedUrl}${endQuote}`; diff --git a/packages/scramjet/packages/runway/src/tests/rewriter-css.ts b/packages/scramjet/packages/runway/src/tests/rewriter-css.ts index 1fd0ecf8..5b93f5f6 100644 --- a/packages/scramjet/packages/runway/src/tests/rewriter-css.ts +++ b/packages/scramjet/packages/runway/src/tests/rewriter-css.ts @@ -1,4 +1,8 @@ -import { rewriteCss, rewriteUrl } from "@mercuryworkshop/scramjet"; +import { + rewriteCss, + rewriteUrl, + unrewriteCss, +} from "@mercuryworkshop/scramjet"; import { directTest, type Test } from "../testcommon.ts"; function createRewriteContext() { @@ -68,6 +72,28 @@ function cssRewriteMultiTest(props: { }); } +function cssUnrewriteTest(props: { + name: string; + url: string; + fn: (url: string, encoded: string) => [string, string]; +}) { + const { name, url, fn } = props; + return directTest({ + name, + fn: ({ assertEqual }) => { + const { context, meta } = createRewriteContext(); + const encoded = rewriteUrl(url, context, meta); + const [input, expected] = fn(url, encoded); + const unrewritten = unrewriteCss(input, context); + assertEqual( + unrewritten, + expected, + `${name}: ${input} should be unrewritten to ${expected}` + ); + }, + }); +} + export default [ cssRewriteTest({ name: "rewriter-css-url", @@ -277,4 +303,43 @@ export default [ assertEqual(rewriteCss(input, context, meta), input); }, }), + cssUnrewriteTest({ + name: "rewriter-css-unrewrite-url", + url: "/assets/bg.png", + fn: (url, encoded) => [ + `body{background:url("${encoded}")}`, + `body{background:url("${url}")}`, + ], + }), + cssUnrewriteTest({ + name: "rewriter-css-unrewrite-import-string", + url: "/styles/theme.css", + fn: (url, encoded) => [`@import "${encoded}";`, `@import "${url}";`], + }), + cssUnrewriteTest({ + name: "rewriter-css-unrewrite-import-url", + url: "/styles/theme.css", + fn: (url, encoded) => [ + `@import url("${encoded}");`, + `@import url("${url}");`, + ], + }), + cssUnrewriteTest({ + name: "rewriter-css-unrewrite-uppercase-url-function", + url: "/z.png", + fn: (url, encoded) => [ + `b{background:URL("${encoded}")}`, + `b{background:URL("${url}")}`, + ], + }), + directTest({ + name: "rewriter-css-unrewrite-regression-import-layer-supports-media", + fn: ({ assertEqual }) => { + const { context, meta } = createRewriteContext(); + const input = + '@import url("/bundle.css") layer(theme) supports(display: grid) screen, print;'; + const rewritten = rewriteCss(input, context, meta); + assertEqual(unrewriteCss(rewritten, context), input); + }, + }), ];