@plumeria/codemod 18.2.18 → 18.2.20

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.
@@ -1,11 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.__private = void 0;
3
+ exports.__private = exports.compositionSignature = void 0;
4
4
  exports.extractPlumeriaThemes = extractPlumeriaThemes;
5
+ exports.extractPlumeriaStatics = extractPlumeriaStatics;
5
6
  exports.extractPlumeriaAnimations = extractPlumeriaAnimations;
6
7
  exports.convertPlumeriaModule = convertPlumeriaModule;
7
8
  const parser_1 = require("@typescript-eslint/parser");
8
9
  const zss_engine_1 = require("zss-engine");
10
+ const compositionSignature = (classNames) => classNames.join('|');
11
+ exports.compositionSignature = compositionSignature;
12
+ const toIdent = (value) => value.replace(/[^A-Za-z0-9-]/g, '');
13
+ const capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
14
+ const camelJoin = (names) => names
15
+ .map((name, index) => {
16
+ const camel = name
17
+ .split('-')
18
+ .map((part, position) => (position === 0 ? part : capitalize(part)))
19
+ .join('');
20
+ return index === 0 ? camel : capitalize(camel);
21
+ })
22
+ .join('');
23
+ const markerVariable = (id, pseudo) => {
24
+ const hash = (0, zss_engine_1.genBase36Hash)({ [id]: pseudo }, 1, 8);
25
+ return `--${hash}-${toIdent(id)}-${toIdent(pseudo.split('(')[0])}`;
26
+ };
9
27
  const propertyKey = (node) => {
10
28
  if (!node)
11
29
  return undefined;
@@ -35,11 +53,37 @@ const lookup = (path, values) => {
35
53
  }
36
54
  return value;
37
55
  };
56
+ const markerArguments = (node, method, values) => {
57
+ if (node?.type !== 'CallExpression')
58
+ return undefined;
59
+ const path = memberPath(node.callee);
60
+ if (path?.length !== 2 || path[1] !== method)
61
+ return undefined;
62
+ const id = evaluate(node.arguments[0], values);
63
+ const pseudo = evaluate(node.arguments[1], values);
64
+ return typeof id === 'string' && typeof pseudo === 'string'
65
+ ? { id, pseudo }
66
+ : undefined;
67
+ };
38
68
  const evaluate = (node, values) => {
39
69
  if (!node)
40
70
  return undefined;
71
+ if (node.type === 'CallExpression') {
72
+ const extended = markerArguments(node, 'extended', values);
73
+ return extended
74
+ ? `@container style(${markerVariable(extended.id, extended.pseudo)}: 1)`
75
+ : undefined;
76
+ }
41
77
  if (node.type === 'Literal')
42
78
  return node.value;
79
+ if (node.type === 'UnaryExpression') {
80
+ if (node.operator !== '-' && node.operator !== '+')
81
+ return undefined;
82
+ const operand = evaluate(node.argument, values);
83
+ if (typeof operand !== 'number')
84
+ return undefined;
85
+ return node.operator === '-' ? -operand : operand;
86
+ }
43
87
  if (node.type === 'Identifier')
44
88
  return values.get(node.name);
45
89
  if (node.type === 'MemberExpression') {
@@ -78,7 +122,29 @@ const evaluate = (node, values) => {
78
122
  }
79
123
  return undefined;
80
124
  };
81
- exports.__private = { propertyKey, memberPath, evaluate };
125
+ const bindsElsewhere = (ast, name, declared) => {
126
+ const skipped = new Set(['loc', 'range', 'parent']);
127
+ const visit = (node, parent) => {
128
+ if (!node || typeof node !== 'object' || node === declared)
129
+ return false;
130
+ if (Array.isArray(node))
131
+ return node.some((child) => visit(child, parent));
132
+ if (typeof node.type !== 'string')
133
+ return false;
134
+ if (node.type === 'Identifier' && node.name === name) {
135
+ if (parent?.type === 'ExportSpecifier')
136
+ return false;
137
+ if (parent?.type === 'MemberExpression' && parent.property === node)
138
+ return !!parent.computed;
139
+ if (parent?.type === 'Property' && parent.key === node)
140
+ return !!parent.computed;
141
+ return true;
142
+ }
143
+ return Object.keys(node).some((key) => !skipped.has(key) && visit(node[key], node));
144
+ };
145
+ return visit(ast, undefined);
146
+ };
147
+ exports.__private = { propertyKey, memberPath, evaluate, bindsElsewhere };
82
148
  const isCall = (node, method) => {
83
149
  if (node?.type !== 'CallExpression')
84
150
  return false;
@@ -192,6 +258,41 @@ const emitGlobalObject = (selector, object) => {
192
258
  const content = [...declarations, ...children].join('\n');
193
259
  return `${selector} {\n${content}\n}`;
194
260
  };
261
+ function extractPlumeriaStatics(source, externalValues = {}) {
262
+ const ast = (0, parser_1.parse)(source, {
263
+ sourceType: 'module',
264
+ ecmaFeatures: { jsx: true },
265
+ loc: true,
266
+ range: true,
267
+ });
268
+ const values = new Map(Object.entries(externalValues));
269
+ const bindings = {};
270
+ const reports = [];
271
+ for (const raw of ast.body) {
272
+ const statement = declarationOf(raw);
273
+ if (statement?.type !== 'VariableDeclaration')
274
+ continue;
275
+ for (const declaration of statement.declarations) {
276
+ if (declaration.id.type !== 'Identifier' ||
277
+ !isCall(declaration.init, 'createStatic'))
278
+ continue;
279
+ const value = evaluate(declaration.init.arguments[0], values);
280
+ if (value && typeof value === 'object') {
281
+ values.set(declaration.id.name, value);
282
+ bindings[declaration.id.name] = value;
283
+ }
284
+ else {
285
+ reports.push({
286
+ line: declaration.init.loc.start.line,
287
+ column: declaration.init.loc.start.column + 1,
288
+ kind: 'dynamic-create-static',
289
+ hint: '`createStatic` must contain values that can be written into CSS.',
290
+ });
291
+ }
292
+ }
293
+ }
294
+ return { bindings, reports };
295
+ }
195
296
  function extractPlumeriaAnimations(source, externalValues = {}) {
196
297
  const ast = (0, parser_1.parse)(source, {
197
298
  sourceType: 'module',
@@ -265,7 +366,7 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
265
366
  reports,
266
367
  };
267
368
  }
268
- function convertPlumeriaModule(source, externalValues = {}) {
369
+ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
269
370
  const ast = (0, parser_1.parse)(source, {
270
371
  sourceType: 'module',
271
372
  ecmaFeatures: { jsx: true },
@@ -311,7 +412,7 @@ function convertPlumeriaModule(source, externalValues = {}) {
311
412
  if (isCall(declaration.init, 'create')) {
312
413
  const object = declaration.init.arguments[0];
313
414
  if (object?.type === 'ObjectExpression') {
314
- creates.push({ binding: declaration.id.name, object });
415
+ creates.push({ binding: declaration.id.name, object, declaration });
315
416
  }
316
417
  else {
317
418
  report(declaration.init, 'dynamic-create', '`css.create` must receive an object literal.');
@@ -321,21 +422,75 @@ function convertPlumeriaModule(source, externalValues = {}) {
321
422
  }
322
423
  if (creates.length === 0)
323
424
  return null;
324
- if (creates.length > 1) {
325
- report(creates[1].object, 'multiple-create', 'Move each `css.create` call to its own module before exporting to CSS Modules.');
326
- }
327
- const { binding, object } = creates[0];
328
- const rules = [];
425
+ const { binding } = creates[0];
426
+ const groups = [];
427
+ let rules = [];
428
+ const openGroup = (name) => {
429
+ rules = [];
430
+ groups.push({ name, rules });
431
+ };
329
432
  const keys = [];
433
+ const aliases = {};
434
+ const merges = {};
435
+ const members = new Map();
436
+ const footprints = new Map();
437
+ const track = (className) => {
438
+ const map = new Map();
439
+ footprints.set(className, map);
440
+ return { root: `.${className}`, map };
441
+ };
442
+ const conflicts = (first, second) => {
443
+ const left = footprints.get(first);
444
+ const right = footprints.get(second);
445
+ if (!left || !right)
446
+ return true;
447
+ for (const [bucket, properties] of left) {
448
+ const other = right.get(bucket);
449
+ if (!other)
450
+ continue;
451
+ for (const property of properties)
452
+ if (other.has(property))
453
+ return true;
454
+ }
455
+ return false;
456
+ };
457
+ const taken = new Set();
330
458
  const functions = {};
331
- const emit = (style, selector, wrappers = [], scope = values) => {
459
+ const classNameFor = (owner, key) => {
460
+ if (!taken.has(key))
461
+ return key;
462
+ const namespaced = `${owner}-${key}`;
463
+ let candidate = namespaced;
464
+ let suffix = 2;
465
+ while (taken.has(candidate))
466
+ candidate = `${namespaced}-${suffix++}`;
467
+ return candidate;
468
+ };
469
+ const bucketOf = (wrappers, suffix) => `${wrappers.join('\u0000')}\u0001${suffix}`;
470
+ const selectorRoot = (selector) => {
471
+ const match = /^\.[A-Za-z0-9_-]+(?::not\(#\\#\))?/.exec(selector);
472
+ return match ? match[0].length : 0;
473
+ };
474
+ const emit = (style, selector, wrappers = [], scope = values, phase = 'both', footprint, restrict) => {
332
475
  if (style?.type !== 'ObjectExpression') {
333
476
  report(style, 'dynamic-style', 'Only static style objects are exported.');
334
477
  return;
335
478
  }
479
+ const restrictRoot = restrict ? selectorRoot(selector) : 0;
336
480
  const declarations = [];
337
481
  const nested = [];
482
+ const markers = [];
338
483
  for (const property of style.properties) {
484
+ if (property.type === 'SpreadElement') {
485
+ const marker = markerArguments(property.argument, 'marker', scope);
486
+ if (marker) {
487
+ markers.push({
488
+ pseudo: marker.pseudo,
489
+ variable: markerVariable(marker.id, marker.pseudo),
490
+ });
491
+ continue;
492
+ }
493
+ }
339
494
  if (property.type !== 'Property' || property.kind !== 'init') {
340
495
  report(property, 'spread-style', 'Spread styles must be expanded before exporting to CSS Modules.');
341
496
  continue;
@@ -359,10 +514,23 @@ function convertPlumeriaModule(source, externalValues = {}) {
359
514
  continue;
360
515
  }
361
516
  const cssProperty = (0, zss_engine_1.camelToKebabCase)(rawKey);
517
+ if (restrict &&
518
+ !restrict
519
+ .get(bucketOf(wrappers, selector.slice(restrictRoot)))
520
+ ?.has(cssProperty))
521
+ continue;
522
+ if (footprint) {
523
+ const bucket = bucketOf(wrappers, selector.slice(footprint.root.length));
524
+ const held = footprint.map.get(bucket) ?? new Set();
525
+ held.add(cssProperty);
526
+ footprint.map.set(bucket, held);
527
+ }
362
528
  declarations.push(` ${cssProperty}: ${(0, zss_engine_1.applyCssValue)(value, cssProperty)};`);
363
529
  }
364
- if (declarations.length > 0) {
365
- let rule = `${selector} {\n${declarations.join('\n')}\n}`;
530
+ const pushRule = (target, lines) => {
531
+ if (lines.length === 0)
532
+ return;
533
+ let rule = `${target} {\n${lines.join('\n')}\n}`;
366
534
  for (const wrapper of [...wrappers].reverse()) {
367
535
  rule = `${wrapper} {\n${rule
368
536
  .split('\n')
@@ -370,67 +538,284 @@ function convertPlumeriaModule(source, externalValues = {}) {
370
538
  .join('\n')}\n}`;
371
539
  }
372
540
  rules.push(rule);
541
+ };
542
+ if (phase !== 'nested')
543
+ pushRule(selector, declarations);
544
+ if (phase === 'declarations')
545
+ return;
546
+ for (const marker of markers) {
547
+ pushRule(`${selector}${marker.pseudo}`, [` ${marker.variable}: 1;`]);
373
548
  }
374
549
  for (const child of nested) {
375
550
  if (child.key.startsWith('@')) {
376
- emit(child.value, selector, [...wrappers, child.key], scope);
551
+ emit(child.value, selector, [...wrappers, child.key], scope, 'both', footprint, restrict);
377
552
  }
378
553
  else {
379
- emit(child.value, `${selector}${child.key}`, wrappers, scope);
554
+ emit(child.value, `${selector}${child.key}`, wrappers, scope, 'both', footprint, restrict);
380
555
  }
381
556
  }
382
557
  };
383
- for (const property of object.properties) {
384
- if (property.type !== 'Property' || property.kind !== 'init') {
385
- report(property, 'spread-create', 'Top-level spreads cannot name a CSS Module class.');
386
- continue;
387
- }
388
- const key = property.computed
389
- ? evaluate(property.key, values)
390
- : propertyKey(property.key);
391
- if (typeof key !== 'string') {
392
- report(property.key, 'dynamic-style-key', 'Style names must be statically known.');
393
- continue;
558
+ for (const create of creates) {
559
+ const owner = create.binding;
560
+ const alias = {};
561
+ for (const property of create.object.properties) {
562
+ if (property.type !== 'Property' || property.kind !== 'init') {
563
+ report(property, 'spread-create', 'Top-level spreads cannot name a CSS Module class.');
564
+ continue;
565
+ }
566
+ const key = property.computed
567
+ ? evaluate(property.key, values)
568
+ : propertyKey(property.key);
569
+ if (typeof key !== 'string') {
570
+ report(property.key, 'dynamic-style-key', 'Style names must be statically known.');
571
+ continue;
572
+ }
573
+ const className = classNameFor(owner, key);
574
+ taken.add(className);
575
+ if (owner !== binding)
576
+ alias[key] = className;
577
+ members.set(`${owner}.${key}`, { className, value: property.value });
578
+ if (property.value.type === 'ArrowFunctionExpression' ||
579
+ property.value.type === 'FunctionExpression') {
580
+ const params = [];
581
+ let valid = true;
582
+ for (const parameter of property.value.params) {
583
+ if (parameter.type !== 'Identifier') {
584
+ valid = false;
585
+ break;
586
+ }
587
+ params.push(parameter.name);
588
+ }
589
+ let body = property.value.body;
590
+ if (body.type === 'BlockStatement') {
591
+ const returned = body.body.find((statement) => statement.type === 'ReturnStatement');
592
+ body = returned?.argument;
593
+ }
594
+ if (!valid || body?.type !== 'ObjectExpression') {
595
+ report(property.value, 'function-style', 'Function style keys need identifier parameters and an object return value.');
596
+ continue;
597
+ }
598
+ const variables = params.map((parameter) => `--${(0, zss_engine_1.camelToKebabCase)(owner)}-${(0, zss_engine_1.camelToKebabCase)(key)}-${(0, zss_engine_1.camelToKebabCase)(parameter)}`);
599
+ const scope = new Map(values);
600
+ params.forEach((parameter, index) => {
601
+ scope.set(parameter, `var(${variables[index]})`);
602
+ });
603
+ functions[className] = { params, variables };
604
+ keys.push(className);
605
+ openGroup(className);
606
+ emit(body, `.${className}`, [], scope, 'both', track(className));
607
+ continue;
608
+ }
609
+ keys.push(className);
610
+ openGroup(className);
611
+ emit(property.value, `.${className}`, [], values, 'both', track(className));
394
612
  }
395
- if (property.value.type === 'ArrowFunctionExpression' ||
396
- property.value.type === 'FunctionExpression') {
397
- const params = [];
398
- let valid = true;
399
- for (const parameter of property.value.params) {
400
- if (parameter.type !== 'Identifier') {
401
- valid = false;
402
- break;
613
+ if (owner !== binding)
614
+ aliases[owner] = alias;
615
+ }
616
+ const resolved = compositions.map((composition) => ({
617
+ composition,
618
+ parts: composition.parts.flatMap((part) => {
619
+ const member = members.get(`${part.binding}.${part.key}`);
620
+ return member && !functions[member.className] ? [member] : [];
621
+ }),
622
+ slots: composition.slots.map((slot) => slot.flatMap((part) => {
623
+ const member = members.get(`${part.binding}.${part.key}`);
624
+ return member ? [member.className] : [];
625
+ })),
626
+ }));
627
+ const collapsible = new Set(resolved.filter((entry) => entry.composition.parts.length > 1 &&
628
+ entry.parts.length === entry.composition.parts.length));
629
+ const position = new Map(groups.map((group, index) => [group.name, index]));
630
+ const inlined = new Set();
631
+ const sort = () => {
632
+ const edges = new Map();
633
+ const blame = new Map();
634
+ for (const entry of resolved) {
635
+ if (inlined.has(entry))
636
+ continue;
637
+ for (let first = 0; first < entry.slots.length; first++) {
638
+ for (let second = first + 1; second < entry.slots.length; second++) {
639
+ for (const before of entry.slots[first]) {
640
+ for (const after of entry.slots[second]) {
641
+ if (before === after || !conflicts(before, after))
642
+ continue;
643
+ edges.set(before, (edges.get(before) ?? new Set()).add(after));
644
+ blame.set(`${before}|${after}`, entry.composition);
645
+ }
646
+ }
403
647
  }
404
- params.push(parameter.name);
405
648
  }
406
- let body = property.value.body;
407
- if (body.type === 'BlockStatement') {
408
- const returned = body.body.find((statement) => statement.type === 'ReturnStatement');
409
- body = returned?.argument;
649
+ }
650
+ const incoming = new Map(groups.map((group) => [group.name, 0]));
651
+ for (const [, targets] of edges)
652
+ for (const target of targets)
653
+ if (incoming.has(target))
654
+ incoming.set(target, incoming.get(target) + 1);
655
+ const ready = groups
656
+ .filter((group) => incoming.get(group.name) === 0)
657
+ .map((group) => group.name);
658
+ const ordered = [];
659
+ while (ready.length > 0) {
660
+ ready.sort((a, b) => position.get(a) - position.get(b));
661
+ const name = ready.shift();
662
+ ordered.push(name);
663
+ for (const target of edges.get(name) ?? []) {
664
+ if (!incoming.has(target))
665
+ continue;
666
+ const remaining = incoming.get(target) - 1;
667
+ incoming.set(target, remaining);
668
+ if (remaining === 0)
669
+ ready.push(target);
410
670
  }
411
- if (!valid || body?.type !== 'ObjectExpression') {
412
- report(property.value, 'function-style', 'Function style keys need identifier parameters and an object return value.');
671
+ }
672
+ const stuck = new Set(groups
673
+ .map((group) => group.name)
674
+ .filter((name) => !ordered.includes(name)));
675
+ return { ordered, stuck, edges, blame };
676
+ };
677
+ let sorted = sort();
678
+ while (sorted.stuck.size > 0) {
679
+ const offender = [...collapsible].find((entry) => !inlined.has(entry) &&
680
+ entry.parts.filter((part) => sorted.stuck.has(part.className)).length >
681
+ 1);
682
+ if (!offender)
683
+ break;
684
+ inlined.add(offender);
685
+ sorted = sort();
686
+ }
687
+ const order = new Map([...sorted.ordered, ...sorted.stuck].map((name, index) => [name, index]));
688
+ const overrides = {};
689
+ const pending = [];
690
+ for (const entry of resolved) {
691
+ if (inlined.has(entry))
692
+ continue;
693
+ if (entry.slots.some((slot) => slot.length !== 1))
694
+ continue;
695
+ const names = entry.slots.map((slot) => slot[0]);
696
+ const signature = (0, exports.compositionSignature)(names);
697
+ if (overrides[signature])
698
+ continue;
699
+ const perSlot = {};
700
+ for (let slot = 1; slot < names.length; slot++) {
701
+ const losers = names
702
+ .slice(0, slot)
703
+ .filter((earlier) => earlier !== names[slot] &&
704
+ conflicts(earlier, names[slot]) &&
705
+ order.get(earlier) > order.get(names[slot]));
706
+ if (losers.length === 0)
413
707
  continue;
708
+ pending.push({ signature, slot, winner: names[slot], losers });
709
+ perSlot[slot] = '';
710
+ }
711
+ if (Object.keys(perSlot).length > 0)
712
+ overrides[signature] = perSlot;
713
+ }
714
+ for (const request of pending) {
715
+ const winner = members.get([...members.keys()].find((key) => members.get(key)?.className === request.winner));
716
+ if (!winner)
717
+ continue;
718
+ const disputed = new Map();
719
+ const mine = footprints.get(request.winner);
720
+ for (const loser of request.losers) {
721
+ const theirs = footprints.get(loser);
722
+ if (!mine || !theirs)
723
+ continue;
724
+ for (const [bucket, properties] of mine) {
725
+ const other = theirs.get(bucket);
726
+ if (!other)
727
+ continue;
728
+ for (const property of properties) {
729
+ if (!other.has(property))
730
+ continue;
731
+ const held = disputed.get(bucket) ?? new Set();
732
+ held.add(property);
733
+ disputed.set(bucket, held);
734
+ }
735
+ }
736
+ }
737
+ if (disputed.size === 0)
738
+ continue;
739
+ const name = classNameFor(binding, camelJoin([request.winner, `over-${request.losers.join('-')}`]));
740
+ taken.add(name);
741
+ openGroup(name);
742
+ emit(winner.value, `.${name}:not(#\\#)`, [], values, 'both', undefined, disputed);
743
+ keys.push(name);
744
+ overrides[request.signature][request.slot] = name;
745
+ }
746
+ for (const entry of resolved) {
747
+ if (inlined.has(entry))
748
+ continue;
749
+ const signature = entry.slots.every((slot) => slot.length === 1) &&
750
+ (0, exports.compositionSignature)(entry.slots.map((slot) => slot[0]));
751
+ let unresolved;
752
+ for (let first = 0; first < entry.slots.length && !unresolved; first++) {
753
+ for (let second = first + 1; second < entry.slots.length; second++) {
754
+ if (signature && overrides[signature]?.[second])
755
+ continue;
756
+ for (const before of entry.slots[first]) {
757
+ for (const after of entry.slots[second]) {
758
+ if (before === after || !conflicts(before, after))
759
+ continue;
760
+ if (order.get(before) < order.get(after))
761
+ continue;
762
+ unresolved = [before, after];
763
+ }
764
+ }
414
765
  }
415
- const variables = params.map((parameter) => `--${(0, zss_engine_1.camelToKebabCase)(binding)}-${(0, zss_engine_1.camelToKebabCase)(key)}-${(0, zss_engine_1.camelToKebabCase)(parameter)}`);
416
- const scope = new Map(values);
417
- params.forEach((parameter, index) => {
418
- scope.set(parameter, `var(${variables[index]})`);
419
- });
420
- functions[key] = { params, variables };
421
- keys.push(key);
422
- emit(body, `.${key}`, [], scope);
766
+ }
767
+ if (!unresolved)
423
768
  continue;
769
+ reports.push({
770
+ line: entry.composition.line,
771
+ column: entry.composition.column,
772
+ kind: 'composition-order',
773
+ hint: `\`${unresolved[1]}\` has to win over \`${unresolved[0]}\` here and lose elsewhere, and this call site cannot carry an override.`,
774
+ });
775
+ }
776
+ for (const entry of collapsible) {
777
+ const signature = (0, exports.compositionSignature)(entry.parts.map((part) => part.className));
778
+ if (merges[signature])
779
+ continue;
780
+ const merged = classNameFor(binding, camelJoin(entry.parts.map((part) => part.className)));
781
+ taken.add(merged);
782
+ openGroup(merged);
783
+ if (inlined.has(entry)) {
784
+ for (const part of entry.parts)
785
+ emit(part.value, `.${merged}`, [], values, 'declarations');
786
+ for (const part of entry.parts)
787
+ emit(part.value, `.${merged}`, [], values, 'nested');
788
+ }
789
+ else {
790
+ rules.push(`.${merged} {\n composes: ${entry.parts
791
+ .map((part) => part.className)
792
+ .join(' ')};\n}`);
424
793
  }
425
- keys.push(key);
426
- emit(property.value, `.${key}`);
794
+ keys.push(merged);
795
+ merges[signature] = merged;
427
796
  }
797
+ const emitted = [
798
+ ...sorted.ordered,
799
+ ...[...sorted.stuck],
800
+ ...groups.slice(position.size).map((group) => group.name),
801
+ ];
802
+ const seen = new Set();
803
+ const css = emitted.flatMap((name) => {
804
+ if (seen.has(name))
805
+ return [];
806
+ seen.add(name);
807
+ return groups.find((group) => group.name === name)?.rules ?? [];
808
+ });
428
809
  return {
429
810
  binding,
430
- css: rules.length > 0 ? `${rules.join('\n\n')}\n` : '',
811
+ css: css.length > 0 ? `${css.join('\n\n')}\n` : '',
431
812
  globalCss: `${themes.globalCss}${animations.globalCss}`,
432
813
  keys,
433
814
  functions,
815
+ aliases,
816
+ merges,
817
+ overrides,
818
+ definitionOnly: creates.every((create) => !bindsElsewhere(ast, create.binding, create.declaration.id)),
434
819
  reports,
435
820
  };
436
821
  }
@@ -1,16 +1,24 @@
1
1
  import type { Rule } from 'eslint';
2
2
  export interface ReleaseModule {
3
3
  source: string;
4
+ target?: string;
4
5
  binding: string;
6
+ definitionOnly?: boolean;
7
+ aliases?: Record<string, Record<string, string>>;
5
8
  functions?: Record<string, {
6
9
  params: string[];
7
10
  variables: string[];
8
11
  }>;
12
+ merges?: Record<string, string>;
13
+ overrides?: Record<string, Record<number, string>>;
9
14
  }
10
15
  export interface ReleaseStylesOptions {
11
16
  modules: Record<string, ReleaseModule>;
12
17
  themes?: Record<string, string[]>;
13
18
  animations?: Record<string, string[]>;
19
+ statics?: Record<string, string[]>;
20
+ values?: Record<string, Record<string, unknown>>;
21
+ complete?: boolean;
14
22
  styleProp?: string;
15
23
  active?: boolean;
16
24
  }