@cssxio/babel-plugin 0.2.0

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.
@@ -0,0 +1,42 @@
1
+ import { PluginObj, PluginPass } from '@babel/core';
2
+ import * as babelTypes from '@babel/types';
3
+ import { ClassNameOptions, ClassNameAllocator, ReusabilityBudget, DarkMode } from '@cssxio/compiler';
4
+
5
+ /** Options that control how the CSSX compiler plugin finds and compiles calls. */
6
+ interface CssxPluginOptions {
7
+ /** Module specifier that exports the CSSX runtime. */
8
+ readonly importSource?: string;
9
+ /** CSS text containing the theme used to compile utilities. */
10
+ readonly theme?: string;
11
+ /** Options that control generated class names. */
12
+ readonly className?: ClassNameOptions;
13
+ /** Shared allocator used by a bundler to name classes across source modules. */
14
+ readonly classNameAllocator?: ClassNameAllocator;
15
+ /** Controls how aggressively static styles share generated class fragments. */
16
+ readonly reusabilityBudget?: ReusabilityBudget;
17
+ /** Uses source-addressed composite names for development stylesheet updates. */
18
+ readonly stableClassNames?: boolean;
19
+ /** Controls how the `dark` variant is activated. */
20
+ readonly darkMode?: DarkMode;
21
+ }
22
+
23
+ /**
24
+ * Compiles CSSX calls in one source module.
25
+ *
26
+ * Program entry creates fresh file state. Call visits compile create, props, and sx calls.
27
+ * Program exit finds reachable candidates, removes unused CSSX imports, and writes metadata.
28
+ * Metadata has a cssx property with candidates mapped to class names and first source origins.
29
+ * Only reachable candidates are included. Origin lines are zero-based and columns are zero-based.
30
+ *
31
+ * @param api The compiler API.
32
+ * @param api.types Helpers for creating source code nodes.
33
+ * @param api.assertVersion Checks the supported compiler version.
34
+ * @param options Plugin options.
35
+ * @returns A compiler plugin that transforms CSSX calls.
36
+ */
37
+ declare function cssxBabelPlugin(api: {
38
+ readonly types: typeof babelTypes;
39
+ assertVersion(version: number): void;
40
+ }, options?: CssxPluginOptions): PluginObj<PluginPass>;
41
+
42
+ export { type CssxPluginOptions, cssxBabelPlugin as default };
@@ -0,0 +1,42 @@
1
+ import { PluginObj, PluginPass } from '@babel/core';
2
+ import * as babelTypes from '@babel/types';
3
+ import { ClassNameOptions, ClassNameAllocator, ReusabilityBudget, DarkMode } from '@cssxio/compiler';
4
+
5
+ /** Options that control how the CSSX compiler plugin finds and compiles calls. */
6
+ interface CssxPluginOptions {
7
+ /** Module specifier that exports the CSSX runtime. */
8
+ readonly importSource?: string;
9
+ /** CSS text containing the theme used to compile utilities. */
10
+ readonly theme?: string;
11
+ /** Options that control generated class names. */
12
+ readonly className?: ClassNameOptions;
13
+ /** Shared allocator used by a bundler to name classes across source modules. */
14
+ readonly classNameAllocator?: ClassNameAllocator;
15
+ /** Controls how aggressively static styles share generated class fragments. */
16
+ readonly reusabilityBudget?: ReusabilityBudget;
17
+ /** Uses source-addressed composite names for development stylesheet updates. */
18
+ readonly stableClassNames?: boolean;
19
+ /** Controls how the `dark` variant is activated. */
20
+ readonly darkMode?: DarkMode;
21
+ }
22
+
23
+ /**
24
+ * Compiles CSSX calls in one source module.
25
+ *
26
+ * Program entry creates fresh file state. Call visits compile create, props, and sx calls.
27
+ * Program exit finds reachable candidates, removes unused CSSX imports, and writes metadata.
28
+ * Metadata has a cssx property with candidates mapped to class names and first source origins.
29
+ * Only reachable candidates are included. Origin lines are zero-based and columns are zero-based.
30
+ *
31
+ * @param api The compiler API.
32
+ * @param api.types Helpers for creating source code nodes.
33
+ * @param api.assertVersion Checks the supported compiler version.
34
+ * @param options Plugin options.
35
+ * @returns A compiler plugin that transforms CSSX calls.
36
+ */
37
+ declare function cssxBabelPlugin(api: {
38
+ readonly types: typeof babelTypes;
39
+ assertVersion(version: number): void;
40
+ }, options?: CssxPluginOptions): PluginObj<PluginPass>;
41
+
42
+ export { type CssxPluginOptions, cssxBabelPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,617 @@
1
+ // src/state-helpers.ts
2
+ function recordCandidateOrigin(state, candidate, location) {
3
+ if (!location || state.candidateOrigins.has(candidate)) {
4
+ return;
5
+ }
6
+ state.candidateOrigins.set(candidate, { line: location.line - 1, column: location.column });
7
+ }
8
+ function markStyleKeyCandidates(state, candidatesByKey, key) {
9
+ for (const candidate of candidatesByKey[key] ?? []) {
10
+ state.liveCandidates.add(candidate);
11
+ }
12
+ }
13
+ function markAllCandidates(state, candidatesByKey) {
14
+ for (const candidates of Object.values(candidatesByKey)) {
15
+ for (const candidate of candidates) {
16
+ state.liveCandidates.add(candidate);
17
+ }
18
+ }
19
+ }
20
+
21
+ // src/ast-helpers.ts
22
+ function memberPropertyName(member, t) {
23
+ if (!member.computed && t.isIdentifier(member.property)) {
24
+ return member.property.name;
25
+ }
26
+ if (member.computed && t.isStringLiteral(member.property)) {
27
+ return member.property.value;
28
+ }
29
+ return null;
30
+ }
31
+ function importedFunctionBinding(path, localName, importedName, t, importSource) {
32
+ const binding = path.scope.getBinding(localName);
33
+ const bindingPath = binding?.path;
34
+ if (!bindingPath?.isImportSpecifier()) {
35
+ return false;
36
+ }
37
+ const imported = bindingPath.node.imported;
38
+ const actualName = t.isIdentifier(imported) ? imported.name : imported.value;
39
+ return actualName === importedName && isCssxImport(bindingPath, importSource);
40
+ }
41
+ function importedNamespaceBinding(path, localName, importSource) {
42
+ const binding = path.scope.getBinding(localName);
43
+ const bindingPath = binding?.path;
44
+ if (!bindingPath || !bindingPath.isImportNamespaceSpecifier() && !bindingPath.isImportDefaultSpecifier()) {
45
+ return false;
46
+ }
47
+ return isCssxImport(bindingPath, importSource);
48
+ }
49
+ function isCssxImport(path, importSource) {
50
+ const parent = path.parentPath;
51
+ return parent?.isImportDeclaration() === true && parent.node.source.value === importSource;
52
+ }
53
+ function assertModuleScope(path) {
54
+ const statement = path.getStatementParent();
55
+ const statementParent = statement?.parentPath;
56
+ const isDirectProgramStatement = statementParent?.isProgram() || statementParent?.isExportNamedDeclaration();
57
+ if (!isDirectProgramStatement) {
58
+ throw diagnosticError(path, "cssx.create() must be declared at module scope.");
59
+ }
60
+ }
61
+ function assertNoComputedCssxApiCall(path, t, importSource) {
62
+ const callee = path.node.callee;
63
+ if (!t.isMemberExpression(callee) || !callee.computed || !t.isIdentifier(callee.object) || !t.isStringLiteral(callee.property)) {
64
+ return;
65
+ }
66
+ if ((callee.property.value === "create" || callee.property.value === "props" || callee.property.value === "sx") && importedNamespaceBinding(path, callee.object.name, importSource)) {
67
+ throw diagnosticError(path, "CSSX API calls must use dot notation, for example cssx.create(...).");
68
+ }
69
+ }
70
+ function diagnosticError(path, message) {
71
+ return process.env.NODE_ENV === "production" ? new Error(message) : path.buildCodeFrameError(message);
72
+ }
73
+ function readStaticString(path) {
74
+ if (path.isStringLiteral()) {
75
+ return path.node.value;
76
+ }
77
+ if (!path.isIdentifier()) {
78
+ return null;
79
+ }
80
+ const binding = path.scope.getBinding(path.node.name);
81
+ if (!binding?.constant || binding.constantViolations.length !== 0 || !binding.path.isVariableDeclarator()) {
82
+ return null;
83
+ }
84
+ const initializer = binding.path.get("init");
85
+ return initializer.isStringLiteral() ? initializer.node.value : null;
86
+ }
87
+ function propertyKey(key, t) {
88
+ return t.isValidIdentifier(key) ? t.identifier(key) : t.stringLiteral(key);
89
+ }
90
+ function packedStyleExpression(style, t) {
91
+ return t.valueToNode(style);
92
+ }
93
+ function objectPropertyName(property, t) {
94
+ if (t.isIdentifier(property.key)) {
95
+ return property.key.name;
96
+ }
97
+ if (t.isStringLiteral(property.key) || t.isNumericLiteral(property.key)) {
98
+ return String(property.key.value);
99
+ }
100
+ return null;
101
+ }
102
+ function isCreateCall(path, t, importSource) {
103
+ return isCssxApiCall(path, t, importSource, "create");
104
+ }
105
+ function isPropsCall(path, t, importSource) {
106
+ return isCssxApiCall(path, t, importSource, "props");
107
+ }
108
+ function isSxCall(path, t, importSource) {
109
+ return isCssxApiCall(path, t, importSource, "sx");
110
+ }
111
+ function isCssxApiCall(path, t, importSource, api) {
112
+ const callee = path.node.callee;
113
+ if (t.isIdentifier(callee)) {
114
+ return importedFunctionBinding(path, callee.name, api, t, importSource);
115
+ }
116
+ return t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.object) && t.isIdentifier(callee.property, { name: api }) && importedNamespaceBinding(path, callee.object.name, importSource);
117
+ }
118
+
119
+ // src/index.ts
120
+ import { compileStyleRecords, composeCompiledStyles, createClassNameAllocator } from "@cssxio/compiler";
121
+ var DEFAULT_IMPORT_SOURCE = "@cssxio/cssx";
122
+ function cssxBabelPlugin(api, options = {}) {
123
+ api.assertVersion(7);
124
+ const t = api.types;
125
+ const importSource = options.importSource ?? DEFAULT_IMPORT_SOURCE;
126
+ let state;
127
+ let fileName = "";
128
+ let foldedProps = [];
129
+ return {
130
+ name: "@cssxio/babel-plugin",
131
+ visitor: {
132
+ Program: {
133
+ enter(_path, babelState) {
134
+ fileName = babelState.file.opts.filename ?? "";
135
+ state = {
136
+ classNameAllocator: options.classNameAllocator ?? createClassNameAllocator(options.className),
137
+ styles: /* @__PURE__ */ new Map(),
138
+ styleCandidates: /* @__PURE__ */ new Map(),
139
+ styleClasses: /* @__PURE__ */ new Map(),
140
+ classes: /* @__PURE__ */ new Map(),
141
+ candidateOrigins: /* @__PURE__ */ new Map(),
142
+ liveCandidates: /* @__PURE__ */ new Set(),
143
+ composites: /* @__PURE__ */ new Map(),
144
+ liveComposites: /* @__PURE__ */ new Set(),
145
+ liveFallbackClasses: /* @__PURE__ */ new Set(),
146
+ cssRanges: []
147
+ };
148
+ foldedProps = [];
149
+ },
150
+ exit(path, babelState) {
151
+ finalizeFoldedProps(path, t);
152
+ path.scope.crawl();
153
+ markReferencedStyleCandidates(path);
154
+ removeDeadStyleMaps(path);
155
+ compactLiveStyleRecords(path);
156
+ for (const statement of path.get("body")) {
157
+ if (!statement.isImportDeclaration() || statement.node.source.value !== importSource) {
158
+ continue;
159
+ }
160
+ for (const specifier of [...statement.get("specifiers")]) {
161
+ const local = specifier.node.local.name;
162
+ const binding = path.scope.getBinding(local);
163
+ if (binding?.referencePaths.length === 0) {
164
+ specifier.remove();
165
+ }
166
+ }
167
+ if (statement.node.specifiers.length === 0) {
168
+ statement.remove();
169
+ }
170
+ }
171
+ babelState.file.metadata.cssx = {
172
+ candidates: Object.fromEntries(
173
+ [...state.classes].filter(([candidate]) => state.liveCandidates.has(candidate))
174
+ ),
175
+ origins: Object.fromEntries(
176
+ [...state.candidateOrigins].filter(([candidate]) => state.liveCandidates.has(candidate))
177
+ ),
178
+ composites: Object.fromEntries(
179
+ [...state.composites].filter(([className]) => state.liveComposites.has(className))
180
+ ),
181
+ atomicClasses: [...state.liveFallbackClasses].sort(),
182
+ cssOnlySignature: cssOnlySignature(babelState.file.code, state.cssRanges)
183
+ };
184
+ }
185
+ },
186
+ CallExpression(path) {
187
+ assertNoComputedCssxApiCall(path, t, importSource);
188
+ if (isCreateCall(path, t, importSource)) {
189
+ assertModuleScope(path);
190
+ transformCreate(path, t);
191
+ return;
192
+ }
193
+ if (isPropsCall(path, t, importSource)) {
194
+ transformStaticProps(path, t);
195
+ }
196
+ if (isSxCall(path, t, importSource)) {
197
+ transformSx(path, t);
198
+ }
199
+ }
200
+ }
201
+ };
202
+ function transformCreate(path, types) {
203
+ if (path.node.arguments.length !== 1 || !types.isObjectExpression(path.node.arguments[0])) {
204
+ throw diagnosticError(path, "cssx.create() expects one object literal argument.");
205
+ }
206
+ const input = readStyleMap(path.get("arguments.0"), types);
207
+ let result;
208
+ try {
209
+ result = compileStyleRecords(input, {
210
+ theme: options.theme,
211
+ classNameAllocator: state.classNameAllocator,
212
+ reusabilityBudget: options.reusabilityBudget
213
+ });
214
+ } catch (error) {
215
+ const message = error instanceof Error ? error.message : "Unable to compile CSSX styles.";
216
+ throw diagnosticError(path, message);
217
+ }
218
+ const parent = path.parentPath;
219
+ if (options.stableClassNames) {
220
+ const anchor = parent.isVariableDeclarator() && types.isIdentifier(parent.node.id) ? `map:${parent.node.id.name}` : `create:${path.node.loc.start.line}:${path.node.loc.start.column}`;
221
+ result = withStableCompositeNames(result, fileName, anchor);
222
+ }
223
+ for (const [candidate, className] of Object.entries(result.classes)) {
224
+ state.classes.set(candidate, className);
225
+ recordCandidateOrigin(state, candidate, path.node.loc?.start);
226
+ }
227
+ for (const [className, atomicClasses] of Object.entries(result.composites)) {
228
+ state.composites.set(className, atomicClasses);
229
+ }
230
+ const replacement = types.objectExpression(
231
+ Object.entries(result.styles).map(
232
+ ([key, style]) => types.objectProperty(propertyKey(key, types), packedStyleExpression(style, types))
233
+ )
234
+ );
235
+ if (parent.isVariableDeclarator() && types.isIdentifier(parent.node.id)) {
236
+ state.styles.set(parent.node.id.name, result.styles);
237
+ state.styleCandidates.set(parent.node.id.name, result.candidates);
238
+ state.styleClasses.set(parent.node.id.name, result.classNames);
239
+ }
240
+ path.replaceWith(replacement);
241
+ }
242
+ function transformStaticProps(path, types) {
243
+ const styles = [];
244
+ for (const argument of path.node.arguments) {
245
+ if (types.isSpreadElement(argument)) {
246
+ return;
247
+ }
248
+ const resolved = resolveStyleArgument(argument, types);
249
+ if (resolved === void 0) {
250
+ return;
251
+ }
252
+ if (resolved) {
253
+ styles.push(...resolved);
254
+ }
255
+ }
256
+ const singleStyle = styles.length === 1 ? styles[0] : void 0;
257
+ const composition = singleStyle ? void 0 : composeCompiledStyles(styles, state.classNameAllocator);
258
+ const className = singleStyle ? singleStyle.c : options.stableClassNames ? stableCompositeName(
259
+ fileName,
260
+ void 0,
261
+ `props:${styles.map((style) => style.c).sort().join("\0")}`
262
+ ) : composition.className;
263
+ if (composition) {
264
+ state.composites.set(className, composition.atomicClasses);
265
+ }
266
+ markEmittedClassNames(className);
267
+ foldedProps.push({ path, className });
268
+ }
269
+ function finalizeFoldedProps(program, types) {
270
+ if (foldedProps.length === 0) {
271
+ return;
272
+ }
273
+ const declarations = [];
274
+ const useHelper = foldedProps.length >= 4;
275
+ const helper = useHelper ? program.scope.generateUidIdentifier("cssxProps") : void 0;
276
+ if (helper) {
277
+ declarations.push(
278
+ types.variableDeclarator(
279
+ helper,
280
+ types.arrowFunctionExpression(
281
+ [types.identifier("className")],
282
+ types.objectExpression([
283
+ types.objectProperty(types.identifier("className"), types.identifier("className"))
284
+ ])
285
+ )
286
+ )
287
+ );
288
+ }
289
+ if (declarations.length > 0) {
290
+ program.unshiftContainer("body", types.variableDeclaration("const", declarations));
291
+ }
292
+ for (const { path, className } of foldedProps) {
293
+ path.replaceWith(
294
+ helper ? types.callExpression(helper, [types.stringLiteral(className)]) : types.objectExpression([
295
+ types.objectProperty(types.identifier("className"), types.stringLiteral(className))
296
+ ])
297
+ );
298
+ }
299
+ }
300
+ function transformSx(path, types) {
301
+ const staticSource = readStaticSxSource(path.node.arguments, types);
302
+ if (staticSource !== null) {
303
+ if (isGeneratedClassNames(staticSource)) {
304
+ return;
305
+ }
306
+ path.replaceWith(types.stringLiteral(compileSxString(staticSource, path.node.loc?.start)));
307
+ return;
308
+ }
309
+ const transformed = path.node.arguments.map(
310
+ (argument) => transformSxArgument(argument, types)
311
+ );
312
+ if (transformed.some((argument) => argument === void 0)) {
313
+ return;
314
+ }
315
+ const expressions = transformed.filter(
316
+ (argument) => argument !== void 0
317
+ );
318
+ path.node.arguments = expressions;
319
+ }
320
+ function transformSxArgument(node, types) {
321
+ if (types.isSpreadElement(node)) {
322
+ return void 0;
323
+ }
324
+ if (types.isStringLiteral(node)) {
325
+ if (isGeneratedClassNames(node.value)) {
326
+ return node;
327
+ }
328
+ return types.stringLiteral(compileSxString(node.value, node.loc?.start));
329
+ }
330
+ if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
331
+ return types.stringLiteral("");
332
+ }
333
+ if (types.isArrayExpression(node)) {
334
+ const elements = node.elements.map(
335
+ (element) => element && !types.isSpreadElement(element) ? transformSxArgument(element, types) : void 0
336
+ );
337
+ if (elements.some((element) => element === void 0)) {
338
+ return void 0;
339
+ }
340
+ const values = elements.filter((element) => element !== void 0);
341
+ return types.arrayExpression(values);
342
+ }
343
+ if (types.isLogicalExpression(node, { operator: "&&" })) {
344
+ const right = transformSxArgument(node.right, types);
345
+ return right ? types.logicalExpression("&&", node.left, right) : void 0;
346
+ }
347
+ if (types.isConditionalExpression(node)) {
348
+ const consequent = transformSxArgument(node.consequent, types);
349
+ const alternate = transformSxArgument(node.alternate, types);
350
+ return consequent && alternate ? types.conditionalExpression(node.test, consequent, alternate) : void 0;
351
+ }
352
+ return node;
353
+ }
354
+ function compileSxString(source, location) {
355
+ if (!source.trim()) {
356
+ return "";
357
+ }
358
+ let result;
359
+ try {
360
+ result = compileStyleRecords(
361
+ { inline: source },
362
+ {
363
+ theme: options.theme,
364
+ classNameAllocator: state.classNameAllocator,
365
+ reusabilityBudget: options.reusabilityBudget
366
+ }
367
+ );
368
+ } catch (error) {
369
+ const message = error instanceof Error ? error.message : "Unable to compile CSSX sx() utilities.";
370
+ throw new Error(message);
371
+ }
372
+ const className = options.stableClassNames ? stableCompositeName(fileName, location, "sx") : result.classNames.inline;
373
+ for (const [candidate, candidateClassName] of Object.entries(result.classes)) {
374
+ state.classes.set(candidate, candidateClassName);
375
+ state.liveCandidates.add(candidate);
376
+ recordCandidateOrigin(state, candidate, location);
377
+ }
378
+ for (const [compositeClassName, atomicClasses] of Object.entries(result.composites)) {
379
+ state.composites.set(compositeClassName, atomicClasses);
380
+ }
381
+ if (options.stableClassNames) {
382
+ state.composites.set(className, atomicClassesForStyle(result.styles.inline));
383
+ }
384
+ markEmittedClassNames(className);
385
+ return className;
386
+ }
387
+ function isGeneratedClassNames(value) {
388
+ return /^s[0-9A-Za-z]+x(?:\s+s[0-9A-Za-z]+x)*$/.test(value);
389
+ }
390
+ function markReferencedStyleCandidates(program) {
391
+ for (const [styleName, candidatesByKey] of state.styleCandidates) {
392
+ const binding = program.scope.getBinding(styleName);
393
+ for (const reference of binding.referencePaths) {
394
+ const parent = reference.parentPath;
395
+ if (!parent?.isMemberExpression() || parent.node.object !== reference.node) {
396
+ markAllCandidates(state, candidatesByKey);
397
+ markAllStyleClasses(styleName);
398
+ markAllFallbackClasses(styleName);
399
+ continue;
400
+ }
401
+ const key = memberPropertyName(parent.node, t);
402
+ if (key === null) {
403
+ markAllCandidates(state, candidatesByKey);
404
+ markAllStyleClasses(styleName);
405
+ markAllFallbackClasses(styleName);
406
+ } else {
407
+ markStyleKeyCandidates(state, candidatesByKey, key);
408
+ markStyleClass(styleName, key);
409
+ markFallbackClasses(styleName, key);
410
+ }
411
+ }
412
+ }
413
+ }
414
+ function removeDeadStyleMaps(program) {
415
+ for (const styleName of state.styles.keys()) {
416
+ const binding = program.scope.getBinding(styleName);
417
+ if (binding && binding.referencePaths.length === 0 && binding.path.isVariableDeclarator()) {
418
+ binding.path.remove();
419
+ }
420
+ }
421
+ }
422
+ function compactLiveStyleRecords(program) {
423
+ for (const styleName of state.styles.keys()) {
424
+ const binding = program.scope.getBinding(styleName);
425
+ if (!binding?.path.isVariableDeclarator() || !binding.path.parentPath?.isVariableDeclaration()) {
426
+ continue;
427
+ }
428
+ const entries = /* @__PURE__ */ new Map();
429
+ const recordArrays = [];
430
+ const styles = binding.path.node.init;
431
+ for (const property of styles.properties) {
432
+ const style = property;
433
+ const records = style.value.properties.find(
434
+ (styleProperty) => t.isObjectProperty(styleProperty) && t.isIdentifier(styleProperty.key, { name: "_" })
435
+ );
436
+ const recordValues = records.value;
437
+ for (let index = 0; index < recordValues.elements.length; index++) {
438
+ const record = recordValues.elements[index];
439
+ const key = packedRecordKey(record);
440
+ const entry = entries.get(key) ?? { record, uses: 0 };
441
+ entry.uses++;
442
+ entries.set(key, entry);
443
+ recordArrays.push({ records: recordValues, index, record });
444
+ }
445
+ }
446
+ const interned = /* @__PURE__ */ new Map();
447
+ const declarations = [];
448
+ for (const [key, entry] of entries) {
449
+ if (entry.uses < 2) {
450
+ continue;
451
+ }
452
+ const identifier = program.scope.generateUidIdentifier("c");
453
+ interned.set(key, identifier);
454
+ declarations.push(t.variableDeclarator(identifier, entry.record));
455
+ }
456
+ if (declarations.length === 0) {
457
+ continue;
458
+ }
459
+ for (const { records, index, record } of recordArrays) {
460
+ const identifier = interned.get(packedRecordKey(record));
461
+ if (!identifier) {
462
+ continue;
463
+ }
464
+ records.elements[index] = t.identifier(identifier.name);
465
+ }
466
+ const declaration = binding.path.parentPath;
467
+ const statement = declaration.parentPath?.isExportNamedDeclaration() ? declaration.parentPath : declaration;
468
+ statement.insertBefore(t.variableDeclaration("const", declarations));
469
+ }
470
+ }
471
+ function packedRecordKey(record) {
472
+ return JSON.stringify(
473
+ record.elements.map(
474
+ (value) => t.isNullLiteral(value) ? null : value.value
475
+ )
476
+ );
477
+ }
478
+ function readStyleMap(path, types) {
479
+ const result = /* @__PURE__ */ Object.create(null);
480
+ for (const property of path.get("properties")) {
481
+ if (!property.isObjectProperty() || property.node.computed) {
482
+ throw diagnosticError(property, "cssx.create() only supports plain object properties.");
483
+ }
484
+ const key = objectPropertyName(property.node, types);
485
+ const value = property.get("value");
486
+ const utilityString = readStaticString(value);
487
+ if (!key || utilityString === null) {
488
+ throw diagnosticError(property, "Each cssx.create() value must be a static utility string.");
489
+ }
490
+ state.cssRanges.push({ start: value.node.start, end: value.node.end });
491
+ result[key] = utilityString;
492
+ }
493
+ return result;
494
+ }
495
+ function resolveStyleArgument(node, types) {
496
+ if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
497
+ return null;
498
+ }
499
+ if (types.isArrayExpression(node)) {
500
+ const styles = [];
501
+ for (const element of node.elements) {
502
+ if (!element || types.isSpreadElement(element)) {
503
+ return void 0;
504
+ }
505
+ const resolved = resolveStyleArgument(element, types);
506
+ if (resolved === void 0) {
507
+ return void 0;
508
+ }
509
+ if (resolved) {
510
+ styles.push(...resolved);
511
+ }
512
+ }
513
+ return styles;
514
+ }
515
+ if (!types.isMemberExpression(node) || node.computed || !types.isIdentifier(node.object) || !types.isIdentifier(node.property)) {
516
+ return void 0;
517
+ }
518
+ const map = state.styles.get(node.object.name);
519
+ const style = map?.[node.property.name];
520
+ const candidates = state.styleCandidates.get(node.object.name);
521
+ if (style && candidates) {
522
+ markStyleKeyCandidates(state, candidates, node.property.name);
523
+ }
524
+ return style ? [style] : void 0;
525
+ }
526
+ function markStyleClass(styleName, key) {
527
+ const className = state.styleClasses.get(styleName)?.[key];
528
+ if (className) {
529
+ markEmittedClassNames(className);
530
+ }
531
+ }
532
+ function markAllStyleClasses(styleName) {
533
+ for (const className of Object.values(state.styleClasses.get(styleName))) {
534
+ markEmittedClassNames(className);
535
+ }
536
+ }
537
+ function markEmittedClassNames(classNames) {
538
+ for (const className of classNames.split(/\s+/).filter(Boolean)) {
539
+ if (state.composites.has(className)) {
540
+ state.liveComposites.add(className);
541
+ } else {
542
+ state.liveFallbackClasses.add(className);
543
+ }
544
+ }
545
+ }
546
+ function markFallbackClasses(styleName, key) {
547
+ for (const record of state.styles.get(styleName)?.[key]?._ ?? []) {
548
+ if (record[0]) {
549
+ state.liveFallbackClasses.add(record[0]);
550
+ }
551
+ }
552
+ }
553
+ function markAllFallbackClasses(styleName) {
554
+ for (const key of Object.keys(state.styles.get(styleName))) {
555
+ markFallbackClasses(styleName, key);
556
+ }
557
+ }
558
+ function readStaticSxSource(nodes, types) {
559
+ const values = [];
560
+ for (const node of nodes) {
561
+ if (types.isStringLiteral(node)) {
562
+ values.push(node.value);
563
+ } else if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
564
+ continue;
565
+ } else if (types.isArrayExpression(node) && node.elements.every((element) => element !== null)) {
566
+ const nested = readStaticSxSource(
567
+ node.elements.filter((element) => element !== null),
568
+ types
569
+ );
570
+ if (nested === null) {
571
+ return null;
572
+ }
573
+ values.push(nested);
574
+ } else {
575
+ return null;
576
+ }
577
+ }
578
+ return values.filter(Boolean).join(" ");
579
+ }
580
+ }
581
+ function withStableCompositeNames(result, fileName, anchor) {
582
+ const styles = /* @__PURE__ */ Object.create(null);
583
+ const classNames = /* @__PURE__ */ Object.create(null);
584
+ const composites = /* @__PURE__ */ Object.create(null);
585
+ for (const [name, style] of Object.entries(result.styles)) {
586
+ const className = stableCompositeName(fileName, void 0, `${anchor}:style:${name}`);
587
+ styles[name] = { ...style, c: className };
588
+ classNames[name] = className;
589
+ composites[className] = atomicClassesForStyle(style);
590
+ }
591
+ return { ...result, styles, classNames, composites };
592
+ }
593
+ function atomicClassesForStyle(style) {
594
+ return [...new Set(style._.map((record) => record[0]).filter((className) => !!className))];
595
+ }
596
+ function stableCompositeName(fileName, location, kind) {
597
+ const anchor = `${fileName}\0${location?.line ?? 0}\0${location?.column ?? 0}\0${kind}`;
598
+ let hash = 0xcbf29ce484222325n;
599
+ for (let index = 0; index < anchor.length; index++) {
600
+ hash ^= BigInt(anchor.charCodeAt(index));
601
+ hash = BigInt.asUintN(64, hash * 0x100000001b3n);
602
+ }
603
+ return `d${hash.toString(36)}`;
604
+ }
605
+ function cssOnlySignature(source, ranges) {
606
+ const chunks = [];
607
+ let position = 0;
608
+ for (const { start, end } of [...ranges].sort((left, right) => left.start - right.start)) {
609
+ chunks.push(source.slice(position, start));
610
+ position = end;
611
+ }
612
+ chunks.push(source.slice(position));
613
+ return chunks.join("");
614
+ }
615
+ export {
616
+ cssxBabelPlugin as default
617
+ };