Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support scoped prefixing for :host() and :host-context() pseudo classes #96

Merged
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
97 changes: 73 additions & 24 deletions src/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ class CssPrefixer {
})
}

shouldSkipPseudoClass(name) {
return {
"host-context": true,
}[name];
}

process(cssString) {
let ast = this.parse(cssString);

Expand All @@ -42,27 +36,82 @@ class CssPrefixer {
visit: "Selector",
enter: (node, item, list) => {
let first = node.children.first;
if(first.type === "PseudoClassSelector" && this.shouldSkipPseudoClass(first.name)) {
// Skip processing some pseudo classes
} else {
if(skipLevel > 0 || first.type === "TypeSelector" && (first.name === "from" || first.name === "to")) {
// do nothing
} else {
if(first.type === "PseudoClassSelector" && first.name === "host") {
// replace :host with prefix class
node.children.shift();
} else {
node.children.prepend(list.createItem({
type: "Combinator",
name: " "
}));

const shouldSkip =
skipLevel > 0 ||
(first.type === "TypeSelector" && first.name === "from") ||
first.name === "to";

if (shouldSkip) {
// do nothing
} else if (
first.type === "PseudoClassSelector" &&
(first.name === "host" || first.name === "host-context")
) {
// Transform :host and :host-context pseudo classes to
// use the prefix class
node.children.shift();

const pseudoClassParamChildren = first.children ? first.children : null;

if (first.name === "host") {
// Replace :host with the prefix class
if (pseudoClassParamChildren) {
// Any param children of a :host() functional pseudo class should be moved up to
// be directly after the prefix class
// ie, :host(.foo) -> .prefix.foo
node.children.prependList(
// :host can only accept one param, so we can safely use the first child
pseudoClassParamChildren.first.children
);
}
node.children.prepend(
list.createItem({
type: "ClassSelector",
name: this.prefix,
})
);
} else if (first.name === "host-context") {
// Replace :host-context with the prefix class and
// place any param children appropriately before the prefix class
node.children.prepend(
list.createItem({
type: "ClassSelector",
name: this.prefix,
})
);

node.children.prepend(list.createItem({
type: "ClassSelector",
name: this.prefix
}));
if (pseudoClassParamChildren) {
// Any param children of a :host-context() functional pseudo class should be moved up to
// be parents before the prefix class
// ie, :host-context(.foo) div -> .foo .prefix div
node.children.prepend(
list.createItem({
type: "Combinator",
name: " ",
})
);
node.children.prependList(
// :host-context can only accept one param, so we can safely use the first child
pseudoClassParamChildren.first.children
);
}
}
} else {
// Prepand the prefix class in front of all selectors
// which don't include :host or :host-context
node.children.prepend(
list.createItem({
type: "Combinator",
name: " ",
})
);
node.children.prepend(
list.createItem({
type: "ClassSelector",
name: this.prefix,
})
);
}

node.children.forEach((node, item, list) => {
Expand Down
24 changes: 19 additions & 5 deletions test/cssTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,25 @@ test("Pseudo classes", t => {
t.is(c.process(`:host:not(p) div {}`), `.my-prefix:not(p) div{}`);
t.is(c.process(`:host.footer div {}`), `.my-prefix.footer div{}`); // same as :host(.footer)

// TODO :host(.footer) should be `.my-prefix.footer` but we can use `:host.footer` for now
t.is(c.process(`:host(.footer) div {}`), `.my-prefix div{}`);

// TODO host-context(html body) should be `html body .my-prefix`
t.is(c.process(`:host-context(html) div {}`), `:host-context(html) div{}`);
t.is(c.process(`:host(.footer) div {}`), `.my-prefix.footer div{}`);
t.is(
c.process(`:host(.footer:not([lang])) .link {}`),
`.my-prefix.footer:not([lang]) .link{}`
);
t.is(
c.process(`:host(:is(div, span)) {}`),
`.my-prefix:is(div,span){}`
);

t.is(c.process(`:host-context(html) div {}`), `html .my-prefix div{}`);
t.is(
c.process(`:host-context(html body.dark-theme) div {}`),
`html body.dark-theme .my-prefix div{}`
);
t.is(
c.process(`:host-context(html body:is(.dark-theme, .light-theme)) div {}`),
`html body:is(.dark-theme,.light-theme) .my-prefix div{}`
);
});

test("Pseudo elements", t => {
Expand Down