Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion glimmer-scoped-css/src/ast-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ASTPluginEnvironment> & {
filename: string;
Expand Down Expand Up @@ -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`
Expand Down
21 changes: 21 additions & 0 deletions glimmer-scoped-css/src/encoding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* 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

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)
);
}
3 changes: 2 additions & 1 deletion glimmer-scoped-css/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { generateScopedCSSPlugin } from './ast-transform';
import { decodeCSS } from './encoding';

export interface GlimmerScopedCSSOptions {
noGlobal?: boolean;
Expand Down Expand Up @@ -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]!) };
}
4 changes: 4 additions & 0 deletions test-app/app/components/multiple.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ const MultipleInner = <template>
p {
font-weight: 700;
}

p:before {
content: '✓';
}
</style>
</template>;

Expand Down
11 changes: 11 additions & 0 deletions test-app/tests/acceptance/scoped-css-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ module('Acceptance | scoped css', function (hooks) {
'font-weight': '700',
});

const multipleInnerElement = find('[data-test-multiple-inner]');
const multipleInnerElementBeforeStyle = getComputedStyle(
multipleInnerElement!,
':before'
);

assert.strictEqual(
multipleInnerElementBeforeStyle.getPropertyValue('content'),
'"✓"'
);

assert.dom('[data-test-multiple-outer]').hasStyle({
'font-style': 'italic',
'font-weight': '900',
Expand Down
Loading