@tamagui/static 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extractor/createExtractor.cjs +136 -9
- package/dist/extractor/extractToClassNames.cjs +48 -2
- package/package.json +15 -15
- package/src/extractor/createExtractor.ts +254 -10
- package/src/extractor/extractToClassNames.ts +104 -5
- package/types/extractor/createExtractor.d.ts.map +1 -1
- package/types/extractor/extractToClassNames.d.ts.map +1 -1
|
@@ -71,6 +71,39 @@ let hasLoggedBaseInfo = false;
|
|
|
71
71
|
function isFullyDisabled(props) {
|
|
72
72
|
return props.disableExtraction && props.disableDebugAttr;
|
|
73
73
|
}
|
|
74
|
+
function styleValueHasToken(v) {
|
|
75
|
+
if (typeof v === "string") return v.charCodeAt(0) === 36;
|
|
76
|
+
if (Array.isArray(v)) return v.some(styleValueHasToken);
|
|
77
|
+
if (v && typeof v === "object") return Object.values(v).some(styleValueHasToken);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
function hasUntilMeasuredAncestor(path, groupName) {
|
|
81
|
+
let current = path.parentPath;
|
|
82
|
+
while (current) {
|
|
83
|
+
if (current.isJSXElement()) {
|
|
84
|
+
const opening = current.node.openingElement;
|
|
85
|
+
let foundGroup = false;
|
|
86
|
+
let foundUntilMeasured = false;
|
|
87
|
+
for (const attr of opening.attributes) {
|
|
88
|
+
if (!t.isJSXAttribute(attr)) continue;
|
|
89
|
+
if (!t.isJSXIdentifier(attr.name)) continue;
|
|
90
|
+
const aName = attr.name.name;
|
|
91
|
+
if (aName === "group") {
|
|
92
|
+
if (t.isStringLiteral(attr.value) && attr.value.value === groupName) {
|
|
93
|
+
foundGroup = true;
|
|
94
|
+
} else if (t.isJSXExpressionContainer(attr.value) && t.isStringLiteral(attr.value.expression) && attr.value.expression.value === groupName) {
|
|
95
|
+
foundGroup = true;
|
|
96
|
+
}
|
|
97
|
+
} else if (aName === "untilMeasured") {
|
|
98
|
+
foundUntilMeasured = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (foundGroup && foundUntilMeasured) return true;
|
|
102
|
+
}
|
|
103
|
+
current = current.parentPath;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
74
107
|
function createExtractor({
|
|
75
108
|
logger = console,
|
|
76
109
|
platform = "web"
|
|
@@ -293,6 +326,13 @@ function createExtractor({
|
|
|
293
326
|
// don't disable variants or else you lose many things flattening
|
|
294
327
|
staticConfig.variants?.[name] || projectInfo?.tamaguiConfig?.shorthands[name]);
|
|
295
328
|
}
|
|
329
|
+
function getGroupPseudo(name) {
|
|
330
|
+
const [_, groupName, a, b, c] = name.split("-");
|
|
331
|
+
if (!groupName) return;
|
|
332
|
+
const m2 = a && b ? `${a}-${b}` : "";
|
|
333
|
+
const media = m2 && mediaQueryConfig[m2] && m2 || a && mediaQueryConfig[a] && a;
|
|
334
|
+
return media ? media === m2 ? c : b ? `${b}${c ? `-${c}` : ""}` : void 0 : a ? `${a}${b ? `-${b}` : ""}${c ? `-${c}` : ""}` : void 0;
|
|
335
|
+
}
|
|
296
336
|
const isTargetingHTML = platform2 === "web";
|
|
297
337
|
const ogDebug = shouldPrintDebug;
|
|
298
338
|
const tm = (0, import_timer.timer)();
|
|
@@ -806,6 +846,10 @@ function createExtractor({
|
|
|
806
846
|
}
|
|
807
847
|
return [attribute.value, path.get("value")];
|
|
808
848
|
})();
|
|
849
|
+
if (deoptProps.has(name) || platform2 === "native" && name[0] === "$" && (name.startsWith("$theme-") || name.startsWith("$group-") && getGroupPseudo(name) !== "hover")) {
|
|
850
|
+
inlined.set(name, true);
|
|
851
|
+
return attr;
|
|
852
|
+
}
|
|
809
853
|
const remove = () => {
|
|
810
854
|
Array.isArray(valuePath) ? valuePath.map(p => p.remove()) : valuePath.remove();
|
|
811
855
|
};
|
|
@@ -843,7 +887,17 @@ function createExtractor({
|
|
|
843
887
|
inlined.set("theme", attr.value);
|
|
844
888
|
return attr;
|
|
845
889
|
}
|
|
890
|
+
if (isTargetingHTML && name === "group" && t.isStringLiteral(value)) {
|
|
891
|
+
return [];
|
|
892
|
+
}
|
|
846
893
|
const styleValue = attemptEvalSafe(value);
|
|
894
|
+
if (name[0] === "$" && (name.startsWith("$theme-") || name.startsWith("$group-")) && styleValue && typeof styleValue === "object" && Object.keys(styleValue).some(k => k[0] === "$")) {
|
|
895
|
+
if (shouldPrintDebug) {
|
|
896
|
+
logger.info(` ! nested media-like key inside ${name}, deopt to runtime`);
|
|
897
|
+
}
|
|
898
|
+
inlined.set(name, true);
|
|
899
|
+
return attr;
|
|
900
|
+
}
|
|
847
901
|
if (!variants[name] && !isValidStyleKey(name, staticConfig)) {
|
|
848
902
|
let out = null;
|
|
849
903
|
propMapper(name, styleValue, propMapperStyleState, false, (key, val) => {
|
|
@@ -906,12 +960,15 @@ function createExtractor({
|
|
|
906
960
|
}
|
|
907
961
|
if (isValidStyleKey(name, staticConfig)) {
|
|
908
962
|
if (name[0] === "$") {
|
|
909
|
-
if (name.startsWith("$
|
|
910
|
-
|
|
911
|
-
|
|
963
|
+
if (name.startsWith("$group-")) {
|
|
964
|
+
const groupName = name.slice("$group-".length).split("-")[0];
|
|
965
|
+
if (groupName && hasUntilMeasuredAncestor(path, groupName)) {
|
|
966
|
+
if (shouldPrintDebug) {
|
|
967
|
+
logger.info(` ! group="${groupName}" ancestor has untilMeasured, not flattening: ${name}`);
|
|
968
|
+
}
|
|
969
|
+
inlined.set(name, true);
|
|
970
|
+
return attr;
|
|
912
971
|
}
|
|
913
|
-
inlined.set(name, true);
|
|
914
|
-
return attr;
|
|
915
972
|
}
|
|
916
973
|
if (name.startsWith("$platform-")) {
|
|
917
974
|
const platformName = name.slice(10);
|
|
@@ -1211,7 +1268,14 @@ function createExtractor({
|
|
|
1211
1268
|
...(restProps.inlineProps || []), ...(staticConfig.inlineProps || [])]);
|
|
1212
1269
|
const deoptProps = /* @__PURE__ */new Set([
|
|
1213
1270
|
// always de-opt animation these
|
|
1214
|
-
"animation", "animateOnly", "animatePresence", "disableOptimization", ...(!isTargetingHTML ? [
|
|
1271
|
+
"animation", "animateOnly", "animatePresence", "disableOptimization", ...(!isTargetingHTML ? [
|
|
1272
|
+
// native has no css pseudo selectors; these are runtime-only on
|
|
1273
|
+
// rn (event listeners + style merge in createComponent), and if
|
|
1274
|
+
// we let them flatten into the stylesheet they get written
|
|
1275
|
+
// under a literal key that rn treats as an unknown style prop.
|
|
1276
|
+
// hover is intentionally excluded: it is no-op on native and
|
|
1277
|
+
// should be dropped rather than preserved as runtime work.
|
|
1278
|
+
"pressStyle", "focusStyle", "focusVisibleStyle", "focusWithinStyle", "disabledStyle"] : []),
|
|
1215
1279
|
// when using a non-CSS driver, de-opt on enterStyle/exitStyle
|
|
1216
1280
|
...(tamaguiConfig?.animations.isReactNative ? ["enterStyle", "exitStyle"] : [])]);
|
|
1217
1281
|
const inlineWhenUnflattened = new Set(staticConfig.inlineWhenUnflattened || []);
|
|
@@ -1328,7 +1392,31 @@ function createExtractor({
|
|
|
1328
1392
|
neverFlatten: staticConfig.neverFlatten
|
|
1329
1393
|
})}`);
|
|
1330
1394
|
}
|
|
1331
|
-
|
|
1395
|
+
let partial = null;
|
|
1396
|
+
if (platform2 === "native" && !staticConfig.isHOC && !staticConfig.isStyledHOC && !ogAttributes.some(a => t.isJSXSpreadAttribute(a) || t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === "style")) {
|
|
1397
|
+
const staticStyle = {};
|
|
1398
|
+
const consumed = /* @__PURE__ */new Set();
|
|
1399
|
+
for (const a of attrs) {
|
|
1400
|
+
if (a.type !== "style" || !a.name || !a.attr) continue;
|
|
1401
|
+
if (pseudoDescriptors[a.name]) continue;
|
|
1402
|
+
if (!t.isJSXAttribute(a.attr) || !t.isJSXIdentifier(a.attr.name)) continue;
|
|
1403
|
+
if (styleValueHasToken(a.value)) continue;
|
|
1404
|
+
Object.assign(staticStyle, a.value);
|
|
1405
|
+
consumed.add(a.attr.name.name);
|
|
1406
|
+
}
|
|
1407
|
+
if (consumed.size >= 2) {
|
|
1408
|
+
const kept = ogAttributes.filter(a => {
|
|
1409
|
+
if (!t.isJSXAttribute(a) || !t.isJSXIdentifier(a.name)) return true;
|
|
1410
|
+
const n = a.name.name;
|
|
1411
|
+
if (consumed.has(n)) return false;
|
|
1412
|
+
if (n === "hoverStyle") return false;
|
|
1413
|
+
if (n.startsWith("$group-") && getGroupPseudo(n) === "hover") return false;
|
|
1414
|
+
return true;
|
|
1415
|
+
});
|
|
1416
|
+
partial = [t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer((0, import_literalToAst.literalToAst)(staticStyle))), ...kept];
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
node.attributes = partial ?? ogAttributes;
|
|
1332
1420
|
return;
|
|
1333
1421
|
}
|
|
1334
1422
|
let skipMap = false;
|
|
@@ -1566,7 +1654,25 @@ function createExtractor({
|
|
|
1566
1654
|
const before = process.env.IS_STATIC;
|
|
1567
1655
|
process.env.IS_STATIC = "is_static";
|
|
1568
1656
|
try {
|
|
1569
|
-
|
|
1657
|
+
let extractedMediaLikeProps = null;
|
|
1658
|
+
let propsForSplit = props;
|
|
1659
|
+
for (const k in props) {
|
|
1660
|
+
if (k[0] === "$" && (k.startsWith("$group-") || k.startsWith("$theme-"))) {
|
|
1661
|
+
if (propsForSplit === props) {
|
|
1662
|
+
propsForSplit = {
|
|
1663
|
+
...props
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
if (platform2 === "native" && k.startsWith("$group-") && getGroupPseudo(k) === "hover") {
|
|
1667
|
+
delete propsForSplit[k];
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
extractedMediaLikeProps ||= {};
|
|
1671
|
+
extractedMediaLikeProps[k] = propsForSplit[k];
|
|
1672
|
+
delete propsForSplit[k];
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
const out = getSplitStyles(propsForSplit, staticConfig, defaultTheme, "", componentState, {
|
|
1570
1676
|
...styleProps,
|
|
1571
1677
|
noClass: true,
|
|
1572
1678
|
fallbackProps: completeProps,
|
|
@@ -1574,15 +1680,36 @@ function createExtractor({
|
|
|
1574
1680
|
resolveValues: "except-theme"
|
|
1575
1681
|
})
|
|
1576
1682
|
}, void 0, void 0, void 0, void 0, false, debugPropValue || shouldPrintDebug);
|
|
1683
|
+
if (extractedMediaLikeProps && platform2 !== "native") {
|
|
1684
|
+
for (const k in extractedMediaLikeProps) {
|
|
1685
|
+
const block = extractedMediaLikeProps[k];
|
|
1686
|
+
if (!block || typeof block !== "object") continue;
|
|
1687
|
+
const blockOut = getSplitStyles(block, staticConfig, defaultTheme, "", componentState, {
|
|
1688
|
+
...styleProps,
|
|
1689
|
+
noClass: true,
|
|
1690
|
+
fallbackProps: completeProps
|
|
1691
|
+
}, void 0, void 0, void 0, void 0, false, debugPropValue || shouldPrintDebug);
|
|
1692
|
+
if (blockOut) {
|
|
1693
|
+
extractedMediaLikeProps[k] = {
|
|
1694
|
+
...blockOut.style,
|
|
1695
|
+
...blockOut.pseudos
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1577
1700
|
let outProps = {
|
|
1578
1701
|
...(includeProps ? out.viewProps : {}),
|
|
1579
1702
|
...out.style,
|
|
1580
|
-
...out.pseudos
|
|
1703
|
+
...out.pseudos,
|
|
1704
|
+
...extractedMediaLikeProps
|
|
1581
1705
|
};
|
|
1582
1706
|
for (const key in outProps) {
|
|
1583
1707
|
if (deoptProps.has(key)) {
|
|
1584
1708
|
shouldFlatten = false;
|
|
1585
1709
|
}
|
|
1710
|
+
if (platform2 === "native" && key[0] === "$" && (key.startsWith("$theme-") || key.startsWith("$group-") && getGroupPseudo(key) !== "hover")) {
|
|
1711
|
+
shouldFlatten = false;
|
|
1712
|
+
}
|
|
1586
1713
|
}
|
|
1587
1714
|
if (shouldPrintDebug) {
|
|
1588
1715
|
logger.info(`(${debugName})`);
|
|
@@ -136,6 +136,39 @@ async function extractToClassNames({
|
|
|
136
136
|
let baseFontFamily = "";
|
|
137
137
|
let mediaStylesSeen = 1;
|
|
138
138
|
const comment = util.format("/* %s:%s (%s) */", filePath, lineNumbers, originalNodeName);
|
|
139
|
+
let staticGroupName = null;
|
|
140
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
141
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue;
|
|
142
|
+
if (attr.name.name !== "group") continue;
|
|
143
|
+
if (t.isStringLiteral(attr.value)) {
|
|
144
|
+
staticGroupName = attr.value.value;
|
|
145
|
+
} else if (t.isJSXExpressionContainer(attr.value) && t.isStringLiteral(attr.value.expression)) {
|
|
146
|
+
staticGroupName = attr.value.expression.value;
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (staticGroupName) {
|
|
151
|
+
const containerIdentifier = `t_group_${staticGroupName}`;
|
|
152
|
+
const containerType = tamaguiConfig.settings?.webContainerType || "inline-size";
|
|
153
|
+
const containerSelector = `.${containerIdentifier}`;
|
|
154
|
+
if (!cssMap.has(containerSelector)) {
|
|
155
|
+
cssMap.set(containerSelector, {
|
|
156
|
+
css: `${containerSelector} { container-name: ${staticGroupName}; container-type: ${containerType}; }`,
|
|
157
|
+
commentTexts: [comment]
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
cssMap.get(containerSelector).commentTexts.push(comment);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
let elementIsAnimated = false;
|
|
164
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
165
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue;
|
|
166
|
+
const n = attr.name.name;
|
|
167
|
+
if (n === "animation" || n === "animateOnly" || n === "animatePresence" || n === "animatedBy" || n === "enterStyle" || n === "exitStyle") {
|
|
168
|
+
elementIsAnimated = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
139
172
|
function addStyle(style) {
|
|
140
173
|
const identifier = style[import_web.StyleObjectIdentifier];
|
|
141
174
|
const rules = style[import_web.StyleObjectRules];
|
|
@@ -158,12 +191,22 @@ async function extractToClassNames({
|
|
|
158
191
|
const property = style2[0];
|
|
159
192
|
const mediaName = property.slice(1);
|
|
160
193
|
if (mediaName.startsWith("group-")) {
|
|
161
|
-
|
|
194
|
+
if (elementIsAnimated) {
|
|
195
|
+
throw new import_errors.BailOptimizationError();
|
|
196
|
+
}
|
|
197
|
+
const mediaStyle = createMediaStyle(style2, mediaName, extractor.getTamagui().media, "group", false, mediaStylesSeen);
|
|
198
|
+
const identifier2 = addStyle(mediaStyle);
|
|
199
|
+
classNames.push(identifier2);
|
|
200
|
+
continue;
|
|
162
201
|
}
|
|
163
202
|
const mediaTypeMatch = mediaName.match(/^(theme|platform)-/);
|
|
164
203
|
if (mediaTypeMatch) {
|
|
165
204
|
const mediaType = mediaTypeMatch[1];
|
|
166
|
-
|
|
205
|
+
if (mediaType === "theme" && elementIsAnimated) {
|
|
206
|
+
throw new import_errors.BailOptimizationError();
|
|
207
|
+
}
|
|
208
|
+
const innerKey = mediaType === "theme" ? mediaName.slice("theme-".length) : mediaName;
|
|
209
|
+
const mediaStyle = createMediaStyle(style2, innerKey, extractor.getTamagui().media, mediaType, false, mediaStylesSeen);
|
|
167
210
|
const identifier2 = addStyle(mediaStyle);
|
|
168
211
|
classNames.push(identifier2);
|
|
169
212
|
continue;
|
|
@@ -249,6 +292,9 @@ async function extractToClassNames({
|
|
|
249
292
|
if (baseFontFamily) {
|
|
250
293
|
baseClassNameStr = `font_${baseFontFamily}${baseClassNameStr ? ` ${baseClassNameStr}` : ""}`;
|
|
251
294
|
}
|
|
295
|
+
if (staticGroupName) {
|
|
296
|
+
baseClassNameStr = `t_group_${staticGroupName}${baseClassNameStr ? ` ${baseClassNameStr}` : ""}`;
|
|
297
|
+
}
|
|
252
298
|
const baseTypeClass = staticConfig.isText ? "is_Text" : "is_View";
|
|
253
299
|
baseClassNameStr = `${baseTypeClass}${baseClassNameStr ? ` ${baseClassNameStr}` : ""}`;
|
|
254
300
|
const componentNameFinal = staticConfig.componentName;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamagui/static",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -50,19 +50,19 @@
|
|
|
50
50
|
"@babel/template": "^7.25.0",
|
|
51
51
|
"@babel/traverse": "^7.25.4",
|
|
52
52
|
"@babel/types": "^7.25.4",
|
|
53
|
-
"@tamagui/cli-color": "2.4.
|
|
54
|
-
"@tamagui/config-default": "2.4.
|
|
55
|
-
"@tamagui/core": "2.4.
|
|
56
|
-
"@tamagui/fake-react-native": "2.4.
|
|
57
|
-
"@tamagui/generate-themes": "2.4.
|
|
58
|
-
"@tamagui/helpers": "2.4.
|
|
59
|
-
"@tamagui/helpers-node": "2.4.
|
|
60
|
-
"@tamagui/proxy-worm": "2.4.
|
|
61
|
-
"@tamagui/react-native-web-internals": "2.4.
|
|
62
|
-
"@tamagui/react-native-web-lite": "2.4.
|
|
63
|
-
"@tamagui/shorthands": "2.4.
|
|
64
|
-
"@tamagui/types": "2.4.
|
|
65
|
-
"@tamagui/web": "2.4.
|
|
53
|
+
"@tamagui/cli-color": "2.4.2",
|
|
54
|
+
"@tamagui/config-default": "2.4.2",
|
|
55
|
+
"@tamagui/core": "2.4.2",
|
|
56
|
+
"@tamagui/fake-react-native": "2.4.2",
|
|
57
|
+
"@tamagui/generate-themes": "2.4.2",
|
|
58
|
+
"@tamagui/helpers": "2.4.2",
|
|
59
|
+
"@tamagui/helpers-node": "2.4.2",
|
|
60
|
+
"@tamagui/proxy-worm": "2.4.2",
|
|
61
|
+
"@tamagui/react-native-web-internals": "2.4.2",
|
|
62
|
+
"@tamagui/react-native-web-lite": "2.4.2",
|
|
63
|
+
"@tamagui/shorthands": "2.4.2",
|
|
64
|
+
"@tamagui/types": "2.4.2",
|
|
65
|
+
"@tamagui/web": "2.4.2",
|
|
66
66
|
"babel-literal-to-ast": "^2.1.0",
|
|
67
67
|
"browserslist": "^4.28.1",
|
|
68
68
|
"check-dependency-version-consistency": "^4.1.0",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@babel/plugin-syntax-typescript": "^7.25.4",
|
|
82
|
-
"@tamagui/build": "2.4.
|
|
82
|
+
"@tamagui/build": "2.4.2",
|
|
83
83
|
"@types/babel__core": "^7.20.5",
|
|
84
84
|
"@types/find-root": "^1.1.2",
|
|
85
85
|
"@types/node": "^22.1.0",
|
|
@@ -76,6 +76,53 @@ function isFullyDisabled(props: TamaguiOptions) {
|
|
|
76
76
|
return props.disableExtraction && props.disableDebugAttr
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// a compile-evaluated style value carries a theme token if any leaf string is
|
|
80
|
+
// "$"-prefixed (e.g. "$gray2"). such styles must NOT be flattened into a static
|
|
81
|
+
// `style` object — the token is resolved at runtime so theme switching keeps working.
|
|
82
|
+
function styleValueHasToken(v: any): boolean {
|
|
83
|
+
if (typeof v === 'string') return v.charCodeAt(0) === 36 // '$'
|
|
84
|
+
if (Array.isArray(v)) return v.some(styleValueHasToken)
|
|
85
|
+
if (v && typeof v === 'object') return Object.values(v).some(styleValueHasToken)
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Walk up JSX ancestors looking for one that declares `group="<groupName>"` together
|
|
90
|
+
// with the `untilMeasured` prop. Used to deopt children whose styles depend on a
|
|
91
|
+
// parent that the runtime measures before emitting child styles (can't be modeled
|
|
92
|
+
// in static CSS).
|
|
93
|
+
function hasUntilMeasuredAncestor(path: NodePath<any>, groupName: string): boolean {
|
|
94
|
+
let current: NodePath<any> | null = path.parentPath
|
|
95
|
+
while (current) {
|
|
96
|
+
if (current.isJSXElement()) {
|
|
97
|
+
const opening = current.node.openingElement
|
|
98
|
+
let foundGroup = false
|
|
99
|
+
let foundUntilMeasured = false
|
|
100
|
+
for (const attr of opening.attributes) {
|
|
101
|
+
if (!t.isJSXAttribute(attr)) continue
|
|
102
|
+
if (!t.isJSXIdentifier(attr.name)) continue
|
|
103
|
+
const aName = attr.name.name
|
|
104
|
+
if (aName === 'group') {
|
|
105
|
+
// only literal string equality counts — dynamic group= is left to runtime
|
|
106
|
+
if (t.isStringLiteral(attr.value) && attr.value.value === groupName) {
|
|
107
|
+
foundGroup = true
|
|
108
|
+
} else if (
|
|
109
|
+
t.isJSXExpressionContainer(attr.value) &&
|
|
110
|
+
t.isStringLiteral(attr.value.expression) &&
|
|
111
|
+
attr.value.expression.value === groupName
|
|
112
|
+
) {
|
|
113
|
+
foundGroup = true
|
|
114
|
+
}
|
|
115
|
+
} else if (aName === 'untilMeasured') {
|
|
116
|
+
foundUntilMeasured = true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (foundGroup && foundUntilMeasured) return true
|
|
120
|
+
}
|
|
121
|
+
current = current.parentPath
|
|
122
|
+
}
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
|
|
79
126
|
export function createExtractor(
|
|
80
127
|
{ logger = console, platform = 'web' }: ExtractorOptions = { logger: console }
|
|
81
128
|
) {
|
|
@@ -359,6 +406,22 @@ export function createExtractor(
|
|
|
359
406
|
)
|
|
360
407
|
}
|
|
361
408
|
|
|
409
|
+
function getGroupPseudo(name: string) {
|
|
410
|
+
const [_, groupName, a, b, c] = name.split('-')
|
|
411
|
+
if (!groupName) return
|
|
412
|
+
const m2 = a && b ? `${a}-${b}` : ''
|
|
413
|
+
const media = (m2 && mediaQueryConfig[m2] && m2) || (a && mediaQueryConfig[a] && a)
|
|
414
|
+
return media
|
|
415
|
+
? media === m2
|
|
416
|
+
? c
|
|
417
|
+
: b
|
|
418
|
+
? `${b}${c ? `-${c}` : ''}`
|
|
419
|
+
: undefined
|
|
420
|
+
: a
|
|
421
|
+
? `${a}${b ? `-${b}` : ''}${c ? `-${c}` : ''}`
|
|
422
|
+
: undefined
|
|
423
|
+
}
|
|
424
|
+
|
|
362
425
|
/**
|
|
363
426
|
* Step 1: Determine if importing any statically extractable components
|
|
364
427
|
*/
|
|
@@ -1095,6 +1158,12 @@ export function createExtractor(
|
|
|
1095
1158
|
|
|
1096
1159
|
...(!isTargetingHTML
|
|
1097
1160
|
? [
|
|
1161
|
+
// native has no css pseudo selectors; these are runtime-only on
|
|
1162
|
+
// rn (event listeners + style merge in createComponent), and if
|
|
1163
|
+
// we let them flatten into the stylesheet they get written
|
|
1164
|
+
// under a literal key that rn treats as an unknown style prop.
|
|
1165
|
+
// hover is intentionally excluded: it is no-op on native and
|
|
1166
|
+
// should be dropped rather than preserved as runtime work.
|
|
1098
1167
|
'pressStyle',
|
|
1099
1168
|
'focusStyle',
|
|
1100
1169
|
'focusVisibleStyle',
|
|
@@ -1368,6 +1437,20 @@ export function createExtractor(
|
|
|
1368
1437
|
return [attribute.value!, path.get('value')!] as const
|
|
1369
1438
|
})()
|
|
1370
1439
|
|
|
1440
|
+
// these props have runtime-only meaning on native. decide from the
|
|
1441
|
+
// original jsx attr, before getSplitStyles has a chance to drop
|
|
1442
|
+
// native-dead work like hoverStyle from its static output.
|
|
1443
|
+
if (
|
|
1444
|
+
deoptProps.has(name) ||
|
|
1445
|
+
(platform === 'native' &&
|
|
1446
|
+
name[0] === '$' &&
|
|
1447
|
+
(name.startsWith('$theme-') ||
|
|
1448
|
+
(name.startsWith('$group-') && getGroupPseudo(name) !== 'hover')))
|
|
1449
|
+
) {
|
|
1450
|
+
inlined.set(name, true)
|
|
1451
|
+
return attr
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1371
1454
|
const remove = () => {
|
|
1372
1455
|
Array.isArray(valuePath)
|
|
1373
1456
|
? valuePath.map((p) => p.remove())
|
|
@@ -1420,9 +1503,33 @@ export function createExtractor(
|
|
|
1420
1503
|
return attr
|
|
1421
1504
|
}
|
|
1422
1505
|
|
|
1506
|
+
// static `group="<literal>"` is handled at compile-time in
|
|
1507
|
+
// extractToClassNames (emits container CSS + adds `t_group_<name>`
|
|
1508
|
+
// className), so we drop the JSX attribute here without bailing
|
|
1509
|
+
// flattening. dynamic `group={expr}` falls through to runtime.
|
|
1510
|
+
if (isTargetingHTML && name === 'group' && t.isStringLiteral(value)) {
|
|
1511
|
+
return []
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1423
1514
|
// if value can be evaluated, extract it and filter it out
|
|
1424
1515
|
const styleValue = attemptEvalSafe(value)
|
|
1425
1516
|
|
|
1517
|
+
// media-like blocks with nested $-keys (eg $theme-dark={{ $sm: {…} }})
|
|
1518
|
+
// can't resolve to static CSS in one pass — keep them on the runtime path
|
|
1519
|
+
if (
|
|
1520
|
+
name[0] === '$' &&
|
|
1521
|
+
(name.startsWith('$theme-') || name.startsWith('$group-')) &&
|
|
1522
|
+
styleValue &&
|
|
1523
|
+
typeof styleValue === 'object' &&
|
|
1524
|
+
Object.keys(styleValue).some((k) => k[0] === '$')
|
|
1525
|
+
) {
|
|
1526
|
+
if (shouldPrintDebug) {
|
|
1527
|
+
logger.info(` ! nested media-like key inside ${name}, deopt to runtime`)
|
|
1528
|
+
}
|
|
1529
|
+
inlined.set(name, true)
|
|
1530
|
+
return attr
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1426
1533
|
// never flatten if a prop isn't a valid static attribute
|
|
1427
1534
|
// only post prop-mapping
|
|
1428
1535
|
if (!variants[name] && !isValidStyleKey(name, staticConfig)) {
|
|
@@ -1525,15 +1632,25 @@ export function createExtractor(
|
|
|
1525
1632
|
}
|
|
1526
1633
|
|
|
1527
1634
|
if (isValidStyleKey(name, staticConfig)) {
|
|
1528
|
-
// $theme
|
|
1529
|
-
//
|
|
1635
|
+
// $theme- / $group- styles extract through the atomic-CSS pipeline
|
|
1636
|
+
// (extractToClassNames → createMediaStyle), so they fall through to
|
|
1637
|
+
// the normal style return below. The one case we still bail is
|
|
1638
|
+
// $group-<name>-* when an ancestor element declares `group="<name>"`
|
|
1639
|
+
// together with `untilMeasured` — the runtime measures the parent
|
|
1640
|
+
// and only then emits child styles, which can't be modeled in CSS.
|
|
1641
|
+
// $platform- can be flattened if the platform matches.
|
|
1530
1642
|
if (name[0] === '$') {
|
|
1531
|
-
if (name.startsWith('$
|
|
1532
|
-
|
|
1533
|
-
|
|
1643
|
+
if (name.startsWith('$group-')) {
|
|
1644
|
+
const groupName = name.slice('$group-'.length).split('-')[0]
|
|
1645
|
+
if (groupName && hasUntilMeasuredAncestor(path, groupName)) {
|
|
1646
|
+
if (shouldPrintDebug) {
|
|
1647
|
+
logger.info(
|
|
1648
|
+
` ! group="${groupName}" ancestor has untilMeasured, not flattening: ${name}`
|
|
1649
|
+
)
|
|
1650
|
+
}
|
|
1651
|
+
inlined.set(name, true)
|
|
1652
|
+
return attr
|
|
1534
1653
|
}
|
|
1535
|
-
inlined.set(name, true)
|
|
1536
|
-
return attr
|
|
1537
1654
|
}
|
|
1538
1655
|
|
|
1539
1656
|
// $platform-web, $platform-native, $platform-ios, $platform-android, $platform-tv, $platform-androidtv, $platform-tvos
|
|
@@ -1901,7 +2018,6 @@ export function createExtractor(
|
|
|
1901
2018
|
})
|
|
1902
2019
|
|
|
1903
2020
|
if (!shouldFlatten) {
|
|
1904
|
-
// were no longer partially optimizing, it adds a lot of complexity for dubious performance
|
|
1905
2021
|
if (shouldPrintDebug) {
|
|
1906
2022
|
logger.info(
|
|
1907
2023
|
`Deopting ${JSON.stringify({
|
|
@@ -1913,7 +2029,55 @@ export function createExtractor(
|
|
|
1913
2029
|
})}`
|
|
1914
2030
|
)
|
|
1915
2031
|
}
|
|
1916
|
-
|
|
2032
|
+
// PARTIAL FLATTEN (native): even when the element must stay on the runtime
|
|
2033
|
+
// path (pseudo/group/dynamic keep it deopted), pre-merge the pure-static
|
|
2034
|
+
// style props into a single `style={…}` so the runtime skips its per-prop
|
|
2035
|
+
// loop for them (the dominant deopt cost on RN). theme tokens ($…) and
|
|
2036
|
+
// dynamic props stay inline so theme/media switching + dynamics are
|
|
2037
|
+
// unaffected; dead native hoverStyle is dropped (no-op on touch).
|
|
2038
|
+
let partial: (t.JSXAttribute | t.JSXSpreadAttribute)[] | null = null
|
|
2039
|
+
if (
|
|
2040
|
+
platform === 'native' &&
|
|
2041
|
+
!staticConfig.isHOC &&
|
|
2042
|
+
!staticConfig.isStyledHOC &&
|
|
2043
|
+
!ogAttributes.some(
|
|
2044
|
+
(a) =>
|
|
2045
|
+
t.isJSXSpreadAttribute(a) ||
|
|
2046
|
+
(t.isJSXAttribute(a) &&
|
|
2047
|
+
t.isJSXIdentifier(a.name) &&
|
|
2048
|
+
a.name.name === 'style')
|
|
2049
|
+
)
|
|
2050
|
+
) {
|
|
2051
|
+
const staticStyle: Record<string, any> = {}
|
|
2052
|
+
const consumed = new Set<string>()
|
|
2053
|
+
for (const a of attrs) {
|
|
2054
|
+
if (a.type !== 'style' || !a.name || !a.attr) continue
|
|
2055
|
+
if (pseudoDescriptors[a.name]) continue
|
|
2056
|
+
if (!t.isJSXAttribute(a.attr) || !t.isJSXIdentifier(a.attr.name)) continue
|
|
2057
|
+
if (styleValueHasToken(a.value)) continue
|
|
2058
|
+
Object.assign(staticStyle, a.value)
|
|
2059
|
+
consumed.add(a.attr.name.name)
|
|
2060
|
+
}
|
|
2061
|
+
if (consumed.size >= 2) {
|
|
2062
|
+
const kept = ogAttributes.filter((a) => {
|
|
2063
|
+
if (!t.isJSXAttribute(a) || !t.isJSXIdentifier(a.name)) return true
|
|
2064
|
+
const n = a.name.name
|
|
2065
|
+
if (consumed.has(n)) return false
|
|
2066
|
+
if (n === 'hoverStyle') return false
|
|
2067
|
+
if (n.startsWith('$group-') && getGroupPseudo(n) === 'hover')
|
|
2068
|
+
return false
|
|
2069
|
+
return true
|
|
2070
|
+
})
|
|
2071
|
+
partial = [
|
|
2072
|
+
t.jsxAttribute(
|
|
2073
|
+
t.jsxIdentifier('style'),
|
|
2074
|
+
t.jsxExpressionContainer(literalToAst(staticStyle) as t.Expression)
|
|
2075
|
+
),
|
|
2076
|
+
...kept,
|
|
2077
|
+
]
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
node.attributes = partial ?? ogAttributes
|
|
1917
2081
|
return
|
|
1918
2082
|
}
|
|
1919
2083
|
|
|
@@ -2302,8 +2466,37 @@ export function createExtractor(
|
|
|
2302
2466
|
const before = process.env.IS_STATIC
|
|
2303
2467
|
process.env.IS_STATIC = 'is_static'
|
|
2304
2468
|
try {
|
|
2469
|
+
// $group-* / $theme-* keys carry block-form style objects that
|
|
2470
|
+
// getSplitStyles drops in static mode (no parent group context,
|
|
2471
|
+
// no theme value to read). Pluck them out so the atomic-CSS
|
|
2472
|
+
// pipeline in extractToClassNames can emit @container / theme
|
|
2473
|
+
// rules for them directly.
|
|
2474
|
+
let extractedMediaLikeProps: Record<string, any> | null = null
|
|
2475
|
+
let propsForSplit: any = props
|
|
2476
|
+
for (const k in props) {
|
|
2477
|
+
if (
|
|
2478
|
+
k[0] === '$' &&
|
|
2479
|
+
(k.startsWith('$group-') || k.startsWith('$theme-'))
|
|
2480
|
+
) {
|
|
2481
|
+
if (propsForSplit === props) {
|
|
2482
|
+
propsForSplit = { ...props }
|
|
2483
|
+
}
|
|
2484
|
+
if (
|
|
2485
|
+
platform === 'native' &&
|
|
2486
|
+
k.startsWith('$group-') &&
|
|
2487
|
+
getGroupPseudo(k) === 'hover'
|
|
2488
|
+
) {
|
|
2489
|
+
delete propsForSplit[k]
|
|
2490
|
+
continue
|
|
2491
|
+
}
|
|
2492
|
+
extractedMediaLikeProps ||= {}
|
|
2493
|
+
extractedMediaLikeProps[k] = propsForSplit[k]
|
|
2494
|
+
delete propsForSplit[k]
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2305
2498
|
const out = getSplitStyles(
|
|
2306
|
-
|
|
2499
|
+
propsForSplit,
|
|
2307
2500
|
staticConfig,
|
|
2308
2501
|
defaultTheme,
|
|
2309
2502
|
'',
|
|
@@ -2324,10 +2517,47 @@ export function createExtractor(
|
|
|
2324
2517
|
debugPropValue || shouldPrintDebug
|
|
2325
2518
|
)!
|
|
2326
2519
|
|
|
2520
|
+
// resolve tokens inside the plucked blocks: they skipped the main
|
|
2521
|
+
// split, so values like "$color5" would flow raw into the emitted
|
|
2522
|
+
// CSS where browsers reject the declaration. run each block through
|
|
2523
|
+
// its own static split so tokens become var(--x) references.
|
|
2524
|
+
// (native never extracts these — it deopts below — so web only.)
|
|
2525
|
+
if (extractedMediaLikeProps && platform !== 'native') {
|
|
2526
|
+
for (const k in extractedMediaLikeProps) {
|
|
2527
|
+
const block = extractedMediaLikeProps[k]
|
|
2528
|
+
if (!block || typeof block !== 'object') continue
|
|
2529
|
+
const blockOut = getSplitStyles(
|
|
2530
|
+
block,
|
|
2531
|
+
staticConfig,
|
|
2532
|
+
defaultTheme,
|
|
2533
|
+
'',
|
|
2534
|
+
componentState,
|
|
2535
|
+
{
|
|
2536
|
+
...styleProps,
|
|
2537
|
+
noClass: true,
|
|
2538
|
+
fallbackProps: completeProps,
|
|
2539
|
+
},
|
|
2540
|
+
undefined,
|
|
2541
|
+
undefined,
|
|
2542
|
+
undefined,
|
|
2543
|
+
undefined,
|
|
2544
|
+
false,
|
|
2545
|
+
debugPropValue || shouldPrintDebug
|
|
2546
|
+
)
|
|
2547
|
+
if (blockOut) {
|
|
2548
|
+
extractedMediaLikeProps[k] = {
|
|
2549
|
+
...blockOut.style,
|
|
2550
|
+
...blockOut.pseudos,
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2327
2556
|
let outProps = {
|
|
2328
2557
|
...(includeProps ? out.viewProps : {}),
|
|
2329
2558
|
...out.style,
|
|
2330
2559
|
...out.pseudos,
|
|
2560
|
+
...extractedMediaLikeProps,
|
|
2331
2561
|
}
|
|
2332
2562
|
|
|
2333
2563
|
// check de-opt props again
|
|
@@ -2335,6 +2565,20 @@ export function createExtractor(
|
|
|
2335
2565
|
if (deoptProps.has(key)) {
|
|
2336
2566
|
shouldFlatten = false
|
|
2337
2567
|
}
|
|
2568
|
+
// native has no atomic-CSS sink for theme- / group- pseudo
|
|
2569
|
+
// blocks; if they flatten they get serialized under the literal
|
|
2570
|
+
// `$theme-…` / `$group-…` key into the RN StyleSheet, where the
|
|
2571
|
+
// runtime's @container / theme-name matching never runs. de-opt
|
|
2572
|
+
// → preserve as inline prop so getSplitStyles handles them at
|
|
2573
|
+
// render time.
|
|
2574
|
+
if (
|
|
2575
|
+
platform === 'native' &&
|
|
2576
|
+
key[0] === '$' &&
|
|
2577
|
+
(key.startsWith('$theme-') ||
|
|
2578
|
+
(key.startsWith('$group-') && getGroupPseudo(key) !== 'hover'))
|
|
2579
|
+
) {
|
|
2580
|
+
shouldFlatten = false
|
|
2581
|
+
}
|
|
2338
2582
|
}
|
|
2339
2583
|
|
|
2340
2584
|
if (shouldPrintDebug) {
|
|
@@ -170,6 +170,72 @@ export async function extractToClassNames({
|
|
|
170
170
|
originalNodeName
|
|
171
171
|
)
|
|
172
172
|
|
|
173
|
+
// detect static `group="<name>"` literal — runtime side normally emits the
|
|
174
|
+
// container-name/container-type CSS lazily via getSplitStyles (createComponent
|
|
175
|
+
// path), but extracted elements never go through that code, so we emit the
|
|
176
|
+
// equivalent CSS at compile time and add `t_group_<name>` to the className.
|
|
177
|
+
let staticGroupName: string | null = null
|
|
178
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
179
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue
|
|
180
|
+
if (attr.name.name !== 'group') continue
|
|
181
|
+
if (t.isStringLiteral(attr.value)) {
|
|
182
|
+
staticGroupName = attr.value.value
|
|
183
|
+
} else if (
|
|
184
|
+
t.isJSXExpressionContainer(attr.value) &&
|
|
185
|
+
t.isStringLiteral(attr.value.expression)
|
|
186
|
+
) {
|
|
187
|
+
staticGroupName = attr.value.expression.value
|
|
188
|
+
}
|
|
189
|
+
// dynamic group={someProp} falls back to the runtime path.
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
if (staticGroupName) {
|
|
193
|
+
const containerIdentifier = `t_group_${staticGroupName}`
|
|
194
|
+
const containerType =
|
|
195
|
+
(tamaguiConfig.settings?.webContainerType as string | undefined) ||
|
|
196
|
+
'inline-size'
|
|
197
|
+
const containerSelector = `.${containerIdentifier}`
|
|
198
|
+
if (!cssMap.has(containerSelector)) {
|
|
199
|
+
cssMap.set(containerSelector, {
|
|
200
|
+
css: `${containerSelector} { container-name: ${staticGroupName}; container-type: ${containerType}; }`,
|
|
201
|
+
commentTexts: [comment],
|
|
202
|
+
})
|
|
203
|
+
} else {
|
|
204
|
+
cssMap.get(containerSelector)!.commentTexts.push(comment)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// invariant guard for $group-/$theme- CSS extraction (below): those styles
|
|
209
|
+
// can only be extracted to static @container / theme CSS when a class toggle
|
|
210
|
+
// alone expresses the change. JS animation drivers (reanimated, motion, moti,
|
|
211
|
+
// react-native) interpolate in JS and need the runtime path to read hard
|
|
212
|
+
// values when the element animates — a static class can't drive them. gated
|
|
213
|
+
// on whether THIS element animates rather than on the driver, because motion
|
|
214
|
+
// is isReactNative:false yet still needs JS.
|
|
215
|
+
//
|
|
216
|
+
// in practice createExtractor's deoptProps already de-opts every animated
|
|
217
|
+
// element upstream (`animation`/`animateOnly`/`animatePresence` always;
|
|
218
|
+
// enterStyle/exitStyle on RN drivers), so a flattened element reaching here
|
|
219
|
+
// is normally non-animated and this never fires. it keeps the invariant
|
|
220
|
+
// local and correct regardless of future deoptProps changes — it only ever
|
|
221
|
+
// bails the animated case to runtime, never widens what extracts.
|
|
222
|
+
let elementIsAnimated = false
|
|
223
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
224
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue
|
|
225
|
+
const n = attr.name.name
|
|
226
|
+
if (
|
|
227
|
+
n === 'animation' ||
|
|
228
|
+
n === 'animateOnly' ||
|
|
229
|
+
n === 'animatePresence' ||
|
|
230
|
+
n === 'animatedBy' ||
|
|
231
|
+
n === 'enterStyle' ||
|
|
232
|
+
n === 'exitStyle'
|
|
233
|
+
) {
|
|
234
|
+
elementIsAnimated = true
|
|
235
|
+
break
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
173
239
|
function addStyle(style: StyleObject) {
|
|
174
240
|
const identifier = style[StyleObjectIdentifier]
|
|
175
241
|
const rules = style[StyleObjectRules]
|
|
@@ -194,20 +260,46 @@ export async function extractToClassNames({
|
|
|
194
260
|
const property = style[0]
|
|
195
261
|
const mediaName = property.slice(1)
|
|
196
262
|
|
|
197
|
-
// $group- styles
|
|
198
|
-
//
|
|
199
|
-
// In the future, CSS animation drivers could potentially optimize this.
|
|
263
|
+
// $group- styles extract through createMediaStyle which emits @container
|
|
264
|
+
// rules keyed on the parent's `t_group_<name>` className.
|
|
200
265
|
if (mediaName.startsWith('group-')) {
|
|
201
|
-
|
|
266
|
+
// a class toggle can't drive a JS animation driver's interpolation;
|
|
267
|
+
// keep animated elements on the runtime path (matches pre-extraction
|
|
268
|
+
// behavior for the group case).
|
|
269
|
+
if (elementIsAnimated) {
|
|
270
|
+
throw new BailOptimizationError()
|
|
271
|
+
}
|
|
272
|
+
const mediaStyle = createMediaStyle(
|
|
273
|
+
style,
|
|
274
|
+
mediaName,
|
|
275
|
+
extractor.getTamagui()!.media,
|
|
276
|
+
'group',
|
|
277
|
+
false,
|
|
278
|
+
mediaStylesSeen
|
|
279
|
+
)
|
|
280
|
+
const identifier = addStyle(mediaStyle)
|
|
281
|
+
classNames.push(identifier)
|
|
282
|
+
continue
|
|
202
283
|
}
|
|
203
284
|
|
|
204
285
|
// Check for theme/platform media queries (e.g., $theme-dark, $platform-web)
|
|
205
286
|
const mediaTypeMatch = mediaName.match(/^(theme|platform)-/)
|
|
206
287
|
if (mediaTypeMatch) {
|
|
207
288
|
const mediaType = mediaTypeMatch[1] as 'theme' | 'platform'
|
|
289
|
+
// $theme- values can change at runtime (theme switch); if this
|
|
290
|
+
// element animates, a JS driver must interpolate the change, so keep
|
|
291
|
+
// it on the runtime path. $platform- is build-time static — never gated.
|
|
292
|
+
if (mediaType === 'theme' && elementIsAnimated) {
|
|
293
|
+
throw new BailOptimizationError()
|
|
294
|
+
}
|
|
295
|
+
// createMediaStyle internally calls getGroupPropParts(`theme-` + key)
|
|
296
|
+
// for theme, so we must pass the short key ("dark", not "theme-dark")
|
|
297
|
+
// to avoid double-prefixing. Platform stays as-is (runtime parity).
|
|
298
|
+
const innerKey =
|
|
299
|
+
mediaType === 'theme' ? mediaName.slice('theme-'.length) : mediaName
|
|
208
300
|
const mediaStyle = createMediaStyle(
|
|
209
301
|
style,
|
|
210
|
-
|
|
302
|
+
innerKey,
|
|
211
303
|
extractor.getTamagui()!.media,
|
|
212
304
|
mediaType,
|
|
213
305
|
false,
|
|
@@ -343,6 +435,13 @@ export async function extractToClassNames({
|
|
|
343
435
|
baseClassNameStr = `font_${baseFontFamily}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
344
436
|
}
|
|
345
437
|
|
|
438
|
+
// add t_group_<name> for elements with a static group="<name>" literal so
|
|
439
|
+
// descendants' @container <name> rules can match (runtime path normally
|
|
440
|
+
// appends this class; extracted elements bypass it).
|
|
441
|
+
if (staticGroupName) {
|
|
442
|
+
baseClassNameStr = `t_group_${staticGroupName}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
443
|
+
}
|
|
444
|
+
|
|
346
445
|
// add is_View or is_Text base class matching runtime behavior
|
|
347
446
|
const baseTypeClass = staticConfig.isText ? 'is_Text' : 'is_View'
|
|
348
447
|
baseClassNameStr = `${baseTypeClass}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExtractor.d.ts","sourceRoot":"","sources":["../../src/extractor/createExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,CAAC,MAAM,cAAc,CAAA;AAmBjC,OAAO,KAAK,EAGV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAGf,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAc1E,OAAO,EAAE,iBAAiB,EAA6B,MAAM,6BAA6B,CAAA;AAwB1F,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAE1D,KAAK,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"createExtractor.d.ts","sourceRoot":"","sources":["../../src/extractor/createExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,CAAC,MAAM,cAAc,CAAA;AAmBjC,OAAO,KAAK,EAGV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAGf,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAc1E,OAAO,EAAE,iBAAiB,EAA6B,MAAM,6BAA6B,CAAA;AAwB1F,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAE1D,KAAK,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAA;AAuD9C,wBAAgB,eAAe,CAC7B,EAAE,MAAgB,EAAE,QAAgB,EAAE,GAAE,gBAAsC;;;;;yBAsJnD,cAAc;6BAPhB,cAAc;;mBAwBtB,UAAU,SAAS,mBAAmB;;;;;;;eAKpC,UAAU,SAAS,mBAAmB;;;;;;;EA4lF1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractToClassNames.d.ts","sourceRoot":"","sources":["../../src/extractor/extractToClassNames.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAA;AAKjC,OAAO,KAAK,EAAe,cAAc,EAAW,MAAM,UAAU,CAAA;AAEpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAYlD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,CAAC,CAAC,IAAI,CAAA;IACX,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;CACF,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAA;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,cAAc,CAAA;IACvB,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAA;CACtC,CAAA;AAQD,wBAAsB,mBAAmB,CAAC,EACxC,SAAS,EACT,MAAM,EACN,UAAe,EACf,OAAO,EACP,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"extractToClassNames.d.ts","sourceRoot":"","sources":["../../src/extractor/extractToClassNames.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAA;AAKjC,OAAO,KAAK,EAAe,cAAc,EAAW,MAAM,UAAU,CAAA;AAEpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAYlD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,CAAC,CAAC,IAAI,CAAA;IACX,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;CACF,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAA;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,cAAc,CAAA;IACvB,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAA;CACtC,CAAA;AAQD,wBAAsB,mBAAmB,CAAC,EACxC,SAAS,EACT,MAAM,EACN,UAAe,EACf,OAAO,EACP,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA8mB9D"}
|