bestax-migrate 2.4.0 → 2.5.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.
@@ -8,9 +8,9 @@
8
8
  * it: each root, modifier and helper here is rendered through the built
9
9
  * library and compared with the raw markup it replaces.
10
10
  *
11
- * Data only, and written so a generator in another package can import it
12
- * directly: nothing Node cannot strip, so no enums, no namespaces, type
13
- * imports only.
11
+ * Data only: `scripts/gen-mcp-index.mjs` imports it directly for the MCP
12
+ * server's `lookup_bulma_classes`, so nothing Node cannot strip (no enums, no
13
+ * namespaces, type imports only), and a change to it wants `pnpm gen:mcp`.
14
14
  */
15
15
  export interface PropWrite {
16
16
  readonly prop: string;
@@ -37,7 +37,10 @@ export interface Modifier {
37
37
  export type RootStatus = 'mapped' | 'todo' | 'plain';
38
38
  export interface RootEntry {
39
39
  readonly status: RootStatus;
40
- /** bestax JSX name, dotted for a part (`Hero.Body`). */
40
+ /**
41
+ * bestax JSX name, dotted for a part (`Hero.Body`). On a `todo` family, the
42
+ * component a person converts the family to by hand.
43
+ */
41
44
  readonly target?: string;
42
45
  /** The tag the target renders with no `as`. */
43
46
  readonly tag?: string;
@@ -60,6 +63,17 @@ export interface RootEntry {
60
63
  readonly untypedAttrs?: readonly string[];
61
64
  /** The target's props type requires children. */
62
65
  readonly requiresChildren?: boolean;
66
+ /**
67
+ * The target renders its children inside an element of its own (`Card`
68
+ * inside `.card-content`) unless one of them is one of these parts, so the
69
+ * element converts only beside a direct child that converts to one.
70
+ * `whenEmpty`: it renders that element with no children too.
71
+ */
72
+ readonly wrapsChildren?: {
73
+ readonly in: string;
74
+ readonly unless: readonly string[];
75
+ readonly whenEmpty?: boolean;
76
+ };
63
77
  /**
64
78
  * Attributes the target types as numbers. A numeric string (`value="40"`)
65
79
  * becomes a number, which renders the same; any other string refuses.
@@ -78,6 +92,13 @@ export interface RootEntry {
78
92
  readonly part?: boolean;
79
93
  /** Why a `todo` or `plain` root is what it is; `todo` messages quote it. */
80
94
  readonly why?: string;
95
+ /**
96
+ * Modifiers left out of `modifiers` on purpose, because the prop that
97
+ * renders the class renders more than the class. They stay in `className`;
98
+ * the reason is for the MCP lookup, which would otherwise say bestax has
99
+ * no prop for them.
100
+ */
101
+ readonly omits?: Readonly<Record<string, string>>;
81
102
  }
82
103
  export declare const COLORS: readonly ["primary", "link", "info", "success", "warning", "danger", "black", "black-bis", "black-ter", "grey-darker", "grey-dark", "grey", "grey-light", "grey-lighter", "white", "white-bis", "white-ter", "light", "dark"];
83
104
  export declare const SIZES: readonly ["0", "1", "2", "3", "4", "5", "6", "auto"];
@@ -1 +1 @@
1
- {"version":3,"file":"class-map.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/class-map.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"class-map.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/class-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,MAAM,GAChB;IAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAE9E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;IACxC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,IAAI,CAAC;IAClD,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAAC;IACxD,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE;QACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;QACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;KAC9B,CAAC;IACF;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAID,eAAO,MAAM,MAAM,+NAoBT,CAAC;AACX,eAAO,MAAM,KAAK,sDAAuD,CAAC;AAC1E,eAAO,MAAM,UAAU,8CAA+C,CAAC;AACvE,eAAO,MAAM,UAAU,qDAAsD,CAAC;AAC9E,eAAO,MAAM,eAAe,8DAKlB,CAAC;AACX,eAAO,MAAM,YAAY,4DAMf,CAAC;AACX,eAAO,MAAM,aAAa,sEAMhB,CAAC;AACX,eAAO,MAAM,QAAQ,6EAOX,CAAC;AACX,eAAO,MAAM,eAAe,6DAKlB,CAAC;AACX,eAAO,MAAM,UAAU,6CAA8C,CAAC;AACtE,eAAO,MAAM,gBAAgB,iIAWnB,CAAC;AACX,eAAO,MAAM,cAAc,2GAQjB,CAAC;AACX,eAAO,MAAM,WAAW,sFAQd,CAAC;AACX,eAAO,MAAM,WAAW,8EAOd,CAAC;AACX,eAAO,MAAM,gBAAgB,yCAA0C,CAAC;AACxE,eAAO,MAAM,SAAS,6HAUZ,CAAC;AAoDX,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD;AAuDD,eAAO,MAAM,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAkxBrD,CAAC;AAcF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EAwCvC,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAAuB,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYrD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAYzE,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtD,CAAC;AAIF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,gBAAgB,GAChB,WAAW,GACX,OAAO,CAAC;AAEZ,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B;AA6DD,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAsGzD,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAS3C,CAAC;AAIH;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CA8BA,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnE;AAiBD,qCAAqC;AACrC,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAE5D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAGlE;AAED,qCAAqC;AACrC,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,GACZ,QAAQ,GAAG,SAAS,CAEtB;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5D;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1D"}
@@ -8,9 +8,9 @@
8
8
  * it: each root, modifier and helper here is rendered through the built
9
9
  * library and compared with the raw markup it replaces.
10
10
  *
11
- * Data only, and written so a generator in another package can import it
12
- * directly: nothing Node cannot strip, so no enums, no namespaces, type
13
- * imports only.
11
+ * Data only: `scripts/gen-mcp-index.mjs` imports it directly for the MCP
12
+ * server's `lookup_bulma_classes`, so nothing Node cannot strip (no enums, no
13
+ * namespaces, type imports only), and a change to it wants `pnpm gen:mcp`.
14
14
  */
15
15
  export function inTagSet(set, tag) {
16
16
  return 'on' in set ? set.on.includes(tag) : !set.except.includes(tag);
@@ -246,7 +246,9 @@ export const ROOTS = {
246
246
  'is-active': 'isActive',
247
247
  'is-hovered': 'isHovered',
248
248
  }),
249
- // No `is-disabled`: `isDisabled` writes attributes as well as the class.
249
+ },
250
+ omits: {
251
+ 'is-disabled': 'bestax `Button` renders it through `isDisabled`, which also disables the element (`disabled`, or `aria-disabled` on a link)',
250
252
  },
251
253
  dropsAttr: {
252
254
  // Kept only on the elements `disabled` means something on.
@@ -318,6 +320,86 @@ export const ROOTS = {
318
320
  'size',
319
321
  ],
320
322
  },
323
+ // `Card` checks its children for a part (`hasCompoundComponents` in
324
+ // bulma-ui's Card.tsx), and `Card.Header` for a `Card.Header.Title`
325
+ // (`hasHeaderTitle`), and wraps them when there is none.
326
+ card: {
327
+ ...BASE,
328
+ target: 'Card',
329
+ tag: 'div',
330
+ wrapsChildren: {
331
+ in: 'card-content',
332
+ unless: [
333
+ 'Card.Header',
334
+ 'Card.Header.Icon',
335
+ 'Card.Image',
336
+ 'Card.Content',
337
+ 'Card.Footer',
338
+ 'Card.FooterItem',
339
+ ],
340
+ },
341
+ ownProps: [
342
+ 'textColor',
343
+ 'color',
344
+ 'bgColor',
345
+ 'hasShadow',
346
+ 'header',
347
+ 'headerCentered',
348
+ 'headerIcon',
349
+ 'footer',
350
+ 'image',
351
+ 'imageAlt',
352
+ ],
353
+ },
354
+ 'card-header': {
355
+ ...BASE,
356
+ target: 'Card.Header',
357
+ tag: 'header',
358
+ wrapsChildren: {
359
+ in: 'card-header-title',
360
+ unless: ['Card.Header.Title'],
361
+ whenEmpty: true,
362
+ },
363
+ ownProps: ['color', 'bgColor', 'textColor', 'centered'],
364
+ },
365
+ 'card-header-title': {
366
+ ...BASE,
367
+ target: 'Card.Header.Title',
368
+ tag: 'div',
369
+ modifiers: flags({ 'is-centered': 'centered' }),
370
+ ownProps: ['color', 'bgColor', 'textColor', 'centered'],
371
+ },
372
+ 'card-header-icon': {
373
+ ...BASE,
374
+ target: 'Card.Header.Icon',
375
+ tag: 'button',
376
+ defaults: { 'aria-label': 'more options' },
377
+ ownProps: ['color', 'bgColor', 'textColor'],
378
+ },
379
+ 'card-image': {
380
+ ...BASE,
381
+ target: 'Card.Image',
382
+ tag: 'div',
383
+ ownProps: ['color', 'bgColor', 'textColor'],
384
+ },
385
+ 'card-content': {
386
+ ...BASE,
387
+ target: 'Card.Content',
388
+ tag: 'div',
389
+ ownProps: ['color', 'bgColor', 'textColor'],
390
+ },
391
+ 'card-footer': {
392
+ ...BASE,
393
+ target: 'Card.Footer',
394
+ tag: 'footer',
395
+ ownProps: ['color', 'bgColor', 'textColor'],
396
+ },
397
+ 'card-footer-item': {
398
+ ...BASE,
399
+ target: 'Card.FooterItem',
400
+ tag: 'span',
401
+ ownProps: ['color', 'bgColor', 'textColor'],
402
+ },
321
403
  columns: {
322
404
  ...BASE,
323
405
  target: 'Columns',
@@ -656,7 +738,9 @@ export const ROOTS = {
656
738
  'is-rounded': 'isRounded',
657
739
  'is-hoverable': 'isHoverable',
658
740
  }),
659
- // No `is-delete`: `isDelete` turns the tag into a <button>.
741
+ },
742
+ omits: {
743
+ 'is-delete': 'bestax `Tag` renders it through `isDelete`, which also turns the tag into a <button>',
660
744
  },
661
745
  ownProps: [
662
746
  'color',
@@ -698,51 +782,43 @@ export const ROOTS = {
698
782
  ownProps: ['size', 'isSpaced', 'as', 'hasSkeleton', 'textColor', 'bgColor'],
699
783
  },
700
784
  // ---- Families bestax has but that do not map element by element yet ------
701
- breadcrumb: todo('bestax `Breadcrumb` renders the `<ul>` itself and adds an `aria-label`'),
702
- card: todo('bestax `Card` wraps any child that is not one of its parts in `.card-content`, so convert the card and its parts together, by hand'),
703
- 'card-header': part(),
704
- 'card-header-title': part(),
705
- 'card-header-icon': part(),
706
- 'card-image': part(),
707
- 'card-content': part(),
708
- 'card-footer': part(),
709
- 'card-footer-item': part(),
710
- cell: todo('this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
711
- grid: todo('this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
712
- 'fixed-grid': todo('this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
713
- checkbox: todo("bestax `Checkbox` renders its own styled markup, not Bulma's"),
714
- checkboxes: todo('bestax `Checkboxes` renders its own `.field` and `.control` wrappers'),
715
- radio: todo("bestax `Radio` renders its own styled markup, not Bulma's"),
716
- radios: todo('bestax `Radios` renders its own `.field` and `.control` wrappers'),
717
- field: todo('bestax form controls render their own `.field` and `.control` wrappers; see the unmappables reference'),
785
+ breadcrumb: todo('Breadcrumb', 'bestax `Breadcrumb` renders the `<ul>` itself and adds an `aria-label`'),
786
+ cell: todo('Cell', 'this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
787
+ grid: todo('Grid', 'this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
788
+ 'fixed-grid': todo('Grid', 'this source leaves Grid markup as written; convert it to bestax `Grid` and `Cell` by hand (`Grid isFixed` renders the `.fixed-grid` wrapper itself)'),
789
+ checkbox: todo('Checkbox', "bestax `Checkbox` renders its own styled markup, not Bulma's"),
790
+ checkboxes: todo('Checkboxes', 'bestax `Checkboxes` renders its own `.field` and `.control` wrappers'),
791
+ radio: todo('Radio', "bestax `Radio` renders its own styled markup, not Bulma's"),
792
+ radios: todo('Radios', 'bestax `Radios` renders its own `.field` and `.control` wrappers'),
793
+ field: todo('Field', 'bestax form controls render their own `.field` and `.control` wrappers; see the unmappables reference'),
718
794
  'field-label': part(),
719
795
  'field-body': part(),
720
796
  control: part(),
721
- input: todo('bestax `Input` renders its own `.field` and `.control` wrappers'),
722
- textarea: todo('bestax `TextArea` renders its own `.field` and `.control` wrappers'),
723
- select: todo('bestax `Select` renders the `.select` wrapper and the `<select>` together'),
724
- file: todo('bestax `File` renders the whole `.file-label` tree itself'),
797
+ input: todo('Input', 'bestax `Input` renders its own `.field` and `.control` wrappers'),
798
+ textarea: todo('TextArea', 'bestax `TextArea` renders its own `.field` and `.control` wrappers'),
799
+ select: todo('Select', 'bestax `Select` renders the `.select` wrapper and the `<select>` together'),
800
+ file: todo('File', 'bestax `File` renders the whole `.file-label` tree itself'),
725
801
  'file-label': part(),
726
802
  'file-input': part(),
727
803
  'file-cta': part(),
728
804
  'file-icon': part(),
729
805
  'file-name': part(),
730
- dropdown: todo('bestax `Dropdown` renders its own trigger and menu from props'),
806
+ dropdown: todo('Dropdown', 'bestax `Dropdown` renders its own trigger and menu from props'),
731
807
  'dropdown-menu': part(),
732
808
  'dropdown-content': part(),
733
809
  'dropdown-item': part(),
734
810
  'dropdown-divider': part(),
735
- icon: todo('bestax `Icon` renders its own `<i>` and adds an `aria-label`'),
736
- 'icon-text': todo('bestax `IconText` pairs with `Icon`, which renders its own `<i>`'),
737
- image: todo('bestax `Image` renders its own `<img>`'),
738
- menu: todo('bestax `Menu.Item` renders the `<li>` and the `<a>` together'),
811
+ icon: todo('Icon', 'bestax `Icon` renders its own `<i>` and adds an `aria-label`'),
812
+ 'icon-text': todo('IconText', 'bestax `IconText` pairs with `Icon`, which renders its own `<i>`'),
813
+ image: todo('Image', 'bestax `Image` renders its own `<img>`'),
814
+ menu: todo('Menu', 'bestax `Menu.Item` renders the `<li>` and the `<a>` together'),
739
815
  'menu-label': part(),
740
816
  'menu-list': part(),
741
817
  'menu-item': part(),
742
- message: todo('bestax `Message` always wraps its children in `.message-body`'),
818
+ message: todo('Message', 'bestax `Message` always wraps its children in `.message-body`'),
743
819
  'message-header': part(),
744
820
  'message-body': part(),
745
- modal: todo('bestax `Modal` renders its own background and content parts, and adds dialog attributes'),
821
+ modal: todo('Modal', 'bestax `Modal` renders its own background and content parts, and adds dialog attributes'),
746
822
  'modal-background': part(),
747
823
  'modal-content': part(),
748
824
  'modal-card': part(),
@@ -751,7 +827,7 @@ export const ROOTS = {
751
827
  'modal-card-body': part(),
752
828
  'modal-card-foot': part(),
753
829
  'modal-close': part(),
754
- navbar: todo('bestax `Navbar` adds navigation roles, and its burger renders its own spans'),
830
+ navbar: todo('Navbar', 'bestax `Navbar` adds navigation roles, and its burger renders its own spans'),
755
831
  'navbar-brand': part(),
756
832
  'navbar-burger': part(),
757
833
  'navbar-menu': part(),
@@ -763,22 +839,22 @@ export const ROOTS = {
763
839
  'navbar-divider': part(),
764
840
  'navbar-content': part(),
765
841
  'navbar-tabs': part(),
766
- pagination: todo('bestax `Pagination` renders its own list items and adds navigation roles'),
842
+ pagination: todo('Pagination', 'bestax `Pagination` renders its own list items and adds navigation roles'),
767
843
  'pagination-list': part(),
768
844
  'pagination-link': part(),
769
845
  'pagination-ellipsis': part(),
770
846
  'pagination-previous': part(),
771
847
  'pagination-next': part(),
772
- panel: todo('bestax `Panel` parts render their own tags and attributes (`Panel.Block` is an `<a>`, `Panel.Icon` adds an `aria-label`)'),
848
+ panel: todo('Panel', 'bestax `Panel` parts render their own tags and attributes (`Panel.Block` is an `<a>`, `Panel.Icon` adds an `aria-label`)'),
773
849
  'panel-heading': part(),
774
850
  'panel-tabs': part(),
775
851
  'panel-block': part(),
776
852
  'panel-icon': part(),
777
853
  'panel-list': part(),
778
- tabs: todo("bestax `Tabs` renders each tab's `<li>` and `<a>` together, with tab roles"),
779
- 'table-container': todo('bestax renders `.table-container` from `Table isResponsive`'),
780
- 'skeleton-block': todo('bestax `Skeleton` renders its own markup'),
781
- 'skeleton-lines': todo('bestax `Skeleton` renders its own markup'),
854
+ tabs: todo('Tabs', "bestax `Tabs` renders each tab's `<li>` and `<a>` together, with tab roles"),
855
+ 'table-container': todo('Table', 'bestax renders `.table-container` from `Table isResponsive`'),
856
+ 'skeleton-block': todo('Skeleton', 'bestax `Skeleton` renders its own markup'),
857
+ 'skeleton-lines': todo('Skeleton', 'bestax `Skeleton` renders its own markup'),
782
858
  // ---- Valid Bulma, nothing to convert to --------------------------------------
783
859
  // The other sources emit these on purpose where bestax has no component.
784
860
  help: plain('bestax renders `.help` only inside its form controls'),
@@ -792,8 +868,8 @@ export const ROOTS = {
792
868
  marginless: plain('a Bulma helper with no bestax prop'),
793
869
  paddingless: plain('a Bulma helper with no bestax prop'),
794
870
  };
795
- function todo(why) {
796
- return { status: 'todo', why };
871
+ function todo(target, why) {
872
+ return { status: 'todo', target, why };
797
873
  }
798
874
  function part() {
799
875
  return { status: 'todo', part: true };
@@ -824,6 +900,15 @@ export const PRECEDENCE = [
824
900
  'media-left',
825
901
  'media-content',
826
902
  'media-right',
903
+ // A part before a surface class, so the card around it still sees a part.
904
+ 'card',
905
+ 'card-header',
906
+ 'card-header-title',
907
+ 'card-header-icon',
908
+ 'card-image',
909
+ 'card-content',
910
+ 'card-footer',
911
+ 'card-footer-item',
827
912
  'buttons',
828
913
  'tags',
829
914
  'button',
@@ -22,6 +22,12 @@ export interface ElementFacts {
22
22
  hasRef: boolean;
23
23
  /** Whether the element has children (JSX text, elements or expressions). */
24
24
  hasChildren: boolean;
25
+ /**
26
+ * The bestax components the element's child elements are, or convert to
27
+ * (`Card.Content`). Only a child written directly inside the element
28
+ * counts: one in an expression may not render.
29
+ */
30
+ childTargets?: readonly string[];
25
31
  /**
26
32
  * The component this element is the only child of, if any (`Link` for
27
33
  * `<Link href="/x"><a className="button">`), since that component can
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACtD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACnD,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACnB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AA+BD,wBAAgB,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAgP9C"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACtD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACnD,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACnB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AA+BD,wBAAgB,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAiQ9C"}
@@ -48,7 +48,7 @@ export function plan(facts) {
48
48
  todos.push({ rule: ruleId('legacy', token), message: hint });
49
49
  }
50
50
  // A family this source leaves as markup keeps its element as markup, even
51
- // beside a class it would convert (`card box`): the family's parts carry no
51
+ // beside a class it would convert (`navbar box`): the family's parts carry no
52
52
  // TODO of their own, so its outermost class is the one place it is flagged.
53
53
  const family = tokens.find(token => {
54
54
  const found = rootFor(token);
@@ -128,6 +128,16 @@ export function plan(facts) {
128
128
  if (entry.requiresChildren && !facts.hasChildren) {
129
129
  return refuse('children', target, `bestax \`${target}\` requires children, and this element has none; keep it as markup`);
130
130
  }
131
+ const wraps = entry.wrapsChildren;
132
+ if (wraps &&
133
+ (facts.hasChildren || wraps.whenEmpty) &&
134
+ !facts.childTargets?.some(child => wraps.unless.includes(child))) {
135
+ const parts = wraps.unless.map(part => `\`${part}\``);
136
+ const list = parts.length > 1
137
+ ? `${parts.slice(0, -1).join(', ')} or ${parts[parts.length - 1]}`
138
+ : parts[0];
139
+ return refuse('children', target, `bestax \`${target}\` renders its children inside a \`.${wraps.in}\` of its own unless one of them is a ${list}, so this element stays markup`);
140
+ }
131
141
  if (missing.length > 0) {
132
142
  const list = missing.map(([name, value]) => `\`${name}="${value}"\``);
133
143
  return refuse('defaults', target, `bestax \`${target}\` renders ${list.join(' and ')} when the element does not set ${missing.length === 1 ? 'it' : 'them'}; add ${missing.length === 1 ? 'it' : 'them'} here if that is what you want, then re-run`);
@@ -8,8 +8,9 @@
8
8
  *
9
9
  * Passes, in order:
10
10
  * 1. the shared stylesheet-import pass (a no-op under the default `keep`)
11
- * 2. plan every element in scope; a computed className is planned from the
12
- * strings in it and becomes a TODO, never a conversion
11
+ * 2. plan every element in scope, children before parents; a computed
12
+ * className is planned from the strings in it and becomes a TODO, never
13
+ * a conversion
13
14
  * 3. file-level gates: a non-React JSX runtime, CommonJS, styled-jsx, and a
14
15
  * Next.js server component (bestax's components are client components)
15
16
  * 4. TODOs in reading order; conversions innermost first
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAuRvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA4QpB"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAmSvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA2SpB"}
@@ -8,8 +8,9 @@
8
8
  *
9
9
  * Passes, in order:
10
10
  * 1. the shared stylesheet-import pass (a no-op under the default `keep`)
11
- * 2. plan every element in scope; a computed className is planned from the
12
- * strings in it and becomes a TODO, never a conversion
11
+ * 2. plan every element in scope, children before parents; a computed
12
+ * className is planned from the strings in it and becomes a TODO, never
13
+ * a conversion
13
14
  * 3. file-level gates: a non-React JSX runtime, CommonJS, styled-jsx, and a
14
15
  * Next.js server component (bestax's components are client components)
15
16
  * 4. TODOs in reading order; conversions innermost first
@@ -17,7 +18,7 @@
17
18
  */
18
19
  import path from 'node:path';
19
20
  import { addTodo, findAttr, forgetJsxParens, jsxNameParts, literalValueOf, makeAttr, renameElement, reprintDirectives, } from '../_shared/jsx-utils.js';
20
- import { collectBoundNames, makeReserve, prefersTabs, } from '../_shared/imports.js';
21
+ import { collectBoundNames, makeReserve, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
21
22
  import { rewriteStylesheetImports } from '../_shared/css-imports.js';
22
23
  import { BESTAX, buildBestaxImport, placeBestaxImport, seedBestaxImport, } from '../_shared/bestax-import.js';
23
24
  import { plan } from './plan.js';
@@ -217,6 +218,17 @@ function isContent(child) {
217
218
  }
218
219
  return true;
219
220
  }
221
+ /**
222
+ * Whether a JSX child reaches React at all. JSX drops a comment, and text
223
+ * made only of spaces and tabs split by a line break; it keeps the rest.
224
+ * `<div> </div>` has a child, which `Card` would wrap, and so does an
225
+ * `&nbsp;` on its own line: `trim()` would drop it, and JSX does not.
226
+ */
227
+ function reachesReact(child) {
228
+ if (child.type !== 'JSXText')
229
+ return isContent(child);
230
+ return /[^ \t\r\n]/.test(child.value) || !/[\r\n]/.test(child.value);
231
+ }
220
232
  function insideForeignContent(elementPath) {
221
233
  let current = elementPath.parent;
222
234
  while (current) {
@@ -278,29 +290,55 @@ export default function transform(fileInfo, api, options = {}) {
278
290
  // every class a converted element renders.
279
291
  rewriteStylesheetImports(ctx, root, options.cssMode ?? 'keep');
280
292
  // ---- 2. Plan the elements in scope ----------------------------------------
281
- const bestaxLocals = new Set();
293
+ // Each local bestax name, with the component path it stands for: `Card`
294
+ // for `import { Card as C }`, nothing for a namespace (`B.Card`).
295
+ const bestaxNames = new Map();
282
296
  root
283
297
  .find(j.ImportDeclaration, { source: { value: BESTAX } })
284
298
  .forEach((importPath) => {
285
299
  for (const spec of importPath.node.specifiers ?? []) {
286
- if (spec.local)
287
- bestaxLocals.add(spec.local.name);
300
+ if (!spec.local)
301
+ continue;
302
+ bestaxNames.set(spec.local.name, spec.type === 'ImportSpecifier' ? [spec.imported.name] : []);
288
303
  }
289
304
  });
305
+ const bestaxLocals = new Set(bestaxNames.keys());
306
+ // Children are planned before their parents (reverse document order), so
307
+ // a parent can see what its children become: `Card` converts only beside
308
+ // a child that is one of its parts.
309
+ const planned = new Map();
310
+ const programScope = root.find(j.Program).paths()[0]?.scope;
311
+ const childTargets = (elementPath) => (elementPath.node.children ?? []).flatMap((child) => {
312
+ if (child.type !== 'JSXElement')
313
+ return [];
314
+ const target = planned.get(child)?.conversion?.target;
315
+ if (target)
316
+ return [target];
317
+ // A part the file already uses counts when its name is the bestax
318
+ // import, not a local that shadows it. A direct child is looked up in
319
+ // the element's own scope, which is the child's.
320
+ const parts = jsxNameParts(child.openingElement.name);
321
+ const owner = parts && bestaxNames.get(parts[0]);
322
+ if (!owner || !resolvesToBinding(elementPath, parts[0], programScope)) {
323
+ return [];
324
+ }
325
+ return [[...owner, ...parts.slice(1)].join('.')];
326
+ });
290
327
  // `converts` is whether the element would become a component with its
291
328
  // classes written out, so it holds for a computed className too.
292
329
  const elements = [];
293
- root.find(j.JSXElement).forEach(elementPath => {
330
+ const candidates = root.find(j.JSXElement).paths();
331
+ for (const elementPath of [...candidates].reverse()) {
294
332
  const element = elementPath.node;
295
333
  const name = element.openingElement.name;
296
334
  if (name.type !== 'JSXIdentifier' || !INTRINSIC.test(name.name))
297
- return;
335
+ continue;
298
336
  if (FOREIGN_ROOTS.has(name.name) || insideForeignContent(elementPath)) {
299
- return;
337
+ continue;
300
338
  }
301
339
  const classAttr = findAttr(element, 'className');
302
340
  if (!classAttr)
303
- return;
341
+ continue;
304
342
  const attributes = new Map();
305
343
  for (const attr of element.openingElement.attributes ?? []) {
306
344
  if (attr.type !== 'JSXAttribute' || attr === classAttr)
@@ -316,7 +354,8 @@ export default function transform(fileInfo, api, options = {}) {
316
354
  attributes,
317
355
  hasSpread: (element.openingElement.attributes ?? []).some((attr) => attr.type === 'JSXSpreadAttribute'),
318
356
  hasRef: attributes.has('ref'),
319
- hasChildren: (element.children ?? []).some(isContent),
357
+ hasChildren: (element.children ?? []).some(reachesReact),
358
+ childTargets: childTargets(elementPath),
320
359
  onlyChildOf: onlyChildOf(elementPath, bestaxLocals),
321
360
  };
322
361
  let result = plan(facts);
@@ -336,10 +375,11 @@ export default function transform(fileInfo, api, options = {}) {
336
375
  ],
337
376
  };
338
377
  }
378
+ planned.set(element, result);
339
379
  if (result.conversion || result.todos.length > 0) {
340
- elements.push({ path: elementPath, plan: result, converts });
380
+ elements.unshift({ path: elementPath, plan: result, converts });
341
381
  }
342
- });
382
+ }
343
383
  if (elements.length === 0)
344
384
  return print();
345
385
  // ---- 3. File-level gates ------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bestax-migrate",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Codemods for migrating existing React apps from other Bulma libraries to @allxsmith/bestax-bulma",
5
5
  "type": "module",
6
6
  "bin": {