bestax-migrate 2.23.1 → 2.25.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/README.md CHANGED
@@ -64,6 +64,19 @@ Anything without a safe automatic conversion is left in place with a `// TODO(be
64
64
  npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate
65
65
  ```
66
66
 
67
+ ### Through the Codemod Registry
68
+
69
+ bestax-migrate is also in the [Codemod Registry](https://app.codemod.com/registry) as `bestax-migrate`, for teams that run their migrations through Codemod. That package runs this CLI from npm at a pinned release, which can trail the newest one:
70
+
71
+ ```bash
72
+ # Preview (no writes)
73
+ npx codemod bestax-migrate --workflow preview --param source=react-bulma-components
74
+ # Apply
75
+ npx codemod bestax-migrate --param source=react-bulma-components
76
+ ```
77
+
78
+ Run it from your app root. `--param paths="src lib"` (default `src`) and `--param options="--css keep"` pass through to the CLI. Codemod's own `--dry-run` skips the shell step this package runs, so use the `preview` workflow for a dry run.
79
+
67
80
  ## Options
68
81
 
69
82
  | Flag | Description |
@@ -1 +1 @@
1
- {"version":3,"file":"polymorphic.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/polymorphic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AA0BxB,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAkB,CAAC;AAmK3E;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,GACtB,UAAU,MAAM,GAAG,SAAS,EAC5B,eAAa,KACZ,MAmBF,CAAC;AAMF,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElD;AAQD,mDAAmD;AACnD,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAW,CAAC;AAC1D,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAAuB,CAAC;AAkEpE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAkC,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAwH5C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AASD,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,0DAA0D;AAC1D,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EAAoC,CAAC;AAE7E,6CAA6C;AAC7C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAC1C,CAAC;AAqLrB;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,MAAM;AACd;;;;;;;;;GASG;AACH,gBAAgB,UAAQ,GACvB,IAAI,CAsEN"}
1
+ {"version":3,"file":"polymorphic.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/polymorphic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AA0BxB,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAkB,CAAC;AAmK3E;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,GACtB,UAAU,MAAM,GAAG,SAAS,EAC5B,eAAa,KACZ,MAmBF,CAAC;AAMF,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElD;AAQD,mDAAmD;AACnD,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAW,CAAC;AAC1D,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAAuB,CAAC;AAkEpE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAkC,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAwH5C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AASD,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,0DAA0D;AAC1D,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EAAoC,CAAC;AAE7E,6CAA6C;AAC7C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAC1C,CAAC;AAqPrB;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,MAAM;AACd;;;;;;;;;GASG;AACH,gBAAgB,UAAQ,GACvB,IAAI,CAuEN"}
@@ -592,6 +592,59 @@ function dropInertLinkAttrs(ctx, path, element, target, rendered) {
592
592
  ctx.dirty = true;
593
593
  }
594
594
  }
595
+ /**
596
+ * The targets that write `type="button"` on the <button> an `as` makes them
597
+ * render, in place of a missing `type` or one HTML does not define
598
+ * (`buttonType` in bulma-ui). A plain <button> with neither submits the form
599
+ * around it, and every source here rendered the tag it was given, so a source
600
+ * element asking for a <button> with no usable `type` was a submit button that
601
+ * the migrated one no longer is.
602
+ *
603
+ * `Button` is not here: bestax leaves its `type` to the caller, as the sources
604
+ * did. Nor are `Card.Header.Icon`, `Delete` and the other targets that always
605
+ * render a <button>. The author never asked for a <button> there, so what the
606
+ * source rendered is a question about the source library, not this element.
607
+ */
608
+ const TYPES_ITS_BUTTON = new Set([
609
+ 'Dropdown.Item',
610
+ 'Menu.Item',
611
+ 'Navbar.Item',
612
+ 'Navbar.Link',
613
+ ]);
614
+ /** The `type` values a <button> keeps under `buttonType`. */
615
+ const BUTTON_TYPES = ['button', 'submit', 'reset'];
616
+ /**
617
+ * Flag a <button> that submitted its form in the source and will not once
618
+ * migrated. Only where the element is known to be a <button> and its `type`
619
+ * is known: a dynamic `type` may hold any value, and a spread may carry one,
620
+ * and either is the author's to know. A spread decides only when nothing
621
+ * literal follows it, since JSX applies attributes last-write-wins, so a
622
+ * literal `type` written after every spread is the one that renders.
623
+ */
624
+ function flagButtonType(ctx, path, element, target, rendered) {
625
+ if (rendered !== 'button' || !TYPES_ITS_BUTTON.has(target))
626
+ return;
627
+ let attr;
628
+ let spreadWins = false;
629
+ for (const a of element.openingElement.attributes) {
630
+ if (a.type === 'JSXSpreadAttribute')
631
+ spreadWins = true;
632
+ else if (a.name.name === 'type') {
633
+ attr = a;
634
+ spreadWins = false;
635
+ }
636
+ }
637
+ if (spreadWins)
638
+ return;
639
+ if (attr) {
640
+ const literal = literalValueOf(attr);
641
+ if (literal.kind !== 'string' || BUTTON_TYPES.includes(literal.value)) {
642
+ return;
643
+ }
644
+ }
645
+ addTodo(ctx, path, 'prop:type', `bestax \`${target}\` writes \`type="button"\` on its <button> ${attr ? 'in place of a `type` HTML does not define' : 'when none is given'}, so it no longer submits the form around it the way a plain <button> does -- add \`type="submit"\` if this one should`);
646
+ ctx.dirty = true;
647
+ }
595
648
  /**
596
649
  * The `as` attribute only if no spread can overwrite it. `findAttr` reads by
597
650
  * name and knows nothing about `{...rest}`, which JSX applies last-write-wins.
@@ -677,6 +730,7 @@ spreadCanCarryAs = false) {
677
730
  ? undefined
678
731
  : HREF_OK[target];
679
732
  dropInertLinkAttrs(ctx, path, element, target, rendered);
733
+ flagButtonType(ctx, path, element, target, rendered);
680
734
  restrictAsValue(ctx, path, element, target, attr, literal);
681
735
  // Only now, and only if something element-dependent actually survived. The
682
736
  // first version fired on every `as` beside a spread -- `<Title as="h2"
@@ -49,7 +49,7 @@ export interface Modifier {
49
49
  * - `mapped`: converts to `target`.
50
50
  * - `todo`: bestax has a component, but the markup does not map element by
51
51
  * element yet; the family's outermost class gets a TODO.
52
- * - `plain`: valid Bulma with nothing to convert to; left alone, no TODO.
52
+ * - `plain`: valid Bulma, left alone with no TODO.
53
53
  * - `fold`: a wrapper the `target` renders itself from a prop, so the element
54
54
  * folds into the one `target` inside it (`.table-container` into
55
55
  * `Table isResponsive`).
@@ -250,8 +250,22 @@ export interface RootEntry {
250
250
  * and each `.icon` converts on its own; they go.
251
251
  */
252
252
  readonly buildsIcons?: boolean;
253
+ /**
254
+ * The target renders the whole `.file` tree from props (`File`): its
255
+ * `<input>`, button text, icons and file name. The element converts only
256
+ * when its tree is exactly that one, and only inside a `Field`, since
257
+ * outside one the target renders a `.field` of its own around it.
258
+ */
259
+ readonly buildsFile?: boolean;
253
260
  /** The target takes no helper props, so every helper class stays a class. */
254
261
  readonly noHelpers?: boolean;
262
+ /**
263
+ * Helper props the target does not take as helpers, each with why, so their
264
+ * classes stay classes: its props type leaves the helper out, or a prop of
265
+ * its own by that name renders something else (`Columns`' `gap` is its
266
+ * gutter). The reason is for the MCP lookup.
267
+ */
268
+ readonly helpersLeftOut?: Readonly<Record<string, string>>;
255
269
  /**
256
270
  * Tags the target doesn't render, on which the element is valid Bulma that
257
271
  * bestax renders some other way, so it stays markup with no `tag` TODO
@@ -305,6 +319,13 @@ export declare const ALIGN_CONTENTS: readonly ["flex-start", "flex-end", "center
305
319
  export declare const ALIGN_ITEMS: readonly ["stretch", "flex-start", "flex-end", "center", "baseline", "start", "end"];
306
320
  export declare const ALIGN_SELFS: readonly ["auto", "flex-start", "flex-end", "center", "baseline", "stretch"];
307
321
  export declare const FLEX_GROW_SHRINK: readonly ["0", "1", "2", "3", "4", "5"];
322
+ /** Bulma's gap scale, whole and half steps, as the gap props take it. */
323
+ export declare const GAPS: readonly ["0", "0.5", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5", "6", "6.5", "7", "7.5", "8"];
324
+ export declare const POSITIONS: readonly ["absolute", "fixed", "relative", "static", "sticky"];
325
+ /** The CSS keywords `overflow`, `overflowX` and `overflowY` take. */
326
+ export declare const AXIS_OVERFLOWS: readonly ["auto", "clip", "hidden", "scroll", "visible"];
327
+ export declare const RADII: readonly ["radiusless", "small", "normal", "large", "rounded"];
328
+ export declare const ASPECT_RATIOS: readonly ["1by1", "5by4", "4by3", "3by2", "5by3", "16by9", "2by1", "3by1", "4by5", "3by4", "2by3", "3by5", "9by16", "1by2", "1by3"];
308
329
  export declare const VIEWPORTS: readonly ["mobile", "tablet", "tablet-only", "touch", "desktop", "desktop-only", "widescreen", "widescreen-only", "fullhd"];
309
330
  /** `tablet-only` → `TabletOnly`: the suffix bestax puts on per-viewport props. */
310
331
  export declare function viewportSuffix(viewport: string): string;
@@ -342,9 +363,12 @@ export declare const LEGACY_09: Readonly<Record<string, string>>;
342
363
  /**
343
364
  * `text-color` and `background` resolve to a per-target prop (`RootEntry`'s
344
365
  * `textColor`/`bgColor`); `flex-container` only renders beside a flex
345
- * `display`; `display` cannot mix a base value with per-viewport ones.
366
+ * `display`; `display` cannot mix a base value with per-viewport ones. In
367
+ * `gap`, `position` and `overflow`, one prop decides what another renders:
368
+ * a `gap` drops `gapless`, a `pos` drops `relative`, and beside an axis prop
369
+ * `overflow` is written per axis.
346
370
  */
347
- export type HelperGroup = 'text-color' | 'background' | 'spacing' | 'typography' | 'display' | 'visibility' | 'flex-container' | 'flex-item' | 'other';
371
+ export type HelperGroup = 'text-color' | 'background' | 'spacing' | 'gap' | 'typography' | 'display' | 'visibility' | 'flex-container' | 'flex-item' | 'position' | 'overflow' | 'other';
348
372
  export interface HelperToken {
349
373
  readonly group: HelperGroup;
350
374
  readonly write: PropWrite;
@@ -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":"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"}
@@ -114,6 +114,65 @@ export const ALIGN_SELFS = [
114
114
  'stretch',
115
115
  ];
116
116
  export const FLEX_GROW_SHRINK = ['0', '1', '2', '3', '4', '5'];
117
+ /** Bulma's gap scale, whole and half steps, as the gap props take it. */
118
+ export const GAPS = [
119
+ '0',
120
+ '0.5',
121
+ '1',
122
+ '1.5',
123
+ '2',
124
+ '2.5',
125
+ '3',
126
+ '3.5',
127
+ '4',
128
+ '4.5',
129
+ '5',
130
+ '5.5',
131
+ '6',
132
+ '6.5',
133
+ '7',
134
+ '7.5',
135
+ '8',
136
+ ];
137
+ export const POSITIONS = [
138
+ 'absolute',
139
+ 'fixed',
140
+ 'relative',
141
+ 'static',
142
+ 'sticky',
143
+ ];
144
+ /** The CSS keywords `overflow`, `overflowX` and `overflowY` take. */
145
+ export const AXIS_OVERFLOWS = [
146
+ 'auto',
147
+ 'clip',
148
+ 'hidden',
149
+ 'scroll',
150
+ 'visible',
151
+ ];
152
+ export const RADII = [
153
+ 'radiusless',
154
+ 'small',
155
+ 'normal',
156
+ 'large',
157
+ 'rounded',
158
+ ];
159
+ export const ASPECT_RATIOS = [
160
+ '1by1',
161
+ '5by4',
162
+ '4by3',
163
+ '3by2',
164
+ '5by3',
165
+ '16by9',
166
+ '2by1',
167
+ '3by1',
168
+ '4by5',
169
+ '3by4',
170
+ '2by3',
171
+ '3by5',
172
+ '9by16',
173
+ '1by2',
174
+ '1by3',
175
+ ];
117
176
  export const VIEWPORTS = [
118
177
  'mobile',
119
178
  'tablet',
@@ -146,8 +205,6 @@ const COMPONENT_COLORS = [
146
205
  'light',
147
206
  'dark',
148
207
  ];
149
- /** Bulma's whole-step spacing scale, as `Grid`'s gap props take it. */
150
- const GAPS = ['0', '1', '2', '3', '4', '5', '6', '7', '8'];
151
208
  const COLUMN_SIZES = [
152
209
  '1',
153
210
  '2',
@@ -211,6 +268,8 @@ const BASE = {
211
268
  textColor: 'textColor',
212
269
  bgColor: 'bgColor',
213
270
  };
271
+ /** Why the column and row gap helpers stay classes on a `.columns`. */
272
+ const COLUMNS_AXIS_GAPS = 'bestax `Columns` leaves the column and row gap helpers out: the gutter is padding inside each column, which CSS `gap` would add to';
214
273
  // ---- Roots ------------------------------------------------------------------
215
274
  export const ROOTS = {
216
275
  block: {
@@ -387,6 +446,13 @@ export const ROOTS = {
387
446
  target: 'Card.Header.Icon',
388
447
  tag: 'button',
389
448
  defaults: { 'aria-label': 'more options' },
449
+ writesAttr: {
450
+ type: {
451
+ on: ['button'],
452
+ fallback: 'button',
453
+ keeps: ['button', 'submit', 'reset'],
454
+ },
455
+ },
390
456
  ownProps: ['color', 'bgColor', 'textColor'],
391
457
  },
392
458
  'card-image': {
@@ -486,6 +552,11 @@ export const ROOTS = {
486
552
  'gapSizeWidescreen',
487
553
  'gapSizeFullhd',
488
554
  ],
555
+ helpersLeftOut: {
556
+ gap: "bestax `Columns`' own `gap` is the columns gutter (`is-<value>`), not the gap helper",
557
+ columnGap: COLUMNS_AXIS_GAPS,
558
+ rowGap: COLUMNS_AXIS_GAPS,
559
+ },
489
560
  },
490
561
  column: {
491
562
  ...BASE,
@@ -528,8 +599,9 @@ export const ROOTS = {
528
599
  'isNarrowFullhd',
529
600
  ],
530
601
  },
531
- // Grid's gaps take a numeric string, but its column minimum and every Cell
532
- // placement are typed as numbers. The half steps (`is-gap-0.5`) have no prop.
602
+ // Grid's gaps take a numeric string, half steps included, but its column
603
+ // minimum and every Cell placement are typed as numbers. Its gap props are
604
+ // its own, and render the same classes as the gap helpers.
533
605
  grid: {
534
606
  ...BASE,
535
607
  target: 'Grid',
@@ -1021,6 +1093,13 @@ export const ROOTS = {
1021
1093
  tag: 'a',
1022
1094
  as: 'any',
1023
1095
  modifiers: flags({ 'is-active': 'active' }),
1096
+ writesAttr: {
1097
+ type: {
1098
+ on: ['button'],
1099
+ fallback: 'button',
1100
+ keeps: ['button', 'submit', 'reset'],
1101
+ },
1102
+ },
1024
1103
  ownProps: ['as', 'active', 'textColor', 'bgColor'],
1025
1104
  },
1026
1105
  'navbar-dropdown': {
@@ -1218,7 +1297,59 @@ export const ROOTS = {
1218
1297
  checkboxes: todo('Checkboxes', 'bestax `Checkboxes` renders its own `.field` and `.control` wrappers'),
1219
1298
  radio: todo('Radio', "bestax `Radio` renders its own styled markup, not Bulma's"),
1220
1299
  radios: todo('Radios', 'bestax `Radios` renders its own `.field` and `.control` wrappers'),
1221
- file: todo('File', 'bestax `File` renders the whole `.file-label` tree itself'),
1300
+ // `File` renders the whole `.file` tree from props, and puts the
1301
+ // attributes it's given on its `<input>`. `color` renders `has-text-<color>`
1302
+ // too, so a color stays a class, and `isCentered` renders nothing beside
1303
+ // `isRight`.
1304
+ file: {
1305
+ ...BASE,
1306
+ target: 'File',
1307
+ tag: 'div',
1308
+ textColor: null,
1309
+ bgColor: 'backgroundColor',
1310
+ modifiers: {
1311
+ ...tokens('is-', ['small', 'medium', 'large'], 'size'),
1312
+ ...flags({
1313
+ 'is-boxed': 'isBoxed',
1314
+ 'is-fullwidth': 'isFullwidth',
1315
+ 'is-right': 'isRight',
1316
+ }),
1317
+ // `File` renders `{hasName && fileName && …}`, so a condition handed to
1318
+ // it as it is would render a falsy number (`0`) as text.
1319
+ 'has-name': { writes: [{ prop: 'hasName' }], onlyTrue: true },
1320
+ },
1321
+ omits: {
1322
+ ...Object.fromEntries(COMPONENT_COLORS.map(color => [
1323
+ `is-${color}`,
1324
+ '`color` renders `has-text-<color>` on the `.file` as well',
1325
+ ])),
1326
+ 'is-centered': '`isCentered` renders nothing beside `isRight`',
1327
+ },
1328
+ buildsFile: true,
1329
+ ownProps: [
1330
+ 'label',
1331
+ 'labelSize',
1332
+ 'labelProps',
1333
+ 'horizontal',
1334
+ 'message',
1335
+ 'messageColor',
1336
+ 'fieldClassName',
1337
+ 'color',
1338
+ 'size',
1339
+ 'isBoxed',
1340
+ 'isFullwidth',
1341
+ 'isFullWidth',
1342
+ 'isRight',
1343
+ 'isCentered',
1344
+ 'hasName',
1345
+ 'buttonLabel',
1346
+ 'iconLeft',
1347
+ 'iconRight',
1348
+ 'inputClassName',
1349
+ 'fileName',
1350
+ ],
1351
+ passThrough: ['ref'],
1352
+ },
1222
1353
  'file-label': part(),
1223
1354
  'file-input': part(),
1224
1355
  'file-cta': part(),
@@ -1654,12 +1785,12 @@ export const ROOTS = {
1654
1785
  'has-auto-count': set('fixedCols', 'auto'),
1655
1786
  },
1656
1787
  },
1657
- // ---- Valid Bulma, nothing to convert to --------------------------------------
1658
- // The other sources emit these on purpose where bestax has no component.
1788
+ // ---- Valid Bulma, left as markup ---------------------------------------------
1789
+ // The other sources emit these on purpose.
1659
1790
  help: plain('bestax renders `.help` only inside its form controls'),
1660
1791
  'panel-list': plain('nothing in bestax renders `.panel-list`'),
1661
1792
  label: plain('bestax renders `.label` only inside its form controls'),
1662
- loader: plain("bestax `Loading` is an overlay, not Bulma's inline spinner"),
1793
+ loader: plain("bestax's `Loader` draws the same ring; swap it in by hand for its progressbar role and accessible name"),
1663
1794
  'hero-buttons': plain('a layout class inside `.hero`; bestax has no part for it'),
1664
1795
  'hero-video': plain('a layout class inside `.hero`; bestax has no part for it'),
1665
1796
  'navbar-content': plain('a layout class inside `.navbar`; bestax has no part for it'),
@@ -1788,6 +1919,7 @@ export const FORWARDS_REF = [
1788
1919
  'InputBase',
1789
1920
  'TextAreaBase',
1790
1921
  'SelectBase',
1922
+ 'File',
1791
1923
  ];
1792
1924
  /**
1793
1925
  * Plain tags bestax wraps, for an element with helper classes and no root
@@ -1880,11 +2012,20 @@ export const HELPER_TOKENS = new Map([
1880
2012
  ...helperTokens('flex-item', 'is-align-self-', ALIGN_SELFS, 'alignSelf'),
1881
2013
  ...helperTokens('flex-item', 'is-flex-grow-', FLEX_GROW_SHRINK, 'flexGrow'),
1882
2014
  ...helperTokens('flex-item', 'is-flex-shrink-', FLEX_GROW_SHRINK, 'flexShrink'),
1883
- ...helperTokens('other', 'is-pulled-', ['left', 'right'], 'float'),
2015
+ ...helperTokens('gap', 'is-gap-', GAPS, 'gap'),
2016
+ ...helperTokens('gap', 'is-column-gap-', GAPS, 'columnGap'),
2017
+ ...helperTokens('gap', 'is-row-gap-', GAPS, 'rowGap'),
2018
+ helperFlag('gap', 'is-gapless', 'gapless'),
2019
+ ...helperTokens('position', 'is-position-', POSITIONS, 'pos'),
2020
+ helperFlag('position', 'is-relative', 'relative'),
1884
2021
  [
1885
2022
  'is-clipped',
1886
- { group: 'other', write: { prop: 'overflow', value: 'clipped' } },
2023
+ { group: 'overflow', write: { prop: 'overflow', value: 'clipped' } },
1887
2024
  ],
2025
+ ...helperTokens('overflow', 'is-overflow-', AXIS_OVERFLOWS, 'overflow'),
2026
+ ...helperTokens('overflow', 'is-overflow-x-', AXIS_OVERFLOWS, 'overflowX'),
2027
+ ...helperTokens('overflow', 'is-overflow-y-', AXIS_OVERFLOWS, 'overflowY'),
2028
+ ...helperTokens('other', 'is-pulled-', ['left', 'right'], 'float'),
1888
2029
  helperFlag('other', 'is-overlay', 'overlay'),
1889
2030
  [
1890
2031
  'is-unselectable',
@@ -1898,6 +2039,7 @@ export const HELPER_TOKENS = new Map([
1898
2039
  'is-radiusless',
1899
2040
  { group: 'other', write: { prop: 'radius', value: 'radiusless' } },
1900
2041
  ],
2042
+ ...helperTokens('other', 'has-radius-', RADII.filter(radius => radius !== 'radiusless'), 'radius'),
1901
2043
  [
1902
2044
  'is-shadowless',
1903
2045
  { group: 'other', write: { prop: 'shadow', value: 'shadowless' } },
@@ -1905,7 +2047,7 @@ export const HELPER_TOKENS = new Map([
1905
2047
  ...helperTokens('other', 'is-', ['mobile', 'narrow'], 'responsive'),
1906
2048
  helperFlag('other', 'is-skeleton', 'skeleton'),
1907
2049
  helperFlag('other', 'is-clearfix', 'clearfix'),
1908
- helperFlag('other', 'is-relative', 'relative'),
2050
+ ...helperTokens('other', 'is-aspect-ratio-', ASPECT_RATIOS, 'aspectRatio'),
1909
2051
  ]);
1910
2052
  /** Every helper prop name, for the attribute-collision check. */
1911
2053
  export const HELPER_PROPS = new Set([
@@ -1936,7 +2078,8 @@ export const PASSTHROUGH = [
1936
2078
  },
1937
2079
  {
1938
2080
  why: 'a Grid or Cell class, which converts only on its own `.grid` or `.cell`, and only where bestax has a prop for it',
1939
- match: /^(?:is-(?:col|row)-.+|is-(?:column|row)-gap-\d+|is-gap-\d+|is-auto-fill)$/,
2081
+ // `is-row-gap-*` is a gap helper.
2082
+ match: /^(?:is-(?:col|row)-(?!gap-).+|is-auto-fill)$/,
1940
2083
  },
1941
2084
  {
1942
2085
  why: 'a `.fixed-grid` column count, which converts only on its own `.fixed-grid`',
@@ -1944,11 +2087,11 @@ export const PASSTHROUGH = [
1944
2087
  },
1945
2088
  {
1946
2089
  why: 'an `.image` modifier, which converts only on its own `.image`, and only where bestax has a prop for it',
1947
- match: /^(?:is-\d+by\d+|is-\d+x\d+|is-square|is-aspect-ratio-.+|has-ratio)$/,
2090
+ match: /^(?:is-\d+by\d+|is-\d+x\d+|is-square|has-ratio)$/,
1948
2091
  },
1949
2092
  {
1950
2093
  why: 'a Bulma helper with no bestax prop',
1951
- match: /^(?:is-display-.+|is-visibility-.+|is-overflow-.+|is-position-.+|is-float-.+|is-clear-.+|has-radius-.+|has-text-weight-extrabold|is-align-content-(?:baseline|start|end)|is-align-items-self-(?:start|end)|is-offset-0(?:-[a-z]+)?)$/,
2094
+ match: /^(?:is-display-.+|is-visibility-.+|is-float-.+|is-clear-.+|has-text-weight-extrabold|is-align-content-(?:baseline|start|end)|is-align-items-self-(?:start|end)|is-offset-0(?:-[a-z]+)?)$/,
1952
2095
  },
1953
2096
  {
1954
2097
  why: 'a `.breadcrumb` separator, which converts only on its own `.breadcrumb`',
@@ -129,6 +129,13 @@ export interface ChildFacts {
129
129
  becomes?: Conversion;
130
130
  soleChild?: ChildFacts;
131
131
  text?: string;
132
+ /**
133
+ * Its own `childElements`, read the same way, and whether its content is
134
+ * static: text and elements only, at least one of them rendered. What an
135
+ * entry that `buildsFile` reads, down the tree.
136
+ */
137
+ children?: readonly ChildFacts[];
138
+ staticContent?: boolean;
132
139
  }
133
140
  export interface Todo {
134
141
  rule: string;
@@ -188,7 +195,38 @@ export interface Conversion {
188
195
  * has a text, which becomes its children.
189
196
  */
190
197
  icons?: BuiltIcon[];
198
+ /**
199
+ * The target renders the element's whole tree from props (`File`): the
200
+ * `<input>`'s attributes join the element's, and the parts below become
201
+ * props. The tree goes, and the element closes itself.
202
+ */
203
+ file?: BuiltFile;
191
204
  }
205
+ /**
206
+ * The `.file` tree a target that `buildsFile` renders from props. The one
207
+ * child of the element is its `.file-label` <label>, holding the <input>,
208
+ * the `.file-cta` and at most one `.file-name`.
209
+ */
210
+ export interface BuiltFile {
211
+ /** The <input>'s classes but `file-input`, as `inputClassName`, or null. */
212
+ inputClassName: string | null;
213
+ /**
214
+ * Which of the `.file-cta`'s children holds the button text, when it isn't
215
+ * the text the target renders by default.
216
+ */
217
+ buttonLabel?: number;
218
+ /** Which of them holds the icon before that text, and after it. */
219
+ iconLeft?: number;
220
+ iconRight?: number;
221
+ /** The `.file-name`'s text. */
222
+ fileName?: string;
223
+ }
224
+ /**
225
+ * Why a target that `buildsFile` stays markup outside a `Field`, for the
226
+ * planner here and for the transform, which can see whether the `.field`
227
+ * around it converts.
228
+ */
229
+ export declare function fileContextTodo(target: string): Todo;
192
230
  /** One icon a target that `buildsIcons` builds from props. */
193
231
  export interface BuiltIcon {
194
232
  /** Which of the element's `childElements` its `.icon` is. */
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,19 @@
12
12
  import { FORWARDS_REF, HELPER_PROPS, HELPER_TOKENS, inTagSet, legacyHint, modifierFor, numberAttrsOf, placedFor, PRECEDENCE, rootFor, WRAPPER_OWN_PROPS, wrapperFor, } from './class-map.js';
13
13
  import { readGlyph } from './glyph.js';
14
14
  import { ruleId } from './rules.js';
15
+ /** The button text `File` renders when it's given none. */
16
+ const DEFAULT_FILE_LABEL = 'Choose a file\u2026';
17
+ /**
18
+ * Why a target that `buildsFile` stays markup outside a `Field`, for the
19
+ * planner here and for the transform, which can see whether the `.field`
20
+ * around it converts.
21
+ */
22
+ export function fileContextTodo(target) {
23
+ return {
24
+ rule: ruleId('context', target),
25
+ message: `bestax \`${target}\` renders a \`.field\` of its own around the \`.file\` unless it sits inside a \`Field\`, and nothing around this element is one or becomes one here; keep it as markup, or convert the \`.field\` around it, then re-run`,
26
+ };
27
+ }
15
28
  function wrapperEntry(tag) {
16
29
  const target = wrapperFor(tag);
17
30
  if (!target)
@@ -114,6 +127,16 @@ export function plan(facts) {
114
127
  if (facts.hasSpread) {
115
128
  return refuse('spread', target, `this element spreads props, which bestax \`${target}\` may read differently than the element did (a spread \`className\` merges with its classes instead of replacing them); convert it to \`${target}\` by hand`);
116
129
  }
130
+ if (entry.buildsFile) {
131
+ const own = [...facts.attributes.keys()].find(name => name !== 'key');
132
+ if (own) {
133
+ return refuse('attr', own, `bestax \`${target}\` puts the attributes it's given on its <input>, so this element's \`${own}\` would move there; move it onto the <input> if that's what you want, then re-run`);
134
+ }
135
+ if (!facts.bestaxAround?.includes('Field') &&
136
+ !facts.classesAround?.includes('field')) {
137
+ return { conversion: null, todos: [...todos, fileContextTodo(target)] };
138
+ }
139
+ }
117
140
  if (facts.hasRef && !FORWARDS_REF.includes(target)) {
118
141
  return refuse('ref', target, `bestax \`${target}\` does not forward refs, so this \`ref\` would stop reaching the DOM node; keep this element as markup`);
119
142
  }
@@ -324,6 +347,14 @@ export function plan(facts) {
324
347
  return refuse('children', target, built.why);
325
348
  icons = built.icons;
326
349
  }
350
+ let file;
351
+ if (entry.buildsFile) {
352
+ const built = buildFile(facts.childElements, entry, writes.has('hasName'));
353
+ if ('why' in built)
354
+ return refuse(built.kind, built.token, built.why);
355
+ file = built.file;
356
+ numbers.push(...built.numbers);
357
+ }
327
358
  const props = [];
328
359
  if (as)
329
360
  props.push(['as', as]);
@@ -388,10 +419,91 @@ export function plan(facts) {
388
419
  ...(parent ? { replacesParent: true } : {}),
389
420
  ...(conditional.length > 0 ? { conditional } : {}),
390
421
  ...(icons ? { icons } : {}),
422
+ ...(file ? { file } : {}),
391
423
  },
392
424
  todos,
393
425
  };
394
426
  }
427
+ /**
428
+ * The tree a target that `buildsFile` renders from props, read from an
429
+ * element's children: exactly the one it renders, with static content where
430
+ * it takes a prop. The <input>'s attributes become the target's, so one it
431
+ * reads as a prop of its own refuses.
432
+ */
433
+ function buildFile(children, entry, hasName) {
434
+ const target = entry.target;
435
+ const shape = {
436
+ kind: 'children',
437
+ token: target,
438
+ why: `bestax \`${target}\` renders the whole \`.file\` tree itself, so this converts only when its tree is the one it renders: one bare \`.file-label\` <label> holding a \`.file-input\` <input type="file">, a bare \`.file-cta\` <span> with a bare \`.file-label\` <span> of static content and at most one bare \`.file-icon\` <span> of static content on each side of it, and, with \`has-name\`, at most one bare \`.file-name\` <span> of static text; keep it as markup, or convert it by hand`,
439
+ };
440
+ const bare = (child, tag, token) => child?.tag === tag &&
441
+ child.tokens?.length === 1 &&
442
+ child.tokens[0] === token &&
443
+ child.attributes.size === 0 &&
444
+ !child.hasSpread;
445
+ const [label, ...beside] = children ?? [];
446
+ if (beside.length > 0 || !bare(label, 'label', 'file-label'))
447
+ return shape;
448
+ const [input, cta, name, ...more] = label.children ?? [];
449
+ if (more.length > 0 ||
450
+ input?.tag !== 'input' ||
451
+ !input.tokens?.includes('file-input') ||
452
+ input.attributes.get('type') !== 'file' ||
453
+ input.attributes.has('key') ||
454
+ input.hasSpread ||
455
+ !bare(cta, 'span', 'file-cta')) {
456
+ return shape;
457
+ }
458
+ const parts = cta.children ?? [];
459
+ const at = parts.findIndex(part => bare(part, 'span', 'file-label'));
460
+ const icons = parts.filter((_, index) => index !== at);
461
+ if (at === -1 ||
462
+ at > 1 ||
463
+ parts.length - at > 2 ||
464
+ !parts[at].staticContent ||
465
+ !icons.every(icon => bare(icon, 'span', 'file-icon') && icon.staticContent)) {
466
+ return shape;
467
+ }
468
+ if (name && !(hasName && bare(name, 'span', 'file-name') && name.text)) {
469
+ return shape;
470
+ }
471
+ const numbers = [];
472
+ for (const [attribute, value] of input.attributes) {
473
+ if (attribute === 'type')
474
+ continue;
475
+ if (entry.ownProps?.includes(attribute) || HELPER_PROPS.has(attribute)) {
476
+ return {
477
+ kind: 'attr',
478
+ token: attribute,
479
+ why: `bestax \`${target}\` takes the <input>'s attributes as its own props, and \`${attribute}\` is one it reads differently; rename or drop the attribute, then re-run`,
480
+ };
481
+ }
482
+ if (!numberAttrsOf(entry).includes(attribute))
483
+ continue;
484
+ if (typeof value !== 'string')
485
+ continue;
486
+ if (!/^-?\d+(?:\.\d+)?$/.test(value) || String(Number(value)) !== value) {
487
+ return {
488
+ kind: 'attr',
489
+ token: attribute,
490
+ why: `bestax \`${target}\` types \`${attribute}\` as a number, and \`${value}\` is not a number spelled the way it renders; keep this element as markup`,
491
+ };
492
+ }
493
+ numbers.push(attribute);
494
+ }
495
+ const inputClasses = input.tokens.filter(token => token !== 'file-input');
496
+ return {
497
+ file: {
498
+ inputClassName: inputClasses.length > 0 ? inputClasses.join(' ') : null,
499
+ ...(parts[at].text !== DEFAULT_FILE_LABEL && { buttonLabel: at }),
500
+ ...(at === 1 && { iconLeft: 0 }),
501
+ ...(parts.length - at === 2 && { iconRight: at + 1 }),
502
+ ...(name && { fileName: name.text }),
503
+ },
504
+ numbers,
505
+ };
506
+ }
395
507
  /**
396
508
  * The icons a target that `buildsIcons` builds from an element's children:
397
509
  * each `.icon` that converts to `Icon` on its own and holds a glyph `Icon`
@@ -464,8 +576,8 @@ function buildIcons(children, target) {
464
576
  * write, the classes that leave `className`, and the props written as
465
577
  * numbers. The group rules apply, so a helper the target would drop (a
466
578
  * base `display` beside a per-viewport one, a flex-container helper with no
467
- * flex `display`) stays a class, and so does a modifier missing the
468
- * attribute it needs.
579
+ * flex `display`, `gapless` beside a `gap`) stays a class, as does one it
580
+ * leaves out, and so does a modifier missing the attribute it needs.
469
581
  */
470
582
  function propsFrom(entry, tokens, tag, root, attributes) {
471
583
  const numbers = [];
@@ -506,6 +618,9 @@ function propsFrom(entry, tokens, tag, root, attributes) {
506
618
  : helper.write.prop;
507
619
  if (!prop || writes.has(prop))
508
620
  continue;
621
+ if (entry.helpersLeftOut && Object.hasOwn(entry.helpersLeftOut, prop)) {
622
+ continue;
623
+ }
509
624
  writes.set(prop, helper.write.value ?? true);
510
625
  sourceOf.set(prop, token);
511
626
  groupOf.set(prop, helper.group);
@@ -529,6 +644,18 @@ function propsFrom(entry, tokens, tag, root, attributes) {
529
644
  undo(prop);
530
645
  }
531
646
  }
647
+ // Props one helper wrote that another drops (`groupOf` holds only what
648
+ // helpers wrote). A `gap` drops `gapless`, though `Grid`'s own `gap`, a
649
+ // modifier, does not; `pos` drops `relative`; and beside an axis prop,
650
+ // `overflow` is written per axis instead of as its own class.
651
+ if (groupOf.has('gap') && groupOf.has('gapless'))
652
+ undo('gapless');
653
+ if (groupOf.has('pos') && groupOf.has('relative'))
654
+ undo('relative');
655
+ if (groupOf.has('overflow') &&
656
+ (groupOf.has('overflowX') || groupOf.has('overflowY'))) {
657
+ undo('overflow');
658
+ }
532
659
  return { writes, converted, numbers };
533
660
  }
534
661
  /** `a`, `a or b`, `a, b or c`, each in backticks. */
@@ -1 +1 @@
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;AAo3BvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CAkwBpB"}
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;AA+6BvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA02BpB"}
@@ -21,7 +21,7 @@ import { addTodo, findAttr, forgetJsxParens, jsxNameParts, literalValueOf, makeA
21
21
  import { collectBoundNames, makeReserve, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
22
22
  import { rewriteStylesheetImports } from '../_shared/css-imports.js';
23
23
  import { BESTAX, buildBestaxImport, placeBestaxImport, seedBestaxImport, } from '../_shared/bestax-import.js';
24
- import { plan, } from './plan.js';
24
+ import { fileContextTodo, plan, } from './plan.js';
25
25
  import { REACT_RUNTIMES, } from './project.js';
26
26
  import { PLACED, placedFor, ROOTS, rootFor } from './class-map.js';
27
27
  import { ruleId } from './rules.js';
@@ -711,6 +711,47 @@ function textChild(j, text) {
711
711
  ? j.jsxText(text)
712
712
  : j.jsxExpressionContainer(j.stringLiteral(text));
713
713
  }
714
+ /**
715
+ * A text as an attribute value: quoted when JSX reads it as written there,
716
+ * and as a string otherwise. A JSX attribute string has no escapes, so a
717
+ * quote would end it, a backslash would print doubled, and an `&` could be
718
+ * read as an entity.
719
+ */
720
+ function textValue(j, text) {
721
+ return /^[^"&\\\r\n]*$/.test(text)
722
+ ? j.stringLiteral(text)
723
+ : j.jsxExpressionContainer(j.stringLiteral(text));
724
+ }
725
+ /** Whether an element's content is text and elements only, and renders. */
726
+ function staticContent(element) {
727
+ const content = (element.children ?? []).filter(reachesReact);
728
+ return (content.length > 0 &&
729
+ content.every((child) => child.type === 'JSXText' ||
730
+ child.type === 'JSXElement' ||
731
+ child.type === 'JSXFragment' ||
732
+ (child.type === 'JSXExpressionContainer' &&
733
+ child.expression.type === 'StringLiteral' &&
734
+ child.expression.value !== '')));
735
+ }
736
+ /**
737
+ * An element's content as one prop value: its one element, or its text, or
738
+ * a fragment of them all, each text written as the string it renders.
739
+ */
740
+ function contentValue(j, element) {
741
+ const content = (element.children ?? []).filter((child) => !dropped(child));
742
+ if (content.length === 1) {
743
+ const [only] = content;
744
+ if (only.type === 'JSXText') {
745
+ return textValue(j, jsxTextRenders(only.value));
746
+ }
747
+ if (only.type === 'JSXExpressionContainer')
748
+ return only;
749
+ return j.jsxExpressionContainer(only);
750
+ }
751
+ return j.jsxExpressionContainer(j.jsxFragment(j.jsxOpeningFragment(), j.jsxClosingFragment(), content.map((child) => child.type === 'JSXText'
752
+ ? textChild(j, jsxTextRenders(child.value))
753
+ : child)));
754
+ }
714
755
  function insideForeignContent(elementPath) {
715
756
  let current = elementPath.parent;
716
757
  while (current) {
@@ -943,6 +984,33 @@ export default function transform(fileInfo, api, options = {}) {
943
984
  ? root
944
985
  : undefined;
945
986
  };
987
+ /**
988
+ * What the planner reads of a child element, down its tree: what it
989
+ * converts to on its own (children are planned first), its only child,
990
+ * its text and whether its content is static, and its own children the
991
+ * same way. Each element is read once, since every element around it
992
+ * reads it again: a plan that changes afterwards forgets its reading.
993
+ */
994
+ const readings = new Map();
995
+ const readChild = (child) => {
996
+ const known = readings.get(child);
997
+ if (known)
998
+ return known;
999
+ const becomes = planned.get(child)?.conversion;
1000
+ const only = soleChild(child);
1001
+ const inner = only && childFacts(only);
1002
+ const text = textOf(child);
1003
+ const children = childElementsOf(child, readChild);
1004
+ const reading = {
1005
+ ...(becomes && { becomes }),
1006
+ ...(inner && { soleChild: inner }),
1007
+ ...(text !== undefined && { text }),
1008
+ ...(children && { children }),
1009
+ ...(staticContent(child) && { staticContent: true }),
1010
+ };
1011
+ readings.set(child, reading);
1012
+ return reading;
1013
+ };
946
1014
  /** The element directly around this one, when this is its only child. */
947
1015
  const holderOf = (elementPath) => {
948
1016
  const up = elementPath.parent?.node;
@@ -982,17 +1050,7 @@ export default function transform(fileInfo, api, options = {}) {
982
1050
  childTargets: childTargets(elementPath),
983
1051
  soleChildTarget: planned.get(sole)?.conversion?.target,
984
1052
  soleChild: sole && childFacts(sole),
985
- childElements: childElementsOf(element, child => {
986
- const becomes = planned.get(child)?.conversion;
987
- const only = soleChild(child);
988
- const inner = only && childFacts(only);
989
- const text = textOf(child);
990
- return {
991
- ...(becomes && { becomes }),
992
- ...(inner && { soleChild: inner }),
993
- ...(text !== undefined && { text }),
994
- };
995
- }),
1053
+ childElements: childElementsOf(element, readChild),
996
1054
  bestaxInside: bestaxInside.get(element) ?? [],
997
1055
  bestaxAround: surrounding.bestax,
998
1056
  componentsAround: surrounding.other,
@@ -1067,6 +1125,7 @@ export default function transform(fileInfo, api, options = {}) {
1067
1125
  const node = inside[icon.index];
1068
1126
  const own = { conversion: null, todos: planned.get(node).todos };
1069
1127
  planned.set(node, own);
1128
+ readings.delete(node);
1070
1129
  const at = elements.findIndex(found => found.path.node === node);
1071
1130
  if (at === -1)
1072
1131
  continue;
@@ -1121,6 +1180,30 @@ export default function transform(fileInfo, api, options = {}) {
1121
1180
  found.converts = false;
1122
1181
  planned.set(found.path.node, found.plan);
1123
1182
  }
1183
+ // A target that renders a `.field` of its own outside a `Field` converts
1184
+ // only inside one: a bestax `Field` already around it, or a `.field`
1185
+ // around it that converts here.
1186
+ for (const found of elements) {
1187
+ const conversion = found.plan.conversion;
1188
+ if (!conversion?.file)
1189
+ continue;
1190
+ let inField = false;
1191
+ for (let up = found.path.parent; up && !inField; up = up.parent) {
1192
+ if (up.node?.type !== 'JSXElement')
1193
+ continue;
1194
+ inField =
1195
+ (bestaxTarget(up.node, up) ??
1196
+ planned.get(up.node)?.conversion?.target) === 'Field';
1197
+ }
1198
+ if (inField)
1199
+ continue;
1200
+ found.plan = {
1201
+ conversion: null,
1202
+ todos: [...found.plan.todos, fileContextTodo(conversion.target)],
1203
+ };
1204
+ found.converts = false;
1205
+ planned.set(found.path.node, found.plan);
1206
+ }
1124
1207
  if (elements.length === 0)
1125
1208
  return print();
1126
1209
  // ---- 3. File-level gates ------------------------------------------------------
@@ -1264,6 +1347,62 @@ export default function transform(fileInfo, api, options = {}) {
1264
1347
  name.comments = [...(name.comments ?? []), ...gone.map(afterTagName)];
1265
1348
  }
1266
1349
  }
1350
+ if (conversion.file) {
1351
+ // The target renders the whole tree from props: the <input>'s
1352
+ // attributes join the element's, the parts below become props, and
1353
+ // the tree goes, its comments moving to the name that stays.
1354
+ const built = conversion.file;
1355
+ const inside = (node) => (node.children ?? []).filter((child) => !dropped(child));
1356
+ const [label] = inside(element);
1357
+ const [input, cta, name] = inside(label);
1358
+ const parts = inside(cta);
1359
+ const given = (input.openingElement.attributes ?? []).filter((attr) => !['className', 'type'].includes(attributeName(attr)));
1360
+ const props = [...given];
1361
+ const prop = (key, value) => props.push(j.jsxAttribute(j.jsxIdentifier(key), value));
1362
+ if (built.inputClassName !== null) {
1363
+ prop('inputClassName', textValue(j, built.inputClassName));
1364
+ }
1365
+ for (const key of ['buttonLabel', 'iconLeft', 'iconRight']) {
1366
+ const index = built[key];
1367
+ if (index !== undefined)
1368
+ prop(key, contentValue(j, parts[index]));
1369
+ }
1370
+ if (built.fileName !== undefined) {
1371
+ prop('fileName', textValue(j, built.fileName));
1372
+ }
1373
+ element.openingElement.attributes = [
1374
+ ...(element.openingElement.attributes ?? []),
1375
+ ...props,
1376
+ ];
1377
+ const commentChildren = (node) => (node.children ?? [])
1378
+ .filter((child) => child.type === 'JSXExpressionContainer' &&
1379
+ child.expression.type === 'JSXEmptyExpression')
1380
+ .flatMap((child) => commentsWithin(child));
1381
+ const moved = [
1382
+ ...[label, input, cta, name, ...parts]
1383
+ .filter(Boolean)
1384
+ .flatMap((node) => [
1385
+ ...(node.comments ?? []),
1386
+ ...tagComments(node),
1387
+ ]),
1388
+ ...[label, cta, name, ...parts]
1389
+ .filter(Boolean)
1390
+ .flatMap((node) => node.openingElement.attributes ?? [])
1391
+ .flatMap((attr) => commentsWithin(attr)),
1392
+ ...(input.openingElement.attributes ?? [])
1393
+ .filter((attr) => !given.includes(attr))
1394
+ .flatMap((attr) => commentsWithin(attr)),
1395
+ ...[element, label, cta].flatMap(commentChildren),
1396
+ ...[element.closingElement, element.closingElement?.name].flatMap((node) => node?.comments ?? []),
1397
+ ];
1398
+ element.children = [];
1399
+ element.openingElement.selfClosing = true;
1400
+ element.closingElement = null;
1401
+ if (moved.length) {
1402
+ const tag = element.openingElement.name;
1403
+ tag.comments = [...(tag.comments ?? []), ...moved.map(afterTagName)];
1404
+ }
1405
+ }
1267
1406
  if (conversion.icons) {
1268
1407
  // The target builds its children from props: one icon as `iconProps`,
1269
1408
  // with its text as the children, or several as `items`. The children
@@ -128,7 +128,7 @@ export const MAPPING = {
128
128
  },
129
129
  forwardRefAs: {
130
130
  status: 'todo',
131
- todo: '`forwardRefAs` is rbx-internal and has no counterpart. bestax forwards a ref from the form controls, plus `Avatar`, `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`, `Navbar.Link`, `Sidebar` and `Toast` — pass `ref` directly on those, though not on the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers, which forward none. Mind the `Navbar.Dropdown` collision: your rbx `Navbar.Dropdown` is the menu and becomes `Navbar.DropdownMenu`, which forwards no ref, while bestax `Navbar.Dropdown` — the container your `<Navbar.Item dropdown>` becomes — does. The rest, `Box` included, forward no ref: React 18 drops it with a warning, React 19 leaves it wherever the rest props go, so put the ref on a wrapping element you control',
131
+ todo: '`forwardRefAs` is rbx-internal and has no counterpart. bestax forwards a ref from the form controls, plus `Avatar`, `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Loader`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`, `Navbar.Link`, `Sidebar` and `Toast` — pass `ref` directly on those, though not on the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers, which forward none. Mind the `Navbar.Dropdown` collision: your rbx `Navbar.Dropdown` is the menu and becomes `Navbar.DropdownMenu`, which forwards no ref, while bestax `Navbar.Dropdown` — the container your `<Navbar.Item dropdown>` becomes — does. The rest, `Box` included, forward no ref: React 18 drops it with a warning, React 19 leaves it wherever the rest props go, so put the ref on a wrapping element you control',
132
132
  },
133
133
  // ---- elements -----------------------------------------------------------
134
134
  Block: { status: 'mapped', target: 'Block' },
@@ -1 +1 @@
1
- {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOnE;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAgFtD,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAUhE,CAAC;AAIF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAsqBpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAsEhD,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CAM3E"}
1
+ {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOnE;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAgFtD,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAUhE,CAAC;AAIF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAuqBpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAsEhD,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CAM3E"}
@@ -26,7 +26,7 @@ export const UNIVERSAL_PROPS = {
26
26
  todo: 'this bestax component has no `as` prop; restructure the element instead',
27
27
  },
28
28
  domRef: {
29
- todo: 'bestax-bulma has no `domRef`. The form controls, plus `Avatar`, `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`, `Navbar.Link`, `Sidebar` and `Toast` forward a ref — on those, rename `domRef` to `ref`, though not on the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers, which forward none. Mind the `Navbar.Dropdown` collision: the `Navbar.Item` that wrapped your dropdown became bestax `Navbar.Dropdown` and forwards one, while your `Navbar.Dropdown` became `Navbar.DropdownMenu` and does not. The rest forward no ref: React 18 drops it with a warning, React 19 leaves it wherever the rest props go, so put the ref on a DOM child or wrap the component',
29
+ todo: 'bestax-bulma has no `domRef`. The form controls, plus `Avatar`, `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Loader`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`, `Navbar.Link`, `Sidebar` and `Toast` forward a ref — on those, rename `domRef` to `ref`, though not on the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers, which forward none. Mind the `Navbar.Dropdown` collision: the `Navbar.Item` that wrapped your dropdown became bestax `Navbar.Dropdown` and forwards one, while your `Navbar.Dropdown` became `Navbar.DropdownMenu` and does not. The rest forward no ref: React 18 drops it with a warning, React 19 leaves it wherever the rest props go, so put the ref on a DOM child or wrap the component',
30
30
  },
31
31
  // ColorProps
32
32
  backgroundColor: { rename: 'bgColor' },
@@ -506,8 +506,9 @@ export const MAPPING = {
506
506
  },
507
507
  Loader: {
508
508
  // RBC Loader is literally <div class="loader"> and Bulma v1 still ships
509
- // that class — a plain element is the faithful conversion (bestax
510
- // Loading is an overlay-style component, not an inline spinner).
509
+ // that class, so a plain element is the faithful conversion. bestax's
510
+ // Loader draws the same ring on a span with a progressbar role and an
511
+ // accessible name; swapping it in is left to the user.
511
512
  status: 'mapped',
512
513
  special: 'plain-loader',
513
514
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bestax-migrate",
3
- "version": "2.23.1",
3
+ "version": "2.25.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": {
@@ -38,7 +38,7 @@
38
38
  "chalk": "^6.0.0",
39
39
  "commander": "^15.0.0",
40
40
  "jscodeshift": "^17.3.0",
41
- "@allxsmith/bestax-bulma": "^5.18.0"
41
+ "@allxsmith/bestax-bulma": "^5.26.4"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@jest/globals": "^30.0.0",
@@ -75,5 +75,5 @@
75
75
  "clean": "rimraf dist",
76
76
  "release": "npx semantic-release"
77
77
  },
78
- "readme": "# bestax-migrate\n\n[![npm version](https://img.shields.io/npm/v/bestax-migrate.svg)](https://www.npmjs.com/package/bestax-migrate)\n[![npm downloads](https://img.shields.io/npm/dm/bestax-migrate.svg)](https://www.npmjs.com/package/bestax-migrate)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Socket Badge](https://badge.socket.dev/npm/package/bestax-migrate/latest)](https://socket.dev/npm/package/bestax-migrate/overview)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/allxsmith/bestax/badge)](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14361/badge)](https://www.bestpractices.dev/projects/14361)\n[![npm provenance](https://img.shields.io/badge/npm-provenance-3fb950.svg)](https://www.npmjs.com/package/bestax-migrate#provenance)\n[![Security policy](https://img.shields.io/badge/security-policy-blue.svg)](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nCodemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — the actively maintained React component library for **Bulma v1**.\n\nCurrently supported sources:\n\n| Source | Status |\n| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |\n| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |\n| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |\n| [`bloomer`](https://github.com/AlgusDark/bloomer) (0.6) | ✅ All 108 exports mapped (Font Awesome 4 icons and Bulma 0.4's `Nav` are flagged) |\n| `bulma-classes`: Bulma's classes on plain JSX, no library | ✅ Converts an element only when bestax renders the same markup; the rest is flagged |\n\nPass the source as the first argument: `bestax-migrate <source> <paths…>`.\n\n## Requirements\n\n**Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the CLI\nexits immediately with an explicit upgrade message.\n\nThis applies only to the Node version the codemod itself runs on. It places no requirement on\nthe app being migrated — the source is read as text and is never executed.\n\n## Usage\n\n```bash\n# Preview what would change (no writes)\npnpm dlx bestax-migrate react-bulma-components src/ --dry\n# …or, for an rbx or bloomer app\npnpm dlx bestax-migrate rbx src/ --dry\npnpm dlx bestax-migrate bloomer src/ --dry\n# …or, for an app that writes Bulma's classes on plain JSX\npnpm dlx bestax-migrate bulma-classes src/ --dry\n\n# Apply the migration\npnpm dlx bestax-migrate react-bulma-components src/\n```\n\nnpm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`.\n\nThe codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to:\n\n- rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports)\n- rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …) — and, for bloomer, its flat names onto bestax's compounds (`CardHeaderTitle` → `Card.Header.Title`)\n- convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign=\"center\"` → `textAlign=\"centered\"`, …)\n- flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`) and bloomer's `isDisplay`/`isHidden` strings, arrays and objects (`isHidden=\"touch\"` → `visibilityTouch=\"hidden\"`)\n- for `bulma-classes`, turn raw Bulma markup into components and props (`<a className=\"button is-primary\">` → `<Button as=\"a\" color=\"primary\">`, `<p className=\"has-text-centered mt-4\">` → `<Paragraph textAlign=\"centered\" mt=\"4\">`), converting an element only when bestax renders exactly the same markup\n- restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → `<Icon name=…>`)\n- migrate stylesheets: CSS imports converge on `@allxsmith/bestax-bulma/bestax.css`, and SCSS files move from Bulma 0.9's `@import` + `$var !default` overrides to `@use 'bulma/sass' with (…)` plus `@use '@allxsmith/bestax-bulma/scss/extras'`\n- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them. A **bloomer** app declared its own Bulma 0.6, which is what gets bumped. For **bulma-classes** there is no library to remove, and by default (`--css keep`) the app's Bulma version, Sass and stylesheet imports all stay: its own Bulma already styles every class a converted element renders. `--css bestax` or `--css bulma` moves it to Bulma v1 like the other sources\n\nAnything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx), [bloomer](https://bestax.io/docs/guides/getting-started/migration/bloomer), [Bulma classes](https://bestax.io/docs/guides/getting-started/migration/bulma-classes)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:\n\n```bash\nnpx skills add https://github.com/allxsmith/bestax --skill bestax-migrate\n```\n\n## Options\n\n| Flag | Description |\n| -------------------- | ---------------------------------------------------------------------------------------------- |\n| `--dry`, `-d` | Report what would change without writing files |\n| `--print`, `-p` | Print transformed sources to stdout |\n| `--extensions`, `-e` | File extensions to include (default `js,jsx,ts,tsx,scss,sass`) |\n| `--css <mode>` | Stylesheet target: `bestax`, `bulma`, or `keep` (default `bestax`; `keep` for `bulma-classes`) |\n| `--no-deps` | Skip updating package.json dependencies |\n\n## After the codemod\n\n1. Run your package manager's install (the codemod rewrote `package.json` but never installs)\n2. Search for `TODO(bestax-migrate)` and resolve each comment\n3. Typecheck/build and review the rendered app\n\nFull walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx) · [bloomer migration guide](https://bestax.io/docs/guides/getting-started/migration/bloomer) · [Bulma classes migration guide](https://bestax.io/docs/guides/getting-started/migration/bulma-classes).\n\n## Hardened by default\n\nA codemod rewrites your source in place, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/bestax-migrate#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **The libraries this tool migrates away from are never installed here** — source fixtures are read as text only, so no unmaintained third-party package enters the dependency tree.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, and CodeQL, Dependency Review, and Dependabot run continuously alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`bestax-migrate` can send one **anonymous** usage event after a successful\nrun — only if you opt in when asked (once, on an interactive terminal). The\nevent is just the run's shape — source library, CSS mode, the dry/deps flags, a\ncapped changed-file count, and per-rule TODO counts — plus the CLI version,\nNode major version, and OS platform name. Never file paths, file contents,\ncode, names, IPs, or machine IDs, and no identifier exists that could link two\nevents together. Opt out any time with `--no-telemetry`,\n`BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## License\n\nSource code licensed MIT\n"
78
+ "readme": "# bestax-migrate\n\n[![npm version](https://img.shields.io/npm/v/bestax-migrate.svg)](https://www.npmjs.com/package/bestax-migrate)\n[![npm downloads](https://img.shields.io/npm/dm/bestax-migrate.svg)](https://www.npmjs.com/package/bestax-migrate)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Socket Badge](https://badge.socket.dev/npm/package/bestax-migrate/latest)](https://socket.dev/npm/package/bestax-migrate/overview)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/allxsmith/bestax/badge)](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14361/badge)](https://www.bestpractices.dev/projects/14361)\n[![npm provenance](https://img.shields.io/badge/npm-provenance-3fb950.svg)](https://www.npmjs.com/package/bestax-migrate#provenance)\n[![Security policy](https://img.shields.io/badge/security-policy-blue.svg)](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nCodemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — the actively maintained React component library for **Bulma v1**.\n\nCurrently supported sources:\n\n| Source | Status |\n| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |\n| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |\n| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |\n| [`bloomer`](https://github.com/AlgusDark/bloomer) (0.6) | ✅ All 108 exports mapped (Font Awesome 4 icons and Bulma 0.4's `Nav` are flagged) |\n| `bulma-classes`: Bulma's classes on plain JSX, no library | ✅ Converts an element only when bestax renders the same markup; the rest is flagged |\n\nPass the source as the first argument: `bestax-migrate <source> <paths…>`.\n\n## Requirements\n\n**Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the CLI\nexits immediately with an explicit upgrade message.\n\nThis applies only to the Node version the codemod itself runs on. It places no requirement on\nthe app being migrated — the source is read as text and is never executed.\n\n## Usage\n\n```bash\n# Preview what would change (no writes)\npnpm dlx bestax-migrate react-bulma-components src/ --dry\n# …or, for an rbx or bloomer app\npnpm dlx bestax-migrate rbx src/ --dry\npnpm dlx bestax-migrate bloomer src/ --dry\n# …or, for an app that writes Bulma's classes on plain JSX\npnpm dlx bestax-migrate bulma-classes src/ --dry\n\n# Apply the migration\npnpm dlx bestax-migrate react-bulma-components src/\n```\n\nnpm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`.\n\nThe codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to:\n\n- rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports)\n- rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …) — and, for bloomer, its flat names onto bestax's compounds (`CardHeaderTitle` → `Card.Header.Title`)\n- convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign=\"center\"` → `textAlign=\"centered\"`, …)\n- flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`) and bloomer's `isDisplay`/`isHidden` strings, arrays and objects (`isHidden=\"touch\"` → `visibilityTouch=\"hidden\"`)\n- for `bulma-classes`, turn raw Bulma markup into components and props (`<a className=\"button is-primary\">` → `<Button as=\"a\" color=\"primary\">`, `<p className=\"has-text-centered mt-4\">` → `<Paragraph textAlign=\"centered\" mt=\"4\">`), converting an element only when bestax renders exactly the same markup\n- restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → `<Icon name=…>`)\n- migrate stylesheets: CSS imports converge on `@allxsmith/bestax-bulma/bestax.css`, and SCSS files move from Bulma 0.9's `@import` + `$var !default` overrides to `@use 'bulma/sass' with (…)` plus `@use '@allxsmith/bestax-bulma/scss/extras'`\n- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them. A **bloomer** app declared its own Bulma 0.6, which is what gets bumped. For **bulma-classes** there is no library to remove, and by default (`--css keep`) the app's Bulma version, Sass and stylesheet imports all stay: its own Bulma already styles every class a converted element renders. `--css bestax` or `--css bulma` moves it to Bulma v1 like the other sources\n\nAnything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx), [bloomer](https://bestax.io/docs/guides/getting-started/migration/bloomer), [Bulma classes](https://bestax.io/docs/guides/getting-started/migration/bulma-classes)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:\n\n```bash\nnpx skills add https://github.com/allxsmith/bestax --skill bestax-migrate\n```\n\n### Through the Codemod Registry\n\nbestax-migrate is also in the [Codemod Registry](https://app.codemod.com/registry) as `bestax-migrate`, for teams that run their migrations through Codemod. That package runs this CLI from npm at a pinned release, which can trail the newest one:\n\n```bash\n# Preview (no writes)\nnpx codemod bestax-migrate --workflow preview --param source=react-bulma-components\n# Apply\nnpx codemod bestax-migrate --param source=react-bulma-components\n```\n\nRun it from your app root. `--param paths=\"src lib\"` (default `src`) and `--param options=\"--css keep\"` pass through to the CLI. Codemod's own `--dry-run` skips the shell step this package runs, so use the `preview` workflow for a dry run.\n\n## Options\n\n| Flag | Description |\n| -------------------- | ---------------------------------------------------------------------------------------------- |\n| `--dry`, `-d` | Report what would change without writing files |\n| `--print`, `-p` | Print transformed sources to stdout |\n| `--extensions`, `-e` | File extensions to include (default `js,jsx,ts,tsx,scss,sass`) |\n| `--css <mode>` | Stylesheet target: `bestax`, `bulma`, or `keep` (default `bestax`; `keep` for `bulma-classes`) |\n| `--no-deps` | Skip updating package.json dependencies |\n\n## After the codemod\n\n1. Run your package manager's install (the codemod rewrote `package.json` but never installs)\n2. Search for `TODO(bestax-migrate)` and resolve each comment\n3. Typecheck/build and review the rendered app\n\nFull walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx) · [bloomer migration guide](https://bestax.io/docs/guides/getting-started/migration/bloomer) · [Bulma classes migration guide](https://bestax.io/docs/guides/getting-started/migration/bulma-classes).\n\n## Hardened by default\n\nA codemod rewrites your source in place, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/bestax-migrate#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **The libraries this tool migrates away from are never installed here** — source fixtures are read as text only, so no unmaintained third-party package enters the dependency tree.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, and CodeQL, Dependency Review, and Dependabot run continuously alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`bestax-migrate` can send one **anonymous** usage event after a successful\nrun — only if you opt in when asked (once, on an interactive terminal). The\nevent is just the run's shape — source library, CSS mode, the dry/deps flags, a\ncapped changed-file count, and per-rule TODO counts — plus the CLI version,\nNode major version, and OS platform name. Never file paths, file contents,\ncode, names, IPs, or machine IDs, and no identifier exists that could link two\nevents together. Opt out any time with `--no-telemetry`,\n`BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## License\n\nSource code licensed MIT\n"
79
79
  }