From c98345d1952a820fe41eed6ad6fd4ff19ab4f22b Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 28 Apr 2025 14:32:55 -0500 Subject: [PATCH 1/4] Fix CSS encoding to handle non-ASCII characters --- glimmer-scoped-css/src/ast-transform.ts | 3 ++- glimmer-scoped-css/src/encoding.ts | 19 +++++++++++++++++++ glimmer-scoped-css/src/index.ts | 3 ++- test-app/app/components/multiple.gjs | 4 ++++ test-app/tests/acceptance/scoped-css-test.ts | 11 +++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 glimmer-scoped-css/src/encoding.ts diff --git a/glimmer-scoped-css/src/ast-transform.ts b/glimmer-scoped-css/src/ast-transform.ts index 0489a0d..b724d6d 100644 --- a/glimmer-scoped-css/src/ast-transform.ts +++ b/glimmer-scoped-css/src/ast-transform.ts @@ -9,6 +9,7 @@ import postcss from 'postcss'; import scopedStylesPlugin from './postcss-plugin'; import { basename } from 'path'; import { GlimmerScopedCSSOptions } from '.'; +import { encodeCSS } from './encoding'; type Env = WithJSUtils & { filename: string; @@ -76,7 +77,7 @@ export function generateScopedCSSPlugin( // TODO: hard coding the loader chain means we ignore the other // prevailing rules (and we're even assuming these loaders are // available) - let encodedCss = encodeURIComponent(btoa(outputCSS)); + let encodedCss = encodeCSS(outputCSS); jsutils.importForSideEffect( `./${basename(env.filename)}.${encodedCss}.glimmer-scoped.css` diff --git a/glimmer-scoped-css/src/encoding.ts b/glimmer-scoped-css/src/encoding.ts new file mode 100644 index 0000000..0f1375f --- /dev/null +++ b/glimmer-scoped-css/src/encoding.ts @@ -0,0 +1,19 @@ +/** + * These functions convert arbitrary CSS to URI-safe strings that are used + * as data-URI virtual imports. + */ + +export function encodeCSS(plainCSSString: string) { + const binString = Array.from( + new TextEncoder().encode(plainCSSString), + (byte) => String.fromCodePoint(byte) + ).join(''); + return encodeURIComponent(btoa(binString)); +} + +export function decodeCSS(encodedCSSString: string) { + const binString = atob(decodeURIComponent(encodedCSSString)); + return new TextDecoder().decode( + Uint8Array.from(binString, (m) => m.codePointAt(0) as number) + ); +} diff --git a/glimmer-scoped-css/src/index.ts b/glimmer-scoped-css/src/index.ts index 3146f8f..0ba0f89 100644 --- a/glimmer-scoped-css/src/index.ts +++ b/glimmer-scoped-css/src/index.ts @@ -1,4 +1,5 @@ import { generateScopedCSSPlugin } from './ast-transform'; +import { decodeCSS } from './encoding'; export interface GlimmerScopedCSSOptions { noGlobal?: boolean; @@ -40,5 +41,5 @@ export function decodeScopedCSSRequest(request: string): { if (!m) { throw new Error(`not a scoped CSS request: ${request}`); } - return { fromFile: m[1]!, css: atob(decodeURIComponent(m[2]!)) }; + return { fromFile: m[1]!, css: decodeCSS(m[2]!) }; } diff --git a/test-app/app/components/multiple.gjs b/test-app/app/components/multiple.gjs index 4044f07..ee8b35e 100644 --- a/test-app/app/components/multiple.gjs +++ b/test-app/app/components/multiple.gjs @@ -8,6 +8,10 @@ const MultipleInner = ; diff --git a/test-app/tests/acceptance/scoped-css-test.ts b/test-app/tests/acceptance/scoped-css-test.ts index 55fc540..7a0397a 100644 --- a/test-app/tests/acceptance/scoped-css-test.ts +++ b/test-app/tests/acceptance/scoped-css-test.ts @@ -86,6 +86,17 @@ module('Acceptance | scoped css', function (hooks) { 'font-weight': '700', }); + let multipleInnerElement = find('[data-test-multiple-inner]'); + let multipleInnerElementBeforeStyle = getComputedStyle( + multipleInnerElement!, + ':before' + ); + + assert.strictEqual( + multipleInnerElementBeforeStyle.getPropertyValue('content'), + '"✓"' + ); + assert.dom('[data-test-multiple-outer]').hasStyle({ 'font-style': 'italic', 'font-weight': '900', From ee43e46abba196b23c691028f851e559f58787af Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 28 Apr 2025 14:35:41 -0500 Subject: [PATCH 2/4] Fix lint errors --- test-app/tests/acceptance/scoped-css-test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-app/tests/acceptance/scoped-css-test.ts b/test-app/tests/acceptance/scoped-css-test.ts index 7a0397a..3cf041f 100644 --- a/test-app/tests/acceptance/scoped-css-test.ts +++ b/test-app/tests/acceptance/scoped-css-test.ts @@ -86,8 +86,8 @@ module('Acceptance | scoped css', function (hooks) { 'font-weight': '700', }); - let multipleInnerElement = find('[data-test-multiple-inner]'); - let multipleInnerElementBeforeStyle = getComputedStyle( + const multipleInnerElement = find('[data-test-multiple-inner]'); + const multipleInnerElementBeforeStyle = getComputedStyle( multipleInnerElement!, ':before' ); From fc71baa2676da3dc267840f29925a059da3f79d2 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 28 Apr 2025 14:44:05 -0500 Subject: [PATCH 3/4] Add provenance --- glimmer-scoped-css/src/encoding.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glimmer-scoped-css/src/encoding.ts b/glimmer-scoped-css/src/encoding.ts index 0f1375f..0128b8a 100644 --- a/glimmer-scoped-css/src/encoding.ts +++ b/glimmer-scoped-css/src/encoding.ts @@ -3,6 +3,8 @@ * as data-URI virtual imports. */ +// Adapted from https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa#unicode_strings + export function encodeCSS(plainCSSString: string) { const binString = Array.from( new TextEncoder().encode(plainCSSString), From 0d0c4385190170083b698ccb157735a53205a6b9 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 28 Apr 2025 14:57:00 -0500 Subject: [PATCH 4/4] Update glimmer-scoped-css/src/encoding.ts Co-authored-by: Luke Melia --- glimmer-scoped-css/src/encoding.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glimmer-scoped-css/src/encoding.ts b/glimmer-scoped-css/src/encoding.ts index 0128b8a..77c3335 100644 --- a/glimmer-scoped-css/src/encoding.ts +++ b/glimmer-scoped-css/src/encoding.ts @@ -1,6 +1,6 @@ /** - * These functions convert arbitrary CSS to URI-safe strings that are used - * as data-URI virtual imports. + * These functions convert between arbitrary normally formatted CSS and + * URI-safe strings that are used as data-URI virtual imports. */ // Adapted from https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa#unicode_strings