Skip to content

Commit 3085be7

Browse files
feat: support @scope at-rule (#70)
1 parent 82628c2 commit 3085be7

File tree

2 files changed

+279
-4
lines changed

2 files changed

+279
-4
lines changed

src/index.js

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -524,10 +524,12 @@ module.exports = (options = {}) => {
524524
} else if (localMatch) {
525525
atRule.params = localMatch[0];
526526
globalKeyframes = false;
527-
} else if (!globalMode) {
528-
if (atRule.params && !localAliasMap.has(atRule.params)) {
529-
atRule.params = ":local(" + atRule.params + ")";
530-
}
527+
} else if (
528+
atRule.params &&
529+
!globalMode &&
530+
!localAliasMap.has(atRule.params)
531+
) {
532+
atRule.params = ":local(" + atRule.params + ")";
531533
}
532534

533535
atRule.walkDecls((declaration) => {
@@ -537,6 +539,42 @@ module.exports = (options = {}) => {
537539
global: globalKeyframes,
538540
});
539541
});
542+
} else if (/scope$/i.test(atRule.name)) {
543+
atRule.params = atRule.params
544+
.split("to")
545+
.map((item) => {
546+
const selector = item.trim().slice(1, -1).trim();
547+
const context = localizeNode(
548+
selector,
549+
options.mode,
550+
localAliasMap
551+
);
552+
553+
context.options = options;
554+
context.localAliasMap = localAliasMap;
555+
556+
if (pureMode && context.hasPureGlobals) {
557+
throw atRule.error(
558+
'Selector in at-rule"' +
559+
selector +
560+
'" is not pure ' +
561+
"(pure selectors must contain at least one local class or id)"
562+
);
563+
}
564+
565+
return `(${context.selector})`;
566+
})
567+
.join(" to ");
568+
569+
atRule.nodes.forEach((declaration) => {
570+
if (declaration.type === "decl") {
571+
localizeDeclaration(declaration, {
572+
localAliasMap,
573+
options: options,
574+
global: globalMode,
575+
});
576+
}
577+
});
540578
} else if (atRule.nodes) {
541579
atRule.nodes.forEach((declaration) => {
542580
if (declaration.type === "decl") {

test/index.test.js

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,243 @@ const tests = [
10221022
input: ".foo { animation: 1s -500.0ms -a_value; }",
10231023
expected: ":local(.foo) { animation: 1s -500.0ms :local(-a_value); }",
10241024
},
1025+
{
1026+
name: "@scope at-rule",
1027+
input: `
1028+
.article-header {
1029+
color: red;
1030+
}
1031+
1032+
.article-body {
1033+
color: blue;
1034+
}
1035+
1036+
@scope (.article-body) to (.article-header) {
1037+
.article-body {
1038+
border: 5px solid black;
1039+
background-color: goldenrod;
1040+
}
1041+
}
1042+
1043+
@scope(.article-body)to(.article-header){
1044+
.article-footer {
1045+
border: 5px solid black;
1046+
}
1047+
}
1048+
1049+
@scope ( .article-body ) {
1050+
img {
1051+
border: 5px solid black;
1052+
background-color: goldenrod;
1053+
}
1054+
}
1055+
`,
1056+
expected: `
1057+
:local(.article-header) {
1058+
color: red;
1059+
}
1060+
1061+
:local(.article-body) {
1062+
color: blue;
1063+
}
1064+
1065+
@scope (:local(.article-body)) to (:local(.article-header)) {
1066+
:local(.article-body) {
1067+
border: 5px solid black;
1068+
background-color: goldenrod;
1069+
}
1070+
}
1071+
1072+
@scope(:local(.article-body)) to (:local(.article-header)){
1073+
:local(.article-footer) {
1074+
border: 5px solid black;
1075+
}
1076+
}
1077+
1078+
@scope (:local(.article-body)) {
1079+
img {
1080+
border: 5px solid black;
1081+
background-color: goldenrod;
1082+
}
1083+
}
1084+
`,
1085+
},
1086+
{
1087+
name: "@scope at-rule #1",
1088+
input: `
1089+
@scope (.article-body) to (figure) {
1090+
.article-footer {
1091+
border: 5px solid black;
1092+
}
1093+
}
1094+
`,
1095+
expected: `
1096+
@scope (:local(.article-body)) to (figure) {
1097+
:local(.article-footer) {
1098+
border: 5px solid black;
1099+
}
1100+
}
1101+
`,
1102+
},
1103+
{
1104+
name: "@scope at-rule #2",
1105+
input: `
1106+
@scope (:local(.article-body)) to (:global(.class)) {
1107+
.article-footer {
1108+
border: 5px solid black;
1109+
}
1110+
:local(.class-1) {
1111+
color: red;
1112+
}
1113+
:global(.class-2) {
1114+
color: blue;
1115+
}
1116+
}
1117+
`,
1118+
expected: `
1119+
@scope (:local(.article-body)) to (.class) {
1120+
:local(.article-footer) {
1121+
border: 5px solid black;
1122+
}
1123+
:local(.class-1) {
1124+
color: red;
1125+
}
1126+
.class-2 {
1127+
color: blue;
1128+
}
1129+
}
1130+
`,
1131+
},
1132+
{
1133+
name: "@scope at-rule #3",
1134+
options: { mode: "global" },
1135+
input: `
1136+
@scope (:local(.article-header)) to (:global(.class)) {
1137+
.article-footer {
1138+
border: 5px solid black;
1139+
}
1140+
:local(.class-1) {
1141+
color: red;
1142+
}
1143+
:global(.class-2) {
1144+
color: blue;
1145+
}
1146+
}
1147+
`,
1148+
expected: `
1149+
@scope (:local(.article-header)) to (.class) {
1150+
.article-footer {
1151+
border: 5px solid black;
1152+
}
1153+
:local(.class-1) {
1154+
color: red;
1155+
}
1156+
.class-2 {
1157+
color: blue;
1158+
}
1159+
}
1160+
`,
1161+
},
1162+
{
1163+
name: "@scope at-rule #4",
1164+
options: { mode: "pure" },
1165+
input: `
1166+
@scope (.article-header) to (.class) {
1167+
.article-footer {
1168+
border: 5px solid black;
1169+
}
1170+
.class-1 {
1171+
color: red;
1172+
}
1173+
.class-2 {
1174+
color: blue;
1175+
}
1176+
}
1177+
`,
1178+
expected: `
1179+
@scope (:local(.article-header)) to (:local(.class)) {
1180+
:local(.article-footer) {
1181+
border: 5px solid black;
1182+
}
1183+
:local(.class-1) {
1184+
color: red;
1185+
}
1186+
:local(.class-2) {
1187+
color: blue;
1188+
}
1189+
}
1190+
`,
1191+
},
1192+
{
1193+
name: "@scope at-rule #5",
1194+
input: `
1195+
@scope (.article-header) to (.class) {
1196+
.article-footer {
1197+
src: url("./font.woff");
1198+
}
1199+
}
1200+
`,
1201+
options: {
1202+
rewriteUrl: function (global, url) {
1203+
const mode = global ? "global" : "local";
1204+
return "(" + mode + ")" + url + '"' + mode + '"';
1205+
},
1206+
},
1207+
expected: `
1208+
@scope (:local(.article-header)) to (:local(.class)) {
1209+
:local(.article-footer) {
1210+
src: url("(local)./font.woff\\"local\\"");
1211+
}
1212+
}
1213+
`,
1214+
},
1215+
{
1216+
name: "@scope at-rule #6",
1217+
input: `
1218+
.foo {
1219+
@scope (.article-header) to (.class) {
1220+
:scope {
1221+
background: blue;
1222+
}
1223+
1224+
.bar {
1225+
color: red;
1226+
}
1227+
}
1228+
}
1229+
`,
1230+
expected: `
1231+
:local(.foo) {
1232+
@scope (:local(.article-header)) to (:local(.class)) {
1233+
:scope {
1234+
background: blue;
1235+
}
1236+
1237+
:local(.bar) {
1238+
color: red;
1239+
}
1240+
}
1241+
}
1242+
`,
1243+
},
1244+
{
1245+
name: "@scope at-rule #7",
1246+
options: { mode: "pure" },
1247+
input: `
1248+
@scope (:global(.article-header).foo) to (:global(.class).bar) {
1249+
.bar {
1250+
color: red;
1251+
}
1252+
}
1253+
`,
1254+
expected: `
1255+
@scope (.article-header:local(.foo)) to (.class:local(.bar)) {
1256+
:local(.bar) {
1257+
color: red;
1258+
}
1259+
}
1260+
`,
1261+
},
10251262
];
10261263

10271264
function process(css, options) {

0 commit comments

Comments
 (0)