@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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CSSX contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @cssxio/babel-plugin
2
+
3
+ This plugin compiles CSSX calls imported from `@cssxio/cssx`. That is the default `importSource`.
4
+
5
+ ```js
6
+ // babel.config.js
7
+ export default {
8
+ plugins: ['@cssxio/babel-plugin'],
9
+ };
10
+ ```
11
+
12
+ It supports namespace, default, and named `create`, `props`, and `sx` imports. Named imports are called directly. Namespace and default imports must use dot notation, such as `cssx.create(...)`; computed API calls such as `cssx['create'](...)` are rejected.
13
+
14
+ `create` must be declared at module scope. It needs one plain object with non-computed keys. Values must be string literals or unchanged local constants initialized with string literals. Spreads, changed values, and other dynamic values produce an error at the source location.
15
+
16
+ The plugin replaces `create` with compiled styles. It folds a `props` call only when every input is a local compiled style, a nested array of supported inputs, `false`, or `null`. Static props folding does not accept `undefined`. Other `props` calls stay at runtime.
17
+
18
+ The plugin also compiles static strings in `sx` calls. It supports static strings, `false`, `null`, nested arrays without spreads, logical-and expressions, and conditional expressions. Unsupported nested input leaves the `sx` call at runtime.
19
+
20
+ The plugin removes unused CSSX imports. It writes CSS data to Babel file metadata as `cssx`. `cssx.candidates` maps each reachable source candidate to its atomic class, `cssx.composites` maps each emitted composite class to its winning atomic classes, and `cssx.atomicClasses` identifies atoms still needed for dynamic runtime composition. `cssx.origins` stores the first source location for each reachable candidate. Origin lines and columns are zero-based. A direct style-key reference keeps that key's candidates; dynamic or non-member style use keeps every candidate from that style map.
21
+
22
+ Use `importSource` to target a custom runtime re-export:
23
+
24
+ ```js
25
+ export default {
26
+ plugins: [['@cssxio/babel-plugin', { importSource: '@app/cssx' }]],
27
+ };
28
+ ```
29
+
30
+ The plugin accepts an optional `theme` string containing CSS text with CSSX `@theme` input. Use `themeFile` in the CSSX adapter when the build tool should read theme CSS text from a file.
package/dist/index.cjs ADDED
@@ -0,0 +1,640 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => cssxBabelPlugin
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/state-helpers.ts
28
+ function recordCandidateOrigin(state, candidate, location) {
29
+ if (!location || state.candidateOrigins.has(candidate)) {
30
+ return;
31
+ }
32
+ state.candidateOrigins.set(candidate, { line: location.line - 1, column: location.column });
33
+ }
34
+ function markStyleKeyCandidates(state, candidatesByKey, key) {
35
+ for (const candidate of candidatesByKey[key] ?? []) {
36
+ state.liveCandidates.add(candidate);
37
+ }
38
+ }
39
+ function markAllCandidates(state, candidatesByKey) {
40
+ for (const candidates of Object.values(candidatesByKey)) {
41
+ for (const candidate of candidates) {
42
+ state.liveCandidates.add(candidate);
43
+ }
44
+ }
45
+ }
46
+
47
+ // src/ast-helpers.ts
48
+ function memberPropertyName(member, t) {
49
+ if (!member.computed && t.isIdentifier(member.property)) {
50
+ return member.property.name;
51
+ }
52
+ if (member.computed && t.isStringLiteral(member.property)) {
53
+ return member.property.value;
54
+ }
55
+ return null;
56
+ }
57
+ function importedFunctionBinding(path, localName, importedName, t, importSource) {
58
+ const binding = path.scope.getBinding(localName);
59
+ const bindingPath = binding?.path;
60
+ if (!bindingPath?.isImportSpecifier()) {
61
+ return false;
62
+ }
63
+ const imported = bindingPath.node.imported;
64
+ const actualName = t.isIdentifier(imported) ? imported.name : imported.value;
65
+ return actualName === importedName && isCssxImport(bindingPath, importSource);
66
+ }
67
+ function importedNamespaceBinding(path, localName, importSource) {
68
+ const binding = path.scope.getBinding(localName);
69
+ const bindingPath = binding?.path;
70
+ if (!bindingPath || !bindingPath.isImportNamespaceSpecifier() && !bindingPath.isImportDefaultSpecifier()) {
71
+ return false;
72
+ }
73
+ return isCssxImport(bindingPath, importSource);
74
+ }
75
+ function isCssxImport(path, importSource) {
76
+ const parent = path.parentPath;
77
+ return parent?.isImportDeclaration() === true && parent.node.source.value === importSource;
78
+ }
79
+ function assertModuleScope(path) {
80
+ const statement = path.getStatementParent();
81
+ const statementParent = statement?.parentPath;
82
+ const isDirectProgramStatement = statementParent?.isProgram() || statementParent?.isExportNamedDeclaration();
83
+ if (!isDirectProgramStatement) {
84
+ throw diagnosticError(path, "cssx.create() must be declared at module scope.");
85
+ }
86
+ }
87
+ function assertNoComputedCssxApiCall(path, t, importSource) {
88
+ const callee = path.node.callee;
89
+ if (!t.isMemberExpression(callee) || !callee.computed || !t.isIdentifier(callee.object) || !t.isStringLiteral(callee.property)) {
90
+ return;
91
+ }
92
+ if ((callee.property.value === "create" || callee.property.value === "props" || callee.property.value === "sx") && importedNamespaceBinding(path, callee.object.name, importSource)) {
93
+ throw diagnosticError(path, "CSSX API calls must use dot notation, for example cssx.create(...).");
94
+ }
95
+ }
96
+ function diagnosticError(path, message) {
97
+ return process.env.NODE_ENV === "production" ? new Error(message) : path.buildCodeFrameError(message);
98
+ }
99
+ function readStaticString(path) {
100
+ if (path.isStringLiteral()) {
101
+ return path.node.value;
102
+ }
103
+ if (!path.isIdentifier()) {
104
+ return null;
105
+ }
106
+ const binding = path.scope.getBinding(path.node.name);
107
+ if (!binding?.constant || binding.constantViolations.length !== 0 || !binding.path.isVariableDeclarator()) {
108
+ return null;
109
+ }
110
+ const initializer = binding.path.get("init");
111
+ return initializer.isStringLiteral() ? initializer.node.value : null;
112
+ }
113
+ function propertyKey(key, t) {
114
+ return t.isValidIdentifier(key) ? t.identifier(key) : t.stringLiteral(key);
115
+ }
116
+ function packedStyleExpression(style, t) {
117
+ return t.valueToNode(style);
118
+ }
119
+ function objectPropertyName(property, t) {
120
+ if (t.isIdentifier(property.key)) {
121
+ return property.key.name;
122
+ }
123
+ if (t.isStringLiteral(property.key) || t.isNumericLiteral(property.key)) {
124
+ return String(property.key.value);
125
+ }
126
+ return null;
127
+ }
128
+ function isCreateCall(path, t, importSource) {
129
+ return isCssxApiCall(path, t, importSource, "create");
130
+ }
131
+ function isPropsCall(path, t, importSource) {
132
+ return isCssxApiCall(path, t, importSource, "props");
133
+ }
134
+ function isSxCall(path, t, importSource) {
135
+ return isCssxApiCall(path, t, importSource, "sx");
136
+ }
137
+ function isCssxApiCall(path, t, importSource, api) {
138
+ const callee = path.node.callee;
139
+ if (t.isIdentifier(callee)) {
140
+ return importedFunctionBinding(path, callee.name, api, t, importSource);
141
+ }
142
+ return t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.object) && t.isIdentifier(callee.property, { name: api }) && importedNamespaceBinding(path, callee.object.name, importSource);
143
+ }
144
+
145
+ // src/index.ts
146
+ var import_compiler = require("@cssxio/compiler");
147
+ var DEFAULT_IMPORT_SOURCE = "@cssxio/cssx";
148
+ function cssxBabelPlugin(api, options = {}) {
149
+ api.assertVersion(7);
150
+ const t = api.types;
151
+ const importSource = options.importSource ?? DEFAULT_IMPORT_SOURCE;
152
+ let state;
153
+ let fileName = "";
154
+ let foldedProps = [];
155
+ return {
156
+ name: "@cssxio/babel-plugin",
157
+ visitor: {
158
+ Program: {
159
+ enter(_path, babelState) {
160
+ fileName = babelState.file.opts.filename ?? "";
161
+ state = {
162
+ classNameAllocator: options.classNameAllocator ?? (0, import_compiler.createClassNameAllocator)(options.className),
163
+ styles: /* @__PURE__ */ new Map(),
164
+ styleCandidates: /* @__PURE__ */ new Map(),
165
+ styleClasses: /* @__PURE__ */ new Map(),
166
+ classes: /* @__PURE__ */ new Map(),
167
+ candidateOrigins: /* @__PURE__ */ new Map(),
168
+ liveCandidates: /* @__PURE__ */ new Set(),
169
+ composites: /* @__PURE__ */ new Map(),
170
+ liveComposites: /* @__PURE__ */ new Set(),
171
+ liveFallbackClasses: /* @__PURE__ */ new Set(),
172
+ cssRanges: []
173
+ };
174
+ foldedProps = [];
175
+ },
176
+ exit(path, babelState) {
177
+ finalizeFoldedProps(path, t);
178
+ path.scope.crawl();
179
+ markReferencedStyleCandidates(path);
180
+ removeDeadStyleMaps(path);
181
+ compactLiveStyleRecords(path);
182
+ for (const statement of path.get("body")) {
183
+ if (!statement.isImportDeclaration() || statement.node.source.value !== importSource) {
184
+ continue;
185
+ }
186
+ for (const specifier of [...statement.get("specifiers")]) {
187
+ const local = specifier.node.local.name;
188
+ const binding = path.scope.getBinding(local);
189
+ if (binding?.referencePaths.length === 0) {
190
+ specifier.remove();
191
+ }
192
+ }
193
+ if (statement.node.specifiers.length === 0) {
194
+ statement.remove();
195
+ }
196
+ }
197
+ babelState.file.metadata.cssx = {
198
+ candidates: Object.fromEntries(
199
+ [...state.classes].filter(([candidate]) => state.liveCandidates.has(candidate))
200
+ ),
201
+ origins: Object.fromEntries(
202
+ [...state.candidateOrigins].filter(([candidate]) => state.liveCandidates.has(candidate))
203
+ ),
204
+ composites: Object.fromEntries(
205
+ [...state.composites].filter(([className]) => state.liveComposites.has(className))
206
+ ),
207
+ atomicClasses: [...state.liveFallbackClasses].sort(),
208
+ cssOnlySignature: cssOnlySignature(babelState.file.code, state.cssRanges)
209
+ };
210
+ }
211
+ },
212
+ CallExpression(path) {
213
+ assertNoComputedCssxApiCall(path, t, importSource);
214
+ if (isCreateCall(path, t, importSource)) {
215
+ assertModuleScope(path);
216
+ transformCreate(path, t);
217
+ return;
218
+ }
219
+ if (isPropsCall(path, t, importSource)) {
220
+ transformStaticProps(path, t);
221
+ }
222
+ if (isSxCall(path, t, importSource)) {
223
+ transformSx(path, t);
224
+ }
225
+ }
226
+ }
227
+ };
228
+ function transformCreate(path, types) {
229
+ if (path.node.arguments.length !== 1 || !types.isObjectExpression(path.node.arguments[0])) {
230
+ throw diagnosticError(path, "cssx.create() expects one object literal argument.");
231
+ }
232
+ const input = readStyleMap(path.get("arguments.0"), types);
233
+ let result;
234
+ try {
235
+ result = (0, import_compiler.compileStyleRecords)(input, {
236
+ theme: options.theme,
237
+ classNameAllocator: state.classNameAllocator,
238
+ reusabilityBudget: options.reusabilityBudget
239
+ });
240
+ } catch (error) {
241
+ const message = error instanceof Error ? error.message : "Unable to compile CSSX styles.";
242
+ throw diagnosticError(path, message);
243
+ }
244
+ const parent = path.parentPath;
245
+ if (options.stableClassNames) {
246
+ 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}`;
247
+ result = withStableCompositeNames(result, fileName, anchor);
248
+ }
249
+ for (const [candidate, className] of Object.entries(result.classes)) {
250
+ state.classes.set(candidate, className);
251
+ recordCandidateOrigin(state, candidate, path.node.loc?.start);
252
+ }
253
+ for (const [className, atomicClasses] of Object.entries(result.composites)) {
254
+ state.composites.set(className, atomicClasses);
255
+ }
256
+ const replacement = types.objectExpression(
257
+ Object.entries(result.styles).map(
258
+ ([key, style]) => types.objectProperty(propertyKey(key, types), packedStyleExpression(style, types))
259
+ )
260
+ );
261
+ if (parent.isVariableDeclarator() && types.isIdentifier(parent.node.id)) {
262
+ state.styles.set(parent.node.id.name, result.styles);
263
+ state.styleCandidates.set(parent.node.id.name, result.candidates);
264
+ state.styleClasses.set(parent.node.id.name, result.classNames);
265
+ }
266
+ path.replaceWith(replacement);
267
+ }
268
+ function transformStaticProps(path, types) {
269
+ const styles = [];
270
+ for (const argument of path.node.arguments) {
271
+ if (types.isSpreadElement(argument)) {
272
+ return;
273
+ }
274
+ const resolved = resolveStyleArgument(argument, types);
275
+ if (resolved === void 0) {
276
+ return;
277
+ }
278
+ if (resolved) {
279
+ styles.push(...resolved);
280
+ }
281
+ }
282
+ const singleStyle = styles.length === 1 ? styles[0] : void 0;
283
+ const composition = singleStyle ? void 0 : (0, import_compiler.composeCompiledStyles)(styles, state.classNameAllocator);
284
+ const className = singleStyle ? singleStyle.c : options.stableClassNames ? stableCompositeName(
285
+ fileName,
286
+ void 0,
287
+ `props:${styles.map((style) => style.c).sort().join("\0")}`
288
+ ) : composition.className;
289
+ if (composition) {
290
+ state.composites.set(className, composition.atomicClasses);
291
+ }
292
+ markEmittedClassNames(className);
293
+ foldedProps.push({ path, className });
294
+ }
295
+ function finalizeFoldedProps(program, types) {
296
+ if (foldedProps.length === 0) {
297
+ return;
298
+ }
299
+ const declarations = [];
300
+ const useHelper = foldedProps.length >= 4;
301
+ const helper = useHelper ? program.scope.generateUidIdentifier("cssxProps") : void 0;
302
+ if (helper) {
303
+ declarations.push(
304
+ types.variableDeclarator(
305
+ helper,
306
+ types.arrowFunctionExpression(
307
+ [types.identifier("className")],
308
+ types.objectExpression([
309
+ types.objectProperty(types.identifier("className"), types.identifier("className"))
310
+ ])
311
+ )
312
+ )
313
+ );
314
+ }
315
+ if (declarations.length > 0) {
316
+ program.unshiftContainer("body", types.variableDeclaration("const", declarations));
317
+ }
318
+ for (const { path, className } of foldedProps) {
319
+ path.replaceWith(
320
+ helper ? types.callExpression(helper, [types.stringLiteral(className)]) : types.objectExpression([
321
+ types.objectProperty(types.identifier("className"), types.stringLiteral(className))
322
+ ])
323
+ );
324
+ }
325
+ }
326
+ function transformSx(path, types) {
327
+ const staticSource = readStaticSxSource(path.node.arguments, types);
328
+ if (staticSource !== null) {
329
+ if (isGeneratedClassNames(staticSource)) {
330
+ return;
331
+ }
332
+ path.replaceWith(types.stringLiteral(compileSxString(staticSource, path.node.loc?.start)));
333
+ return;
334
+ }
335
+ const transformed = path.node.arguments.map(
336
+ (argument) => transformSxArgument(argument, types)
337
+ );
338
+ if (transformed.some((argument) => argument === void 0)) {
339
+ return;
340
+ }
341
+ const expressions = transformed.filter(
342
+ (argument) => argument !== void 0
343
+ );
344
+ path.node.arguments = expressions;
345
+ }
346
+ function transformSxArgument(node, types) {
347
+ if (types.isSpreadElement(node)) {
348
+ return void 0;
349
+ }
350
+ if (types.isStringLiteral(node)) {
351
+ if (isGeneratedClassNames(node.value)) {
352
+ return node;
353
+ }
354
+ return types.stringLiteral(compileSxString(node.value, node.loc?.start));
355
+ }
356
+ if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
357
+ return types.stringLiteral("");
358
+ }
359
+ if (types.isArrayExpression(node)) {
360
+ const elements = node.elements.map(
361
+ (element) => element && !types.isSpreadElement(element) ? transformSxArgument(element, types) : void 0
362
+ );
363
+ if (elements.some((element) => element === void 0)) {
364
+ return void 0;
365
+ }
366
+ const values = elements.filter((element) => element !== void 0);
367
+ return types.arrayExpression(values);
368
+ }
369
+ if (types.isLogicalExpression(node, { operator: "&&" })) {
370
+ const right = transformSxArgument(node.right, types);
371
+ return right ? types.logicalExpression("&&", node.left, right) : void 0;
372
+ }
373
+ if (types.isConditionalExpression(node)) {
374
+ const consequent = transformSxArgument(node.consequent, types);
375
+ const alternate = transformSxArgument(node.alternate, types);
376
+ return consequent && alternate ? types.conditionalExpression(node.test, consequent, alternate) : void 0;
377
+ }
378
+ return node;
379
+ }
380
+ function compileSxString(source, location) {
381
+ if (!source.trim()) {
382
+ return "";
383
+ }
384
+ let result;
385
+ try {
386
+ result = (0, import_compiler.compileStyleRecords)(
387
+ { inline: source },
388
+ {
389
+ theme: options.theme,
390
+ classNameAllocator: state.classNameAllocator,
391
+ reusabilityBudget: options.reusabilityBudget
392
+ }
393
+ );
394
+ } catch (error) {
395
+ const message = error instanceof Error ? error.message : "Unable to compile CSSX sx() utilities.";
396
+ throw new Error(message);
397
+ }
398
+ const className = options.stableClassNames ? stableCompositeName(fileName, location, "sx") : result.classNames.inline;
399
+ for (const [candidate, candidateClassName] of Object.entries(result.classes)) {
400
+ state.classes.set(candidate, candidateClassName);
401
+ state.liveCandidates.add(candidate);
402
+ recordCandidateOrigin(state, candidate, location);
403
+ }
404
+ for (const [compositeClassName, atomicClasses] of Object.entries(result.composites)) {
405
+ state.composites.set(compositeClassName, atomicClasses);
406
+ }
407
+ if (options.stableClassNames) {
408
+ state.composites.set(className, atomicClassesForStyle(result.styles.inline));
409
+ }
410
+ markEmittedClassNames(className);
411
+ return className;
412
+ }
413
+ function isGeneratedClassNames(value) {
414
+ return /^s[0-9A-Za-z]+x(?:\s+s[0-9A-Za-z]+x)*$/.test(value);
415
+ }
416
+ function markReferencedStyleCandidates(program) {
417
+ for (const [styleName, candidatesByKey] of state.styleCandidates) {
418
+ const binding = program.scope.getBinding(styleName);
419
+ for (const reference of binding.referencePaths) {
420
+ const parent = reference.parentPath;
421
+ if (!parent?.isMemberExpression() || parent.node.object !== reference.node) {
422
+ markAllCandidates(state, candidatesByKey);
423
+ markAllStyleClasses(styleName);
424
+ markAllFallbackClasses(styleName);
425
+ continue;
426
+ }
427
+ const key = memberPropertyName(parent.node, t);
428
+ if (key === null) {
429
+ markAllCandidates(state, candidatesByKey);
430
+ markAllStyleClasses(styleName);
431
+ markAllFallbackClasses(styleName);
432
+ } else {
433
+ markStyleKeyCandidates(state, candidatesByKey, key);
434
+ markStyleClass(styleName, key);
435
+ markFallbackClasses(styleName, key);
436
+ }
437
+ }
438
+ }
439
+ }
440
+ function removeDeadStyleMaps(program) {
441
+ for (const styleName of state.styles.keys()) {
442
+ const binding = program.scope.getBinding(styleName);
443
+ if (binding && binding.referencePaths.length === 0 && binding.path.isVariableDeclarator()) {
444
+ binding.path.remove();
445
+ }
446
+ }
447
+ }
448
+ function compactLiveStyleRecords(program) {
449
+ for (const styleName of state.styles.keys()) {
450
+ const binding = program.scope.getBinding(styleName);
451
+ if (!binding?.path.isVariableDeclarator() || !binding.path.parentPath?.isVariableDeclaration()) {
452
+ continue;
453
+ }
454
+ const entries = /* @__PURE__ */ new Map();
455
+ const recordArrays = [];
456
+ const styles = binding.path.node.init;
457
+ for (const property of styles.properties) {
458
+ const style = property;
459
+ const records = style.value.properties.find(
460
+ (styleProperty) => t.isObjectProperty(styleProperty) && t.isIdentifier(styleProperty.key, { name: "_" })
461
+ );
462
+ const recordValues = records.value;
463
+ for (let index = 0; index < recordValues.elements.length; index++) {
464
+ const record = recordValues.elements[index];
465
+ const key = packedRecordKey(record);
466
+ const entry = entries.get(key) ?? { record, uses: 0 };
467
+ entry.uses++;
468
+ entries.set(key, entry);
469
+ recordArrays.push({ records: recordValues, index, record });
470
+ }
471
+ }
472
+ const interned = /* @__PURE__ */ new Map();
473
+ const declarations = [];
474
+ for (const [key, entry] of entries) {
475
+ if (entry.uses < 2) {
476
+ continue;
477
+ }
478
+ const identifier = program.scope.generateUidIdentifier("c");
479
+ interned.set(key, identifier);
480
+ declarations.push(t.variableDeclarator(identifier, entry.record));
481
+ }
482
+ if (declarations.length === 0) {
483
+ continue;
484
+ }
485
+ for (const { records, index, record } of recordArrays) {
486
+ const identifier = interned.get(packedRecordKey(record));
487
+ if (!identifier) {
488
+ continue;
489
+ }
490
+ records.elements[index] = t.identifier(identifier.name);
491
+ }
492
+ const declaration = binding.path.parentPath;
493
+ const statement = declaration.parentPath?.isExportNamedDeclaration() ? declaration.parentPath : declaration;
494
+ statement.insertBefore(t.variableDeclaration("const", declarations));
495
+ }
496
+ }
497
+ function packedRecordKey(record) {
498
+ return JSON.stringify(
499
+ record.elements.map(
500
+ (value) => t.isNullLiteral(value) ? null : value.value
501
+ )
502
+ );
503
+ }
504
+ function readStyleMap(path, types) {
505
+ const result = /* @__PURE__ */ Object.create(null);
506
+ for (const property of path.get("properties")) {
507
+ if (!property.isObjectProperty() || property.node.computed) {
508
+ throw diagnosticError(property, "cssx.create() only supports plain object properties.");
509
+ }
510
+ const key = objectPropertyName(property.node, types);
511
+ const value = property.get("value");
512
+ const utilityString = readStaticString(value);
513
+ if (!key || utilityString === null) {
514
+ throw diagnosticError(property, "Each cssx.create() value must be a static utility string.");
515
+ }
516
+ state.cssRanges.push({ start: value.node.start, end: value.node.end });
517
+ result[key] = utilityString;
518
+ }
519
+ return result;
520
+ }
521
+ function resolveStyleArgument(node, types) {
522
+ if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
523
+ return null;
524
+ }
525
+ if (types.isArrayExpression(node)) {
526
+ const styles = [];
527
+ for (const element of node.elements) {
528
+ if (!element || types.isSpreadElement(element)) {
529
+ return void 0;
530
+ }
531
+ const resolved = resolveStyleArgument(element, types);
532
+ if (resolved === void 0) {
533
+ return void 0;
534
+ }
535
+ if (resolved) {
536
+ styles.push(...resolved);
537
+ }
538
+ }
539
+ return styles;
540
+ }
541
+ if (!types.isMemberExpression(node) || node.computed || !types.isIdentifier(node.object) || !types.isIdentifier(node.property)) {
542
+ return void 0;
543
+ }
544
+ const map = state.styles.get(node.object.name);
545
+ const style = map?.[node.property.name];
546
+ const candidates = state.styleCandidates.get(node.object.name);
547
+ if (style && candidates) {
548
+ markStyleKeyCandidates(state, candidates, node.property.name);
549
+ }
550
+ return style ? [style] : void 0;
551
+ }
552
+ function markStyleClass(styleName, key) {
553
+ const className = state.styleClasses.get(styleName)?.[key];
554
+ if (className) {
555
+ markEmittedClassNames(className);
556
+ }
557
+ }
558
+ function markAllStyleClasses(styleName) {
559
+ for (const className of Object.values(state.styleClasses.get(styleName))) {
560
+ markEmittedClassNames(className);
561
+ }
562
+ }
563
+ function markEmittedClassNames(classNames) {
564
+ for (const className of classNames.split(/\s+/).filter(Boolean)) {
565
+ if (state.composites.has(className)) {
566
+ state.liveComposites.add(className);
567
+ } else {
568
+ state.liveFallbackClasses.add(className);
569
+ }
570
+ }
571
+ }
572
+ function markFallbackClasses(styleName, key) {
573
+ for (const record of state.styles.get(styleName)?.[key]?._ ?? []) {
574
+ if (record[0]) {
575
+ state.liveFallbackClasses.add(record[0]);
576
+ }
577
+ }
578
+ }
579
+ function markAllFallbackClasses(styleName) {
580
+ for (const key of Object.keys(state.styles.get(styleName))) {
581
+ markFallbackClasses(styleName, key);
582
+ }
583
+ }
584
+ function readStaticSxSource(nodes, types) {
585
+ const values = [];
586
+ for (const node of nodes) {
587
+ if (types.isStringLiteral(node)) {
588
+ values.push(node.value);
589
+ } else if (types.isNullLiteral(node) || types.isBooleanLiteral(node, { value: false })) {
590
+ continue;
591
+ } else if (types.isArrayExpression(node) && node.elements.every((element) => element !== null)) {
592
+ const nested = readStaticSxSource(
593
+ node.elements.filter((element) => element !== null),
594
+ types
595
+ );
596
+ if (nested === null) {
597
+ return null;
598
+ }
599
+ values.push(nested);
600
+ } else {
601
+ return null;
602
+ }
603
+ }
604
+ return values.filter(Boolean).join(" ");
605
+ }
606
+ }
607
+ function withStableCompositeNames(result, fileName, anchor) {
608
+ const styles = /* @__PURE__ */ Object.create(null);
609
+ const classNames = /* @__PURE__ */ Object.create(null);
610
+ const composites = /* @__PURE__ */ Object.create(null);
611
+ for (const [name, style] of Object.entries(result.styles)) {
612
+ const className = stableCompositeName(fileName, void 0, `${anchor}:style:${name}`);
613
+ styles[name] = { ...style, c: className };
614
+ classNames[name] = className;
615
+ composites[className] = atomicClassesForStyle(style);
616
+ }
617
+ return { ...result, styles, classNames, composites };
618
+ }
619
+ function atomicClassesForStyle(style) {
620
+ return [...new Set(style._.map((record) => record[0]).filter((className) => !!className))];
621
+ }
622
+ function stableCompositeName(fileName, location, kind) {
623
+ const anchor = `${fileName}\0${location?.line ?? 0}\0${location?.column ?? 0}\0${kind}`;
624
+ let hash = 0xcbf29ce484222325n;
625
+ for (let index = 0; index < anchor.length; index++) {
626
+ hash ^= BigInt(anchor.charCodeAt(index));
627
+ hash = BigInt.asUintN(64, hash * 0x100000001b3n);
628
+ }
629
+ return `d${hash.toString(36)}`;
630
+ }
631
+ function cssOnlySignature(source, ranges) {
632
+ const chunks = [];
633
+ let position = 0;
634
+ for (const { start, end } of [...ranges].sort((left, right) => left.start - right.start)) {
635
+ chunks.push(source.slice(position, start));
636
+ position = end;
637
+ }
638
+ chunks.push(source.slice(position));
639
+ return chunks.join("");
640
+ }