|
1 | 1 | import { postcssIsolateStyles } from 'node/postcss/isolateStyles'
|
2 | 2 | import postcss from 'postcss'
|
3 | 3 |
|
4 |
| -function apply(selector: string) { |
5 |
| - const { root } = postcss([postcssIsolateStyles()]).process(`${selector} {}`) |
6 |
| - return (root.nodes[0] as any).selector |
| 4 | +const INPUT_CSS = ` |
| 5 | +/* simple classes */ |
| 6 | +.example { color: red; } |
| 7 | +.class-a { color: coral; } |
| 8 | +.class-b { color: deepskyblue; } |
| 9 | +
|
| 10 | +/* escaped colon in class */ |
| 11 | +.baz\\:not\\(.bar\\) { display: block; } |
| 12 | +.disabled\\:opacity-50:disabled { opacity: .5; } |
| 13 | +
|
| 14 | +/* pseudos (class + element) */ |
| 15 | +.button:hover { color: pink; } |
| 16 | +.button:focus:hover { color: hotpink; } |
| 17 | +.item::before { content: '•'; } |
| 18 | +::first-letter { color: pink; } |
| 19 | +::before { content: ''; } |
| 20 | +
|
| 21 | +/* universal + :not */ |
| 22 | +* { background-color: red; } |
| 23 | +*:not(.b) { text-transform: uppercase; } |
| 24 | +
|
| 25 | +/* combinators */ |
| 26 | +.foo:hover .bar { background: blue; } |
| 27 | +ul > li.active { color: green; } |
| 28 | +a + b ~ c { color: orange; } |
| 29 | +
|
| 30 | +/* ids + attribute selectors */ |
| 31 | +#wow { color: yellow; } |
| 32 | +[data-world] .d { padding: 10px 20px; } |
| 33 | +
|
| 34 | +/* :root and chained tags */ |
| 35 | +:root { --bs-blue: #0d6efd; } |
| 36 | +:root .a { --bs-green: #bada55; } |
| 37 | +html { margin: 0; } |
| 38 | +body { padding: 0; } |
| 39 | +html body div { color: blue; } |
| 40 | +
|
| 41 | +/* grouping with commas */ |
| 42 | +.a, .b { color: red; } |
| 43 | +
|
| 44 | +/* multiple repeated groups to ensure stability */ |
| 45 | +.a, .b { color: coral; } |
| 46 | +.a { animation: glow 1s linear infinite alternate; } |
| 47 | +
|
| 48 | +/* nested blocks */ |
| 49 | +.foo { |
| 50 | + svg { display: none; } |
| 51 | + .bar { display: inline; } |
7 | 52 | }
|
8 | 53 |
|
9 |
| -describe('node/postcss/isolateStyles', () => { |
10 |
| - test('splitSelectorPseudo skips escaped colon', () => { |
11 |
| - expect(apply('.foo\\:bar')).toBe( |
12 |
| - '.foo\\:bar:not(:where(.vp-raw, .vp-raw *))' |
13 |
| - ) |
14 |
| - }) |
| 54 | +/* standalone pseudos */ |
| 55 | +:first-child { color: pink; } |
| 56 | +:hover { color: blue; } |
| 57 | +:active { color: red; } |
15 | 58 |
|
16 |
| - test('splitSelectorPseudo splits on pseudo selectors', () => { |
17 |
| - expect(apply('.button:hover')).toBe( |
18 |
| - '.button:not(:where(.vp-raw, .vp-raw *)):hover' |
19 |
| - ) |
| 59 | +/* keyframes (should be ignored) */ |
| 60 | +@keyframes fade { |
| 61 | + from { opacity: 0; } |
| 62 | + to { opacity: 1; } |
| 63 | +} |
| 64 | +@-webkit-keyframes glow { |
| 65 | + from { color: coral; } |
| 66 | + to { color: red; } |
| 67 | +} |
| 68 | +@-moz-keyframes glow { |
| 69 | + from { color: coral; } |
| 70 | + to { color: red; } |
| 71 | +} |
| 72 | +@-o-keyframes glow { |
| 73 | + from { color: coral; } |
| 74 | + to { color: red; } |
| 75 | +} |
| 76 | +` |
| 77 | + |
| 78 | +describe('node/postcss/isolateStyles', () => { |
| 79 | + test('transforms selectors and skips keyframes', () => { |
| 80 | + const out = run(INPUT_CSS) |
| 81 | + expect(out.css).toMatchSnapshot() |
20 | 82 | })
|
21 | 83 |
|
22 |
| - test('postcssIsolateStyles inserts :not(...) in the right place', () => { |
23 |
| - expect(apply('.disabled\\:opacity-50:disabled')).toBe( |
24 |
| - '.disabled\\:opacity-50:not(:where(.vp-raw, .vp-raw *)):disabled' |
25 |
| - ) |
| 84 | + test('idempotent (running twice produces identical CSS)', () => { |
| 85 | + const first = run(INPUT_CSS).css |
| 86 | + const second = run(first).css |
| 87 | + expect(second).toBe(first) |
26 | 88 | })
|
27 | 89 | })
|
| 90 | + |
| 91 | +function run(css: string, from = 'src/styles/vp-doc.css') { |
| 92 | + return postcss([postcssIsolateStyles()]).process(css, { from }) |
| 93 | +} |
0 commit comments