|
| 1 | +import * as AttributeSelectorParser from './attribute-selector-parser' |
1 | 2 | import { |
2 | 3 | printModifier, |
3 | 4 | type Candidate, |
@@ -1088,139 +1089,6 @@ function isAttributeSelector(node: SelectorParser.SelectorAstNode): boolean { |
1088 | 1089 | return node.kind === 'selector' && value[0] === '[' && value[value.length - 1] === ']' |
1089 | 1090 | } |
1090 | 1091 |
|
1091 | | -function isAsciiWhitespace(char: string) { |
1092 | | - return char === ' ' || char === '\t' || char === '\n' || char === '\r' || char === '\f' |
1093 | | -} |
1094 | | - |
1095 | | -enum AttributePart { |
1096 | | - Start, |
1097 | | - Attribute, |
1098 | | - Value, |
1099 | | - Modifier, |
1100 | | - End, |
1101 | | -} |
1102 | | - |
1103 | | -function parseAttributeSelector(value: string) { |
1104 | | - let attribute = { |
1105 | | - key: '', |
1106 | | - operator: null as '=' | '~=' | '|=' | '^=' | '$=' | '*=' | null, |
1107 | | - quote: '', |
1108 | | - value: null as string | null, |
1109 | | - modifier: null as 'i' | 's' | null, |
1110 | | - } |
1111 | | - |
1112 | | - let state = AttributePart.Start |
1113 | | - outer: for (let i = 0; i < value.length; i++) { |
1114 | | - // Skip whitespace |
1115 | | - if (isAsciiWhitespace(value[i])) { |
1116 | | - if (attribute.quote === '' && state !== AttributePart.Value) { |
1117 | | - continue |
1118 | | - } |
1119 | | - } |
1120 | | - |
1121 | | - switch (state) { |
1122 | | - case AttributePart.Start: { |
1123 | | - if (value[i] === '[') { |
1124 | | - state = AttributePart.Attribute |
1125 | | - } else { |
1126 | | - return null |
1127 | | - } |
1128 | | - break |
1129 | | - } |
1130 | | - |
1131 | | - case AttributePart.Attribute: { |
1132 | | - switch (value[i]) { |
1133 | | - case ']': { |
1134 | | - return attribute |
1135 | | - } |
1136 | | - |
1137 | | - case '=': { |
1138 | | - attribute.operator = '=' |
1139 | | - state = AttributePart.Value |
1140 | | - continue outer |
1141 | | - } |
1142 | | - |
1143 | | - case '~': |
1144 | | - case '|': |
1145 | | - case '^': |
1146 | | - case '$': |
1147 | | - case '*': { |
1148 | | - if (value[i + 1] === '=') { |
1149 | | - attribute.operator = (value[i] + '=') as '=' | '~=' | '|=' | '^=' | '$=' | '*=' |
1150 | | - i++ |
1151 | | - state = AttributePart.Value |
1152 | | - continue outer |
1153 | | - } |
1154 | | - |
1155 | | - return null |
1156 | | - } |
1157 | | - } |
1158 | | - |
1159 | | - attribute.key += value[i] |
1160 | | - break |
1161 | | - } |
1162 | | - |
1163 | | - case AttributePart.Value: { |
1164 | | - // End of attribute selector |
1165 | | - if (value[i] === ']') { |
1166 | | - return attribute |
1167 | | - } |
1168 | | - |
1169 | | - // Quoted value |
1170 | | - else if (value[i] === "'" || value[i] === '"') { |
1171 | | - attribute.value ??= '' |
1172 | | - |
1173 | | - attribute.quote = value[i] |
1174 | | - |
1175 | | - for (let j = i + 1; j < value.length; j++) { |
1176 | | - if (value[j] === '\\' && j + 1 < value.length) { |
1177 | | - // Skip the escaped character |
1178 | | - j++ |
1179 | | - attribute.value += value[j] |
1180 | | - } else if (value[j] === attribute.quote) { |
1181 | | - i = j |
1182 | | - state = AttributePart.Modifier |
1183 | | - continue outer |
1184 | | - } else { |
1185 | | - attribute.value += value[j] |
1186 | | - } |
1187 | | - } |
1188 | | - } |
1189 | | - |
1190 | | - // Unquoted value |
1191 | | - else { |
1192 | | - if (isAsciiWhitespace(value[i])) { |
1193 | | - state = AttributePart.Modifier |
1194 | | - } else { |
1195 | | - attribute.value ??= '' |
1196 | | - attribute.value += value[i] |
1197 | | - } |
1198 | | - } |
1199 | | - break |
1200 | | - } |
1201 | | - |
1202 | | - case AttributePart.Modifier: { |
1203 | | - if (value[i] === 'i' || value[i] === 's') { |
1204 | | - attribute.modifier = value[i] as 'i' | 's' |
1205 | | - state = AttributePart.End |
1206 | | - } else if (value[i] == ']') { |
1207 | | - return attribute |
1208 | | - } |
1209 | | - break |
1210 | | - } |
1211 | | - |
1212 | | - case AttributePart.End: { |
1213 | | - if (value[i] === ']') { |
1214 | | - return attribute |
1215 | | - } |
1216 | | - break |
1217 | | - } |
1218 | | - } |
1219 | | - } |
1220 | | - |
1221 | | - return attribute |
1222 | | -} |
1223 | | - |
1224 | 1092 | function modernizeArbitraryValuesVariant( |
1225 | 1093 | designSystem: DesignSystem, |
1226 | 1094 | variant: Variant, |
@@ -1519,44 +1387,44 @@ function modernizeArbitraryValuesVariant( |
1519 | 1387 |
|
1520 | 1388 | // Expecting an attribute selector |
1521 | 1389 | else if (isAttributeSelector(target)) { |
1522 | | - let attribute = parseAttributeSelector(target.value) |
1523 | | - if (attribute === null) continue // Invalid attribute selector |
| 1390 | + let attributeSelector = AttributeSelectorParser.parse(target.value) |
| 1391 | + if (attributeSelector === null) continue // Invalid attribute selector |
1524 | 1392 |
|
1525 | 1393 | // Migrate `data-*` |
1526 | | - if (attribute.key.startsWith('data-')) { |
1527 | | - let name = attribute.key.slice(5) // Remove `data-` |
| 1394 | + if (attributeSelector.attribute.startsWith('data-')) { |
| 1395 | + let name = attributeSelector.attribute.slice(5) // Remove `data-` |
1528 | 1396 |
|
1529 | 1397 | replaceObject(variant, { |
1530 | 1398 | kind: 'functional', |
1531 | 1399 | root: 'data', |
1532 | 1400 | modifier: null, |
1533 | 1401 | value: |
1534 | | - attribute.value === null |
| 1402 | + attributeSelector.value === null |
1535 | 1403 | ? { kind: 'named', value: name } |
1536 | 1404 | : { |
1537 | 1405 | kind: 'arbitrary', |
1538 | | - value: `${name}${attribute.operator}${attribute.quote}${attribute.value}${attribute.quote}${attribute.modifier ? ` ${attribute.modifier}` : ''}`, |
| 1406 | + value: `${name}${attributeSelector.operator}${attributeSelector.quote ?? ''}${attributeSelector.value}${attributeSelector.quote ?? ''}${attributeSelector.sensitivity ? ` ${attributeSelector.sensitivity}` : ''}`, |
1539 | 1407 | }, |
1540 | 1408 | } satisfies Variant) |
1541 | 1409 | } |
1542 | 1410 |
|
1543 | 1411 | // Migrate `aria-*` |
1544 | | - else if (attribute.key.startsWith('aria-')) { |
1545 | | - let name = attribute.key.slice(5) // Remove `aria-` |
| 1412 | + else if (attributeSelector.attribute.startsWith('aria-')) { |
| 1413 | + let name = attributeSelector.attribute.slice(5) // Remove `aria-` |
1546 | 1414 | replaceObject(variant, { |
1547 | 1415 | kind: 'functional', |
1548 | 1416 | root: 'aria', |
1549 | 1417 | modifier: null, |
1550 | 1418 | value: |
1551 | | - attribute.value === null |
| 1419 | + attributeSelector.value === null |
1552 | 1420 | ? { kind: 'arbitrary', value: name } // aria-[foo] |
1553 | | - : attribute.operator === '=' && |
1554 | | - attribute.value === 'true' && |
1555 | | - attribute.modifier === null |
| 1421 | + : attributeSelector.operator === '=' && |
| 1422 | + attributeSelector.value === 'true' && |
| 1423 | + attributeSelector.sensitivity === null |
1556 | 1424 | ? { kind: 'named', value: name } // aria-[foo="true"] or aria-[foo='true'] or aria-[foo=true] |
1557 | 1425 | : { |
1558 | 1426 | kind: 'arbitrary', |
1559 | | - value: `${attribute.key}${attribute.operator}${attribute.quote}${attribute.value}${attribute.quote}${attribute.modifier ? ` ${attribute.modifier}` : ''}`, |
| 1427 | + value: `${attributeSelector.attribute}${attributeSelector.operator}${attributeSelector.quote ?? ''}${attributeSelector.value}${attributeSelector.quote ?? ''}${attributeSelector.sensitivity ? ` ${attributeSelector.sensitivity}` : ''}`, |
1560 | 1428 | }, // aria-[foo~="true"], aria-[foo|="true"], … |
1561 | 1429 | } satisfies Variant) |
1562 | 1430 | } |
|
0 commit comments