@colorye/react-native-css 0.2.1

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/src/babel.js ADDED
@@ -0,0 +1,274 @@
1
+ import fs from "fs";
2
+ import nodePath from "path";
3
+ import {
4
+ getInheritStyleExpression,
5
+ getRootInheritStyleExpression,
6
+ getStaticMergeExpression,
7
+ getStyleExpression,
8
+ isFragmentElement,
9
+ isImportOrRequire,
10
+ isRootLevelJSXElement,
11
+ tryGetStaticStyleInfo,
12
+ } from "./utils/babel.js";
13
+
14
+ const libRoot = nodePath.dirname(__filename);
15
+
16
+ // Cache for loaded stylesheets
17
+ const stylesheetCache = new Map();
18
+
19
+ /**
20
+ * Load pre-computed stylesheet JSON (generated by Metro transformer)
21
+ */
22
+ function loadStylesheet(cssPath) {
23
+ if (stylesheetCache.has(cssPath)) {
24
+ return stylesheetCache.get(cssPath);
25
+ }
26
+
27
+ try {
28
+ // Try the exported-stylesheet.json from lib directory
29
+ const exportedPath = nodePath.join(libRoot, "exported-stylesheet.json");
30
+ if (fs.existsSync(exportedPath)) {
31
+ const content = fs.readFileSync(exportedPath, "utf-8");
32
+ const stylesheet = JSON.parse(content);
33
+ stylesheetCache.set(cssPath, stylesheet);
34
+ return stylesheet;
35
+ }
36
+
37
+ // Try .json next to the CSS file
38
+ const cssJsonPath = `${cssPath}.json`;
39
+ if (fs.existsSync(cssJsonPath)) {
40
+ const content = fs.readFileSync(cssJsonPath, "utf-8");
41
+ const stylesheet = JSON.parse(content);
42
+ stylesheetCache.set(cssPath, stylesheet);
43
+ return stylesheet;
44
+ }
45
+
46
+ return null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ export default function ({ types: t }) {
53
+ return {
54
+ name: "@colorye/react-native-css",
55
+ visitor: {
56
+ Program: {
57
+ enter(path, state) {
58
+ if (!this.opts.css) {
59
+ console.warn("No css file provided");
60
+ return;
61
+ }
62
+
63
+ const { filename } = state.file.opts;
64
+ const options = {
65
+ paths: this.opts.paths || [],
66
+ excludes: this.opts.excludes || [],
67
+ css: this.opts.css,
68
+ };
69
+
70
+ // Normalize filename for cross-platform regex matching
71
+ const normalizedFilename = filename.replace(/\\/g, "/");
72
+ const regex = options.paths.map((p) => new RegExp(p));
73
+ const excludesRegex = options.excludes.map((p) => new RegExp(p));
74
+ if (
75
+ !regex.some((re) => normalizedFilename.match(re) || filename.match(re)) ||
76
+ excludesRegex.some((re) => normalizedFilename.match(re) || filename.match(re))
77
+ ) {
78
+ return;
79
+ }
80
+
81
+ state.enabled = true;
82
+
83
+ // Load pre-computed stylesheet for static inlining
84
+ state.stylesheetData = loadStylesheet(options.css);
85
+
86
+ // Track which runtime helpers we need
87
+ state.needsGetStyle = false;
88
+ state.needsGetInheritStyle = false;
89
+ state.needsMergeStyles = false;
90
+
91
+ // Prepare runtime helper declarations
92
+ state.stylesheetId = path.scope.generateUidIdentifier();
93
+ state.stylesheet = t.variableDeclaration("var", [
94
+ t.variableDeclarator(
95
+ state.stylesheetId,
96
+ t.callExpression(t.identifier("require"), [t.stringLiteral(options.css)]),
97
+ ),
98
+ ]);
99
+
100
+ state.getStyleId = path.scope.generateUidIdentifier();
101
+ state.getStyle = t.variableDeclaration("var", [
102
+ t.variableDeclarator(
103
+ state.getStyleId,
104
+ t.memberExpression(
105
+ t.memberExpression(
106
+ t.callExpression(t.identifier("require"), [
107
+ t.stringLiteral(nodePath.join(libRoot, "./transformer-runtime")),
108
+ ]),
109
+ t.identifier("default"),
110
+ ),
111
+ t.identifier("getStyle"),
112
+ ),
113
+ ),
114
+ ]);
115
+
116
+ state.getInheritStyleId = path.scope.generateUidIdentifier();
117
+ state.getInheritStyle = t.variableDeclaration("var", [
118
+ t.variableDeclarator(
119
+ state.getInheritStyleId,
120
+ t.memberExpression(
121
+ t.memberExpression(
122
+ t.callExpression(t.identifier("require"), [
123
+ t.stringLiteral(nodePath.join(libRoot, "./transformer-runtime")),
124
+ ]),
125
+ t.identifier("default"),
126
+ ),
127
+ t.identifier("getInheritStyle"),
128
+ ),
129
+ ),
130
+ ]);
131
+
132
+ state.mergeStylesId = path.scope.generateUidIdentifier();
133
+ state.mergeStyles = t.variableDeclaration("var", [
134
+ t.variableDeclarator(
135
+ state.mergeStylesId,
136
+ t.memberExpression(
137
+ t.memberExpression(
138
+ t.callExpression(t.identifier("require"), [
139
+ t.stringLiteral(nodePath.join(libRoot, "./transformer-runtime")),
140
+ ]),
141
+ t.identifier("default"),
142
+ ),
143
+ t.identifier("mergeStyles"),
144
+ ),
145
+ ),
146
+ ]);
147
+ },
148
+ exit(path, state) {
149
+ if (!state.enabled) return;
150
+
151
+ const helpers = [];
152
+
153
+ // Only inject helpers that are actually used
154
+ if (state.needsGetStyle) {
155
+ helpers.push(state.stylesheet);
156
+ helpers.push(state.getStyle);
157
+ }
158
+ if (state.needsGetInheritStyle) {
159
+ helpers.push(state.getInheritStyle);
160
+ }
161
+ if (state.needsMergeStyles) {
162
+ helpers.push(state.mergeStyles);
163
+ }
164
+
165
+ if (helpers.length === 0) return;
166
+
167
+ const lastImport = path
168
+ .get("body")
169
+ .filter((p) => isImportOrRequire(p))
170
+ .pop();
171
+
172
+ helpers
173
+ .reverse()
174
+ .forEach((node) =>
175
+ lastImport ? lastImport.insertAfter(node) : path.unshiftContainer("body", node),
176
+ );
177
+ },
178
+ },
179
+ JSXElement(path, state) {
180
+ if (!state.enabled) return;
181
+ if (isFragmentElement(t, path.node.openingElement.name)) return;
182
+
183
+ const openingElement = path.node.openingElement;
184
+ const isRootLevel = isRootLevelJSXElement(path);
185
+
186
+ // Inject inheritStyle for root-level elements
187
+ if (isRootLevel) {
188
+ const inheritStyle = openingElement.attributes.find(
189
+ (attr) => attr.name?.name === "inheritStyle",
190
+ );
191
+
192
+ if (!inheritStyle) {
193
+ const inheritStyleExpressions = getRootInheritStyleExpression(path, t);
194
+ openingElement.attributes.push(
195
+ t.jsxAttribute(
196
+ t.jsxIdentifier("inheritStyle"),
197
+ t.jsxExpressionContainer(inheritStyleExpressions),
198
+ ),
199
+ );
200
+ }
201
+ }
202
+
203
+ const shouldCombineStyles = openingElement.attributes.some(
204
+ (attr) => attr.name?.name === "__combinedStyles",
205
+ );
206
+
207
+ if (shouldCombineStyles) {
208
+ let styleExpressions;
209
+
210
+ // Try static inlining first (if stylesheet loaded and class is static)
211
+ if (state.stylesheetData) {
212
+ const staticInfo = tryGetStaticStyleInfo(path, state, t);
213
+
214
+ if (staticInfo) {
215
+ // Static class! Use lightweight mergeStyles
216
+ styleExpressions = getStaticMergeExpression(path, state, t, staticInfo);
217
+ state.needsMergeStyles = true;
218
+ }
219
+ }
220
+
221
+ if (!styleExpressions) {
222
+ // Fall back to full runtime getStyle
223
+ state.needsGetStyle = true;
224
+ styleExpressions = getStyleExpression(path, state, t);
225
+ }
226
+
227
+ const style = openingElement.attributes.find((attr) => attr.name?.name === "style");
228
+ if (style) {
229
+ style.value = t.jsxExpressionContainer(styleExpressions);
230
+ } else {
231
+ openingElement.attributes.push(
232
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(styleExpressions)),
233
+ );
234
+ }
235
+ }
236
+
237
+ // Inject inheritStyle for child elements
238
+ path.traverse({
239
+ JSXElement(childPath) {
240
+ const childOpeningElement = childPath.node.openingElement;
241
+
242
+ if (isFragmentElement(t, childOpeningElement.name)) {
243
+ childPath.skip();
244
+ return;
245
+ }
246
+
247
+ // Child elements need getInheritStyle for propagation
248
+ state.needsGetInheritStyle = true;
249
+ // getInheritStyle depends on getStyle
250
+ state.needsGetStyle = true;
251
+
252
+ const inheritStyleExpressions = getInheritStyleExpression(path, state, t);
253
+ const inheritStyle = childOpeningElement.attributes.find(
254
+ (attr) => attr.name?.name === "inheritStyle",
255
+ );
256
+
257
+ if (inheritStyle) {
258
+ inheritStyle.value = t.jsxExpressionContainer(inheritStyleExpressions);
259
+ } else {
260
+ childOpeningElement.attributes.push(
261
+ t.jsxAttribute(
262
+ t.jsxIdentifier("inheritStyle"),
263
+ t.jsxExpressionContainer(inheritStyleExpressions),
264
+ ),
265
+ );
266
+ }
267
+
268
+ childPath.skip();
269
+ },
270
+ });
271
+ },
272
+ },
273
+ };
274
+ }