Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfc1dbe9

Browse files
committed
Investigate parsel and css-selector-parser for new globalfication.
1 parent276037a commitfc1dbe9

File tree

7 files changed

+401
-0
lines changed

7 files changed

+401
-0
lines changed

‎globalfy/.node-version‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
18.19.0

‎globalfy/README.md‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#Globalfy
2+
3+
This is just a test directory for me to figure out how to improve the global-fication of css in this project.
4+
5+
Remove this before finishing PR.
6+
7+
##Notes
8+
9+
- parsel didn't work for me... it parses fine but then cannot convert AST back into a selector after modification. The stringify "helper" just smushes the tokens together omitting combinators, commas, and everything else.
10+
- I tried`css-selector-parser` and it works great. In postcss's terms,`h1, h2` is a rule made up of two selectors,`h1` and`h2`. Since`svelte-preprocess` calls`globalifySelector` on each individual selector (i.e.,`rule.selectors.map(globalifySelector)`), that means we don't actually need to worry about parsing the top-level rule into selectors. However,`css-selector-parser` does do it perfectly well, so I designed it to handle both cases.
11+
- The terminology is a little different in`css-selector-parser`. In their lingo, a selector is the top level thing, and it is composed of`rules`.
12+
13+
I tested two strategies (using`css-selector-parser` terminology):
14+
1. Wrap entire rules in`:global()` (i.e.,`h1, h2` ->`:global(h1, h2)`).
15+
2. Recurse deeper and wrap each rule in`:global()` (i.e.,`h1, h2` ->`:global(h1), :global(h2)`).
16+
17+
Strategy 2 seems more in line with what is normal right now. However, I don't really understand this. The only constraint I've seen from Svelte is from the error that was the genesis of my work on this:`:global(...) must contain a single selector`. This seems to suggest that the most reasonable thing to do (and which is also faster) is to wrap the entire rule (remember, what svelte calls a "selector"`css-selector-parser` calls a "rule") in`:global()`. In other words, Svelte is saying you cannot do`:global(h1, h2)`, but you can do`:global(h1 > h2)`, since the former is two selectors and the latter is a single selector.
18+
19+
Here is the output of`node ./globalfy.js`:
20+
21+
```txt
22+
input: .foo
23+
STRAT1: :global(.foo)
24+
STRAT2: :global(.foo)
25+
26+
input: ul + p
27+
STRAT1: :global(ul + p)
28+
STRAT2: :global(ul) + :global(p)
29+
30+
input: p > a
31+
STRAT1: :global(p > a)
32+
STRAT2: :global(p) > :global(a)
33+
34+
input: p + p
35+
STRAT1: :global(p + p)
36+
STRAT2: :global(p) + :global(p)
37+
38+
input: li a
39+
STRAT1: :global(li a)
40+
STRAT2: :global(li) :global(a)
41+
42+
input: div ~ a
43+
STRAT1: :global(div ~ a)
44+
STRAT2: :global(div) ~ :global(a)
45+
46+
input: div, a
47+
STRAT1: :global(div), :global(a)
48+
STRAT2: :global(div), :global(a)
49+
50+
input: .foo.bar
51+
STRAT1: :global(.foo.bar)
52+
STRAT2: :global(.foo.bar)
53+
54+
input: [attr="with spaces"]
55+
STRAT1: :global([attr="with spaces"])
56+
STRAT2: :global([attr="with spaces"])
57+
58+
input: article :is(h1, h2)
59+
STRAT1: :global(article :is(h1, h2))
60+
STRAT2: :global(article) :global(:is(h1, h2))
61+
62+
input: tr:nth-child(2n+1)
63+
STRAT1: :global(tr:nth-child(2n+1))
64+
STRAT2: :global(tr:nth-child(2n+1))
65+
66+
input: p:nth-child(n+8):nth-child(-n+15)
67+
STRAT1: :global(p:nth-child(n+8):nth-child(-n+15))
68+
STRAT2: :global(p:nth-child(n+8):nth-child(-n+15))
69+
70+
input: #foo > .bar + div.k1.k2 [id='baz']:not(:where(#yolo))::before
71+
STRAT1: :global(#foo > .bar + div.k1.k2 [id="baz"]:not(:where(#yolo))::before)
72+
STRAT2: :global(#foo) > :global(.bar) + :global(div.k1.k2) :global([id="baz"]:not(:where(#yolo))::before)
73+
```

‎globalfy/globalfy-parsel.js‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import*asparselfrom"./parsel.mjs"
2+
3+
functionglobalfyNode(node,opts){
4+
const{types, exclude}=opts
5+
6+
// First base case: node must be globalfied.
7+
if(exclude ?!types.includes(node.type) :types.includes(node.type)){
8+
constarg=parsel.stringify(node)
9+
return{
10+
"name":"global",
11+
"argument":arg,
12+
"type":"pseudo-class",
13+
"content":`:global(${arg})`
14+
}
15+
}
16+
17+
// For composite nodes, recursively globalfy their children.
18+
switch(node.type){
19+
case"compound":
20+
case"list":
21+
console.log("list")
22+
letx={
23+
...node,
24+
list:node.list.map((child)=>globalfyNode(child,opts)),
25+
}
26+
console.log(JSON.stringify(x,null,2))
27+
returnx
28+
case"complex":
29+
console.log("complex")
30+
lety={
31+
...node,
32+
left:globalfyNode(node.left,opts),
33+
right:globalfyNode(node.right,opts),
34+
}
35+
console.log(JSON.stringify(y,null,2))
36+
returny
37+
}
38+
39+
// Second base case: node is not composite and doesn't need to be globalfied.
40+
returnnode
41+
}
42+
43+
functionglobalfySelector(selector,opts){
44+
console.log("gns")
45+
console.log(JSON.stringify(JSON.parse(JSON.stringify(parsel.parse(selector))),null,2))
46+
returnparsel.stringify(globalfyNode(JSON.parse(JSON.stringify(parsel.parse(selector))),opts))
47+
}
48+
49+
// const TYPES = [
50+
// "list",
51+
// "complex",
52+
// "compound",
53+
// "id",
54+
// "class",
55+
// "comma",
56+
// "combinator",
57+
// "pseudo-element",
58+
// "pseudo-class",
59+
// "universal",
60+
// "type",
61+
// ]
62+
63+
constAST=process.argv[2]=="ast"
64+
constSTRAT1={types:["list","complex","compound"],exclude:true}
65+
constSTRAT2={types:["class","id","type"]}
66+
67+
functiondebugAST(input){
68+
if(typeofinput==="string"){
69+
input=parsel.parse(input)
70+
}
71+
console.log(JSON.stringify(input,null,2))
72+
console.log(parsel.stringify(input))
73+
}
74+
75+
functiondebugGlobalfy(selector,opts){
76+
console.log(` input:${selector}`)
77+
console.log(` output:${globalfySelector(selector,opts)}`)
78+
console.log()
79+
}
80+
81+
if(AST){
82+
// debugAST(".foo")
83+
debugAST(".foo > .bar")
84+
// debugAST(":global(.foo, .foo.bar)")
85+
// debugAST(".first .second")
86+
// debugAST("test > .first, .second)")
87+
// debugAST("#foo > .bar + div.k1.k2 [id='baz']:hello(2):not(:where(#yolo))::before")
88+
}else{
89+
// debugGlobalfy(".foo", STRAT1)
90+
debugGlobalfy(".foo > .bar",STRAT1)
91+
// debugGlobalfy(".foo > .bar", STRAT1)
92+
}

‎globalfy/globalfy.js‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import{ast,createParser,render}from"css-selector-parser"
2+
3+
functionglobalfyNode(node,opts){
4+
if(!node||!node.type){
5+
returnnode
6+
}
7+
8+
const{types, exclude}=opts
9+
10+
// First base case: node must be globalfied.
11+
if(exclude ?!types.includes(node.type) :types.includes(node.type)){
12+
return{
13+
name:"global",
14+
type:"PseudoClass",
15+
argument:{
16+
type:"Selector",
17+
rules:[{type:"Rule",items:[node]}],
18+
},
19+
}
20+
}
21+
22+
// For composite nodes, recursively globalfy their children.
23+
switch(node.type){
24+
case"Selector":
25+
return{
26+
...node,
27+
rules:node.rules.map((rule)=>globalfyNode(rule,opts)),
28+
}
29+
case"Rule":
30+
return{
31+
...node,
32+
nestedRule:node.nestedRule ?globalfyNode(node.nestedRule,opts) :null,
33+
// items: node.items.map((child) => globalfyNode(child, opts)),
34+
items:[{
35+
name:"global",
36+
type:"PseudoClass",
37+
argument:{
38+
type:"Selector",
39+
rules:[{type:"Rule",items:node.items}],
40+
},
41+
}],
42+
}
43+
case"PseudoClass":
44+
case"PseudoElement":
45+
return{
46+
...node,
47+
argument:globalfyNode(node.argument,opts),
48+
}
49+
}
50+
51+
// Second base case: node is not composite and doesn't need to be globalfied.
52+
returnnode
53+
}
54+
55+
functionglobalfySelector(selector,opts){
56+
constparse=createParser({syntax:"progressive"});
57+
returnrender(ast.selector(globalfyNode(parse(selector),opts)))
58+
}
59+
60+
constAST=process.argv[2]=="ast"
61+
constSTRAT1={types:["Selector"],exclude:true}
62+
constSTRAT2={types:["Selector","Rule"],exclude:true}
63+
64+
functiondebugAST(selector){
65+
constparse=createParser({strict:false,syntax:"progressive"})
66+
constoutput=parse(selector)
67+
console.log(JSON.stringify(output,null,2))
68+
console.log(render(ast.selector(output)))
69+
}
70+
71+
functiondebugGlobalfy(selector){
72+
console.log(` input:${selector}`)
73+
console.log(` STRAT1:${globalfySelector(selector,STRAT1)}`)
74+
console.log(` STRAT2:${globalfySelector(selector,STRAT2)}`)
75+
console.log()
76+
}
77+
78+
if(AST){
79+
debugAST(".foo")
80+
debugAST(".foo > .bar")
81+
debugAST(".foo .bar")
82+
debugAST(".foo.bar")
83+
// debugAST(":global(.foo > .bar)")
84+
// debugAST(".first .second")
85+
// debugAST("test > .first, .second)")
86+
// debugAST("#foo > .bar + div.k1.k2 [id='baz']:hello(2):not(:where(#yolo))::before")
87+
}else{
88+
[
89+
".foo",
90+
"ul + p",
91+
"p > a",
92+
"p + p",
93+
"li a",
94+
"div ~ a",
95+
"div, a",
96+
".foo.bar",
97+
"[attr=\"with spaces\"]",
98+
"article :is(h1, h2)",
99+
"tr:nth-child(2n+1)",
100+
"p:nth-child(n+8):nth-child(-n+15)",
101+
"#foo > .bar + div.k1.k2 [id='baz']:not(:where(#yolo))::before"
102+
].forEach((selector)=>debugGlobalfy(selector))
103+
}

‎globalfy/package-lock.json‎

Lines changed: 92 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎globalfy/package.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type":"module",
3+
"dependencies": {
4+
"css-selector-parser":"^3.0.5",
5+
"parsel":"^0.3.0",
6+
"postcss":"^8.4.38"
7+
}
8+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp