bestax-migrate 2.3.2 → 2.3.4

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.
@@ -28,13 +28,26 @@
28
28
  import type { ASTPath } from 'jscodeshift';
29
29
  import { type TransformContext } from './jsx-utils.js';
30
30
  export declare const AS_UNIONS: Record<string, readonly string[]>;
31
+ /**
32
+ * What to do with a link the element refuses. `carry` is true for an attribute
33
+ * that has to land ON the anchor to do anything (`target`, `download`, …) and
34
+ * false where the anchor itself IS the remedy (a bare `href`).
35
+ *
36
+ * Four answers, because "put an <a> inside" is wrong in three different ways:
37
+ * an anchor inside interactive content is invalid, an anchor inside a void
38
+ * element is impossible, and an anchor around a context-bound element puts it
39
+ * somewhere its parent may not be.
40
+ *
41
+ * Exported because `specials-utils.ts` reaches the same question from the plain
42
+ * markup side, and `bestax-migrate/CLAUDE.md` puts a rule two transforms need in
43
+ * `_shared/` rather than in one of them.
44
+ */
45
+ export declare const nestOrClick: (rendered: string | undefined, carry?: boolean) => string;
31
46
  /** The targets whose `as` this pass may believe. */
32
47
  export declare function declaresAs(target: string): boolean;
33
48
  /** The rows the type test holds to the library. */
34
49
  export declare const HREF_TABLE: Record<string, string>;
35
50
  export declare const AS_ANY_TARGETS: readonly string[];
36
- /** The rows the type test holds to the library. */
37
- export declare const TARGET_LINK_ATTR_TABLE: Record<string, readonly string[]>;
38
51
  /**
39
52
  * The intrinsic elements React types an `href` onto.
40
53
  *
@@ -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;AAsF3E,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;AAuEpE,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAClD,CAAC;AAEpB;;;;;;;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;AAuLrB;;;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;AAkK3E;;;;;;;;;;;;;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;AAoDpE;;;;;;;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"}
@@ -78,12 +78,12 @@ const AS_ANY = new Set(['Button', 'Menu.Item', 'Navbar.Item', 'Navbar.Link']);
78
78
  * typechecking cannot answer the question on its own. `Level.Item` declares
79
79
  * `href` on its props at every `as`, so `<Level.Item href="/x">` compiles --
80
80
  * but it renders a <div> and forwards `href` only when the tag is an `<a>`
81
- * (`bulma-ui/src/layout/Level.tsx:227`), so the attribute is dropped at
82
- * runtime. A table built from the types alone calls that supported; it is the
81
+ * (`LevelItem`'s anchor branch), so the attribute is dropped at runtime. A table built from the types alone calls that supported; it is the
83
82
  * same dead attribute this pass exists to remove.
84
83
  */
85
84
  const HREF_OK = {
86
85
  Button: 'button',
86
+ 'Dropdown.Item': 'a',
87
87
  'Level.Item': 'div',
88
88
  'Menu.Item': 'a',
89
89
  'Navbar.Item': 'a',
@@ -106,7 +106,6 @@ const NO_HREF_HINT = {
106
106
  // mapping said the right thing here before the entry moved into this table.
107
107
  Delete: 'bestax `Delete` renders a <button> with no children and has no anchor form; wrap it in an <a>, or navigate in `onClick`',
108
108
  'Card.Header.Icon': 'bestax `Card.Header.Icon` renders a <button>, so an <a> inside it would nest interactive elements; navigate in `onClick`, or wrap the whole icon in an <a>',
109
- 'Dropdown.Item': 'bestax `Dropdown.Item` declares no `href` and already renders an <a> by default, so an <a> inside would nest anchors; navigate in `onClick`',
110
109
  };
111
110
  /**
112
111
  * Where to put a link on a target, for the attributes beside `href`. The same
@@ -117,12 +116,122 @@ const NO_HREF_HINT = {
117
116
  const LINK_REMEDY = {
118
117
  Delete: 'wrap it in an <a>, or navigate in `onClick`',
119
118
  'Navbar.Dropdown': 'put it on the `<Navbar.Link>` inside',
120
- // Both already render interactive elements -- a <button> and an <a> -- so
121
- // "put an <a> inside" would nest one inside the other, which is invalid.
119
+ // Already renders an interactive element, so "put an <a> inside" would nest
120
+ // one inside the other, which is invalid.
122
121
  'Card.Header.Icon': 'navigate in `onClick`, or wrap the whole icon in an <a>',
123
- 'Dropdown.Item': 'navigate in `onClick`',
124
122
  };
125
- const remedyFor = (target) => LINK_REMEDY[target] ?? 'put it on an <a> inside, or change the element';
123
+ /**
124
+ * Elements that may not CONTAIN an `<a>`, so "put an <a> inside" names markup
125
+ * the author cannot write.
126
+ *
127
+ * This is not the same question as "may an `<a>` contain it". Interactive
128
+ * content -- `button`, `input`, `details`, `iframe`, … -- may not be an `<a>`
129
+ * DESCENDANT, which rules out wrapping; only a subset also refuses an `<a>`
130
+ * child, which is what rules out nesting. `<details>` is the case that shows
131
+ * they differ: an `<a>` may not wrap one, and
132
+ * `<details><summary>…</summary><a href>…</a></details>` is ordinary valid
133
+ * markup. Conflating the two directions suppressed advice that was correct.
134
+ *
135
+ * So this set is only the second direction, read off each element's content
136
+ * model: `a` (transparent, but no `a` descendant), `button` (no interactive
137
+ * descendant), `select`/`textarea` (options and text only), `embed`/`input`
138
+ * (void), `iframe` (content model "nothing" — markup between the tags is not a
139
+ * usable child).
140
+ *
141
+ * `label` is deliberately NOT here, though it is interactive: its content model
142
+ * is phrasing content excluding labelable controls and nested labels, and an
143
+ * `<a>` is neither. It refuses to be WRAPPED, not to hold a link.
144
+ *
145
+ * `LINK_REMEDY` states the same thing per TARGET, for components that render
146
+ * such an element whatever `as` says; this states it per RENDERED ELEMENT, for
147
+ * the ones where the author's `as` chose it. `Dropdown.Item` needs both
148
+ * readings: it is a link by default and a `<button>` on request.
149
+ */
150
+ const NO_ANCHOR_CHILD = new Set([
151
+ 'a',
152
+ 'button',
153
+ 'embed',
154
+ 'iframe',
155
+ 'input',
156
+ 'select',
157
+ 'textarea',
158
+ ]);
159
+ /**
160
+ * Elements an `<a>` may not WRAP, because they are interactive content and an
161
+ * `<a>` may have no interactive descendant. The complement of the set above
162
+ * rather than the same list: `details` and `label` refuse to be wrapped and
163
+ * accept an `<a>` child perfectly well, which is the whole reason the two
164
+ * questions need two sets.
165
+ *
166
+ * `audio` and `video` are deliberately absent -- interactive only with
167
+ * `controls`, which the tag alone does not say, so wrapping one is valid as far
168
+ * as this can tell.
169
+ */
170
+ const NO_ANCHOR_WRAPPER = new Set([...NO_ANCHOR_CHILD, 'details', 'label']);
171
+ /**
172
+ * Void elements an `<a>` may actually WRAP. `<a><img></a>` is the canonical
173
+ * form, and it is the advice `Delete` already gives through `LINK_REMEDY` for
174
+ * the same reason. The plain-markup path reaches these: bloomer's `tag` is
175
+ * whatever literal the source wrote, so `<Help tag="img">` is one rewrite away.
176
+ *
177
+ * Deliberately short. Void-ness is necessary and not sufficient, and the two
178
+ * ways of getting that wrong are both invalid trees: `input` and `embed` are
179
+ * interactive (above), and the set below belongs somewhere an `<a>` cannot be.
180
+ */
181
+ const WRAPPABLE_VOID = new Set(['br', 'hr', 'img', 'wbr']);
182
+ /**
183
+ * Void elements whose parent is fixed by the spec, so no `<a>` can sit between
184
+ * them and it: `base`, `link` and `meta` belong to `<head>`, `col` to
185
+ * `<colgroup>`, `source` and `track` to a media element, `param` to `<object>`,
186
+ * `area` to `<map>`.
187
+ *
188
+ * Nothing can be nested in them and nothing can wrap them, so the only honest
189
+ * remedy is to stop using the element for this.
190
+ */
191
+ const CONTEXT_BOUND = new Set([
192
+ 'area',
193
+ 'base',
194
+ 'col',
195
+ 'link',
196
+ 'meta',
197
+ 'param',
198
+ 'source',
199
+ 'track',
200
+ ]);
201
+ /**
202
+ * What to do with a link the element refuses. `carry` is true for an attribute
203
+ * that has to land ON the anchor to do anything (`target`, `download`, …) and
204
+ * false where the anchor itself IS the remedy (a bare `href`).
205
+ *
206
+ * Four answers, because "put an <a> inside" is wrong in three different ways:
207
+ * an anchor inside interactive content is invalid, an anchor inside a void
208
+ * element is impossible, and an anchor around a context-bound element puts it
209
+ * somewhere its parent may not be.
210
+ *
211
+ * Exported because `specials-utils.ts` reaches the same question from the plain
212
+ * markup side, and `bestax-migrate/CLAUDE.md` puts a rule two transforms need in
213
+ * `_shared/` rather than in one of them.
214
+ */
215
+ export const nestOrClick = (rendered, carry = false) => {
216
+ if (rendered !== undefined && NO_ANCHOR_CHILD.has(rendered)) {
217
+ return 'navigate in `onClick`';
218
+ }
219
+ if (rendered !== undefined && CONTEXT_BOUND.has(rendered)) {
220
+ return carry
221
+ ? 'move it to an element that can take it'
222
+ : 'use an element that can be a link';
223
+ }
224
+ if (rendered !== undefined &&
225
+ WRAPPABLE_VOID.has(rendered) &&
226
+ !NO_ANCHOR_WRAPPER.has(rendered)) {
227
+ return carry
228
+ ? 'put it on an <a> wrapping this element'
229
+ : 'wrap it in an <a>';
230
+ }
231
+ return carry ? 'put it on an <a> inside' : 'put an <a> inside';
232
+ };
233
+ const remedyFor = (target, rendered) => LINK_REMEDY[target] ??
234
+ `${nestOrClick(rendered, true)}, or change the element`;
126
235
  /** The targets whose `as` this pass may believe. */
127
236
  export function declaresAs(target) {
128
237
  return target in AS_UNIONS || AS_ANY.has(target);
@@ -142,7 +251,7 @@ export const AS_ANY_TARGETS = [...AS_ANY].sort();
142
251
  *
143
252
  * They are excluded because the components disagree about what they forward,
144
253
  * so no shared set is right. `Menu.Item` strips `href` unless the tag is an
145
- * `<a>` or a custom component (`bulma-ui/src/components/Menu.tsx:212-222`),
254
+ * `<a>` or a custom component (`MenuItem`'s `isLinkLike` guard),
146
255
  * while `Button` routes everything but `'button'` through its anchor path and
147
256
  * would forward it. Keeping `href` beside `as="area"` therefore typechecks on
148
257
  * both and does nothing on one of them, which is the silently-dead attribute
@@ -181,26 +290,6 @@ const LINK_ATTR_ELEMENTS = {
181
290
  // `rel` is deliberately absent: React declares it on `HTMLAttributes`, so it
182
291
  // is valid on every intrinsic and there is nothing to remove. The same point
183
292
  // `bulma-ui/src/__typetests__/polymorphic.tsx` makes about it.
184
- /**
185
- * The link attributes a target accepts, where its props do not follow `as`.
186
- *
187
- * Most components here either follow `as` (the element decides, above) or
188
- * extend `AnchorHTMLAttributes` outright (`Pagination.*`, `Panel.Block`, so
189
- * everything is fine). `Level.Item` is the exception that enumerates: it
190
- * declares `href`, `target` and `rel` and nothing else, so `download`,
191
- * `hrefLang`, `ping` and `referrerPolicy` are type errors there even at
192
- * `as="a"`.
193
- *
194
- * A target absent from `HREF_OK` needs no row: a component that takes no
195
- * `href` at any `as` takes none of its siblings either -- verified for
196
- * `Dropdown.Item`, `Card.FooterItem`, `Tabs.Item` and `Delete`, which is why
197
- * this is derived from that table rather than being a second list to keep.
198
- */
199
- const TARGET_LINK_ATTRS = {
200
- 'Level.Item': ['target'],
201
- };
202
- /** The rows the type test holds to the library. */
203
- export const TARGET_LINK_ATTR_TABLE = TARGET_LINK_ATTRS;
204
293
  /**
205
294
  * The intrinsic elements React types an `href` onto.
206
295
  *
@@ -431,7 +520,7 @@ function dropInertHref(ctx, path, element, target, literal, elementKnown) {
431
520
  if (rendered === undefined) {
432
521
  if (defaultEl === ANCHOR)
433
522
  return;
434
- drop(`bestax \`${target}\` renders a <${defaultEl}> unless \`as\` says otherwise, and only its <a> form carries an \`href\` -- set \`as="a"\` to make this a link, or navigate in \`onClick\``);
523
+ drop(`bestax \`${target}\` renders a <${defaultEl}> unless \`as\` says otherwise, and only its <a> form carries an \`href\` -- set \`as="a"\` to make this a link, or ${nestOrClick(defaultEl)}`);
435
524
  return;
436
525
  }
437
526
  if (rendered === ANCHOR)
@@ -441,8 +530,8 @@ function dropInertHref(ctx, path, element, target, literal, elementKnown) {
441
530
  // anchor; on `Button` it gives a <button> that will not compile, and on
442
531
  // `Level.Item` a <div> that compiles and quietly is not a link.
443
532
  const remedy = defaultEl === ANCHOR
444
- ? 'drop the `as` to make this a link, or put an <a> inside'
445
- : `set \`as="a"\` to make this a link -- dropping the \`as\` gives you a <${defaultEl}> -- or put an <a> inside`;
533
+ ? `drop the \`as\` to make this a link, or ${nestOrClick(rendered)}`
534
+ : `set \`as="a"\` to make this a link -- dropping the \`as\` gives you a <${defaultEl}> -- or ${nestOrClick(rendered)}`;
446
535
  drop(`\`href\` on \`as="${rendered}"\`: bestax gives an element the attributes of the tag \`as\` names, and a <${rendered}> takes no \`href\` (it navigated nowhere in the source either) -- ${remedy}`);
447
536
  }
448
537
  /**
@@ -455,22 +544,21 @@ function dropInertHref(ctx, path, element, target, literal, elementKnown) {
455
544
  */
456
545
  function dropInertLinkAttrs(ctx, path, element, target, rendered) {
457
546
  const carriesLinks = HREF_OK[target] !== undefined;
458
- const declared = TARGET_LINK_ATTRS[target];
459
547
  for (const name of LINK_ATTRS) {
460
- // Both have to allow it. A target that enumerates its props can be
461
- // narrower than the element, and the element can be narrower than the
462
- // target: `Level.Item` declares `target` but forwards it only when the
463
- // tag is an `<a>`, so `<Level.Item as="p" target="_blank">` was keeping
464
- // the same inert attribute this pass removes everywhere else.
465
- // Which check rejects it decides both the outcome and what the TODO says.
466
- // Keying the message off "the target has a row" instead told a
467
- // `<Level.Item as="p" target>` reader that `Level.Item` does not declare
468
- // `target` -- it does; the <p> is what refuses it.
548
+ // Two ways to refuse it, and which one decides the TODO's wording. The
549
+ // component refuses outright if it is no link at any `as`. Otherwise the
550
+ // ELEMENT decides — `Level.Item` declares `target` and forwards it only for
551
+ // an `<a>`, so `<Level.Item as="p" target="_blank">` would keep the same
552
+ // inert attribute this pass removes everywhere else, and the message has to
553
+ // say the `<p>` refused it rather than that the component did.
554
+ //
555
+ // There used to be a third way: a per-target table for a component that
556
+ // enumerated a SUBSET of the link attributes and was therefore narrower
557
+ // than its own element. Nothing is, since #672, so the table went with the
558
+ // branch that read it.
469
559
  let rejectedBy = null;
470
560
  if (!carriesLinks)
471
561
  rejectedBy = 'component';
472
- else if (declared && !declared.includes(name))
473
- rejectedBy = 'component';
474
562
  // A dynamic `as` leaves the element unknown, and guessing either way is
475
563
  // worse than leaving it for the author.
476
564
  else if (rendered !== undefined && !elementTakesLinkAttr(name, rendered))
@@ -484,10 +572,8 @@ function dropInertLinkAttrs(ctx, path, element, target, rendered) {
484
572
  removeAttr(element, attr);
485
573
  const because = rejectedBy === 'element'
486
574
  ? `\`${name}\` needs an element that takes it, and \`${target}\` renders a <${rendered}> here`
487
- : !carriesLinks
488
- ? `bestax \`${target}\` is not a link at any \`as\`, so it takes no \`${name}\` either`
489
- : `bestax \`${target}\` declares its own props rather than taking the element's, and \`${name}\` is not among them`;
490
- addTodo(ctx, path, `prop:${name}`, `${because} -- ${was ? `it read \`${was}\`; ` : ''}${remedyFor(target)}`);
575
+ : `bestax \`${target}\` is not a link at any \`as\`, so it takes no \`${name}\` either`;
576
+ addTodo(ctx, path, `prop:${name}`, `${because} -- ${was ? `it read \`${was}\`; ` : ''}${remedyFor(target, rendered)}`);
491
577
  ctx.dirty = true;
492
578
  }
493
579
  }
@@ -563,8 +649,8 @@ spreadCanCarryAs = false) {
563
649
  // The element as it will render: the `as` if the target takes it, otherwise
564
650
  // the component's own. Unknown for a dynamic `as`, and unknown for a target
565
651
  // outside `HREF_OK` that was given no `as` -- the default element is only
566
- // recorded for the nine that can carry a link. So this rule reaches an
567
- // explicit accepted `as` on any target, plus those nine bare; elsewhere it
652
+ // recorded for the targets in that table. So this rule reaches an explicit
653
+ // accepted `as` on any target, plus those targets bare; elsewhere it
568
654
  // declines rather than guesses.
569
655
  const rendered = read.shadowed
570
656
  ? undefined
@@ -1 +1 @@
1
- {"version":3,"file":"specials-utils.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/specials-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specials-utils.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/specials-utils.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  * the modifier vocabulary is the one thing here that differs per source.
9
9
  */
10
10
  import { addAttrOnce } from './props.js';
11
- import { LINK_ATTRS, elementTakesLinkAttr, tagRejectsHref, } from './polymorphic.js';
11
+ import { LINK_ATTRS, elementTakesLinkAttr, nestOrClick, tagRejectsHref, } from './polymorphic.js';
12
12
  import { addAttr, addTodo, attrSource, attributesOf, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, } from './jsx-utils.js';
13
13
  /** Pick a target based on a literal align-style prop, removing the prop. */
14
14
  export function alignTarget(ctx, path, element, prop, targets, fallback) {
@@ -234,7 +234,7 @@ export function dropLinkAttrsForPlainTag(ctx, path, element, tag, where) {
234
234
  if (href) {
235
235
  const was = attrSource(ctx.j, href);
236
236
  removeAttr(element, href);
237
- addTodo(ctx, path, 'prop:href', `${where} became a plain <${tag}>, which takes no \`href\` — make it an <a>, or put one inside${was ? ` — it read \`${was}\`` : ''}`);
237
+ addTodo(ctx, path, 'prop:href', `${where} became a plain <${tag}>, which takes no \`href\` — make it an <a>, or ${nestOrClick(tag)}${was ? ` — it read \`${was}\`` : ''}`);
238
238
  ctx.dirty = true;
239
239
  }
240
240
  // Its siblings are judged against the same tag, each on its own, so a
@@ -247,7 +247,7 @@ export function dropLinkAttrsForPlainTag(ctx, path, element, tag, where) {
247
247
  continue;
248
248
  const was = attrSource(ctx.j, attr);
249
249
  removeAttr(element, attr);
250
- addTodo(ctx, path, `prop:${name}`, `${where} became a plain <${tag}>, which takes no \`${name}\`${was ? ` — it read \`${was}\`` : ''}; put it on an <a> inside, or change the element`);
250
+ addTodo(ctx, path, `prop:${name}`, `${where} became a plain <${tag}>, which takes no \`${name}\`${was ? ` — it read \`${was}\`` : ''}; ${nestOrClick(tag, true)}, or change the element`);
251
251
  ctx.dirty = true;
252
252
  }
253
253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bestax-migrate",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
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.15.1"
41
+ "@allxsmith/bestax-bulma": "^5.15.3"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@jest/globals": "^30.0.0",