bestax-migrate 2.3.1 → 2.3.2
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/dist/sources/_shared/jsx-utils.d.ts +7 -0
- package/dist/sources/_shared/jsx-utils.d.ts.map +1 -1
- package/dist/sources/_shared/jsx-utils.js +24 -1
- package/dist/sources/_shared/polymorphic.d.ts +92 -0
- package/dist/sources/_shared/polymorphic.d.ts.map +1 -0
- package/dist/sources/_shared/polymorphic.js +593 -0
- package/dist/sources/_shared/specials-utils.d.ts +9 -0
- package/dist/sources/_shared/specials-utils.d.ts.map +1 -1
- package/dist/sources/_shared/specials-utils.js +47 -1
- package/dist/sources/bloomer/mapping.d.ts.map +1 -1
- package/dist/sources/bloomer/mapping.js +0 -12
- package/dist/sources/bloomer/specials.d.ts.map +1 -1
- package/dist/sources/bloomer/specials.js +93 -19
- package/dist/sources/bloomer/transform.d.ts.map +1 -1
- package/dist/sources/bloomer/transform.js +4 -0
- package/dist/sources/rbx/mapping.d.ts.map +1 -1
- package/dist/sources/rbx/mapping.js +13 -1
- package/dist/sources/rbx/transform.d.ts.map +1 -1
- package/dist/sources/rbx/transform.js +7 -0
- package/dist/sources/react-bulma-components/mapping.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/mapping.js +7 -0
- package/dist/sources/react-bulma-components/specials.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/specials.js +43 -14
- package/dist/sources/react-bulma-components/transform.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/transform.js +4 -0
- package/package.json +2 -2
|
@@ -8,7 +8,8 @@
|
|
|
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 {
|
|
11
|
+
import { LINK_ATTRS, elementTakesLinkAttr, tagRejectsHref, } from './polymorphic.js';
|
|
12
|
+
import { addAttr, addTodo, attrSource, attributesOf, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, } from './jsx-utils.js';
|
|
12
13
|
/** Pick a target based on a literal align-style prop, removing the prop. */
|
|
13
14
|
export function alignTarget(ctx, path, element, prop, targets, fallback) {
|
|
14
15
|
const attr = findAttr(element, prop);
|
|
@@ -206,6 +207,50 @@ export function restrictAsToTargets(ctx, path, element, target, allowed, prop =
|
|
|
206
207
|
.join(' / ')} does); render the tag directly or restructure`);
|
|
207
208
|
ctx.dirty = true;
|
|
208
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Strip the link attributes a plain element cannot carry.
|
|
212
|
+
*
|
|
213
|
+
* Exported because not every source reaches plain markup through
|
|
214
|
+
* `replaceWithPlain`: react-bulma-components builds six of its replacements
|
|
215
|
+
* by hand, and they bypassed this entirely -- `<Form.Help href="/x">` became
|
|
216
|
+
* `<p className="help" href="/x">`, which does not compile.
|
|
217
|
+
*/
|
|
218
|
+
export function dropLinkAttrsForPlainTag(ctx, path, element, tag, where) {
|
|
219
|
+
// An `href` only belongs on the anchor. The source component put it on
|
|
220
|
+
// whatever tag it rendered, where the browser ignored it; a plain element
|
|
221
|
+
// is typed as itself, so carrying it here is a type error for an attribute
|
|
222
|
+
// that never navigated anywhere.
|
|
223
|
+
// A plain rewrite can legitimately produce an `<area>` or a `<link>`, and
|
|
224
|
+
// React types an `href` onto both. The anchor-only rule that applies to
|
|
225
|
+
// bestax components does not apply here -- there is no component in the way
|
|
226
|
+
// to disagree about what it forwards.
|
|
227
|
+
//
|
|
228
|
+
// The message deliberately does not add "it navigated nowhere in the source
|
|
229
|
+
// either", the way the polymorphic path does. That holds only where `tag`
|
|
230
|
+
// came from the source -- bloomer's call sites all pass one -- while rbx and
|
|
231
|
+
// react-bulma-components hard-code the plain tag and ignore the source's
|
|
232
|
+
// element prop, so there the element really did change.
|
|
233
|
+
const href = tagRejectsHref(tag) ? findAttr(element, 'href') : undefined;
|
|
234
|
+
if (href) {
|
|
235
|
+
const was = attrSource(ctx.j, href);
|
|
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}\`` : ''}`);
|
|
238
|
+
ctx.dirty = true;
|
|
239
|
+
}
|
|
240
|
+
// Its siblings are judged against the same tag, each on its own, so a
|
|
241
|
+
// `referrerPolicy` on an `<img>` survives.
|
|
242
|
+
for (const name of LINK_ATTRS) {
|
|
243
|
+
if (elementTakesLinkAttr(name, tag))
|
|
244
|
+
continue;
|
|
245
|
+
const attr = findAttr(element, name);
|
|
246
|
+
if (!attr)
|
|
247
|
+
continue;
|
|
248
|
+
const was = attrSource(ctx.j, attr);
|
|
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`);
|
|
251
|
+
ctx.dirty = true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
209
254
|
/**
|
|
210
255
|
* The two structural rewrites every source needs, bound to that source's own
|
|
211
256
|
* attribute strip (its modifier vocabulary is the one thing that differs):
|
|
@@ -216,6 +261,7 @@ export function makeStructuralHelpers(strip) {
|
|
|
216
261
|
/** Replace the element with a plain HTML tag carrying `className`. */
|
|
217
262
|
function replaceWithPlain(ctx, path, element, tag, className, where) {
|
|
218
263
|
const merged = mergeClassName(ctx, path, element, className, where);
|
|
264
|
+
dropLinkAttrsForPlainTag(ctx, path, element, tag, where);
|
|
219
265
|
const kept = new Set(strip(ctx, path, attributesOf(element), where));
|
|
220
266
|
// Spread attributes pass through untouched, in their original places:
|
|
221
267
|
// they are the caller's own props, and a plain element takes them as
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA2BnE;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,UAEzB,CAAC;AA+CF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsBtD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAG1D,CAAC;AAEF;;;;;GAKG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,aAI/B,CAAC;AAEH,eAAO,MAAM,kBAAkB,aAqB7B,CAAC;AAEH,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAC3C,MAAM,GAAG,IAAI,CA0Bf;AAED,iFAAiF;AACjF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlD,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA2BnE;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,UAEzB,CAAC;AA+CF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsBtD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAG1D,CAAC;AAEF;;;;;GAKG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,aAI/B,CAAC;AAEH,eAAO,MAAM,kBAAkB,aAqB7B,CAAC;AAEH,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAC3C,MAAM,GAAG,IAAI,CA0Bf;AAED,iFAAiF;AACjF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlD,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA0nBpD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAiIpD,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CAM3E"}
|
|
@@ -276,9 +276,6 @@ export const MAPPING = {
|
|
|
276
276
|
props: {
|
|
277
277
|
hasTextColor: TEXT_COLOR_OK,
|
|
278
278
|
isSize: size,
|
|
279
|
-
href: {
|
|
280
|
-
todo: 'bestax `Delete` renders a <button> and has no anchor form; wrap it in an <a>, or handle the navigation in `onClick`',
|
|
281
|
-
},
|
|
282
279
|
},
|
|
283
280
|
},
|
|
284
281
|
Icon: {
|
|
@@ -484,9 +481,6 @@ export const MAPPING = {
|
|
|
484
481
|
target: 'Card.Header.Icon',
|
|
485
482
|
props: {
|
|
486
483
|
hasTextColor: TEXT_COLOR_OK,
|
|
487
|
-
href: {
|
|
488
|
-
todo: 'bestax `Card.Header.Icon` renders a <button>; put an <a> inside it, or handle the navigation in `onClick`',
|
|
489
|
-
},
|
|
490
484
|
},
|
|
491
485
|
},
|
|
492
486
|
CardFooter: {
|
|
@@ -499,9 +493,6 @@ export const MAPPING = {
|
|
|
499
493
|
target: 'Card.FooterItem',
|
|
500
494
|
props: {
|
|
501
495
|
hasTextColor: TEXT_COLOR_OK,
|
|
502
|
-
href: {
|
|
503
|
-
todo: 'bestax `Card.FooterItem` renders a <span> with no anchor form; put an <a> inside it',
|
|
504
|
-
},
|
|
505
496
|
},
|
|
506
497
|
},
|
|
507
498
|
// ---- dropdown -----------------------------------------------------------
|
|
@@ -536,9 +527,6 @@ export const MAPPING = {
|
|
|
536
527
|
props: {
|
|
537
528
|
isActive: active,
|
|
538
529
|
tag: TAG_AS,
|
|
539
|
-
href: {
|
|
540
|
-
todo: 'bestax `Dropdown.Item` declares no `href`; navigate in `onClick`, or put an <a> inside the item',
|
|
541
|
-
},
|
|
542
530
|
},
|
|
543
531
|
},
|
|
544
532
|
DropdownDivider: { status: 'mapped', target: 'Dropdown.Divider' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,
|
|
1
|
+
{"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAUL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAQL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,aAAa,EAAE,CAAC;AA0nC9B,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,GACX,aAAa,CAQf;AAED,eAAO,MAAM,aAAa,UAAwB,CAAC"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* element `replaced` when it substituted the node itself.
|
|
12
12
|
*/
|
|
13
13
|
import { RENDER_TODO, RESPONSIVE_PROPS, UNIVERSAL_PROPS } from './mapping.js';
|
|
14
|
-
import { addAttr, addTodo, attributesOf, findAttr, literalValueOf, makeAttr, removeAttr, resolveBooleanish, } from '../_shared/jsx-utils.js';
|
|
14
|
+
import { addAttr, attrSource, addTodo, attributesOf, findAttr, literalValueOf, makeAttr, removeAttr, resolveBooleanish, } from '../_shared/jsx-utils.js';
|
|
15
15
|
import { applyPropAction } from '../_shared/props.js';
|
|
16
16
|
import { applyIconProps, makeStripModifierProps, makeStructuralHelpers, modifierClass, parseIconClasses, restrictAsToTargets, } from '../_shared/specials-utils.js';
|
|
17
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -76,14 +76,56 @@ const join = (...parts) => {
|
|
|
76
76
|
* (and the Nav family). bestax's defaults differ per component, so this
|
|
77
77
|
* keeps bloomer's markup for those either way:
|
|
78
78
|
*
|
|
79
|
-
* - with
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* - with an `href` that has a value, the anchor is the element bloomer
|
|
80
|
+
* rendered, so the `tag` beside it (which the mapping would turn into `as`)
|
|
81
|
+
* is the branch that did not happen and goes; where the target defaults to
|
|
82
|
+
* something other than an <a>, `as="a"` is set (`setAs`);
|
|
83
|
+
* - a dynamic `href` was a runtime decision bloomer made and bestax's `as` is
|
|
84
|
+
* one element or the other. It resolves to the anchor, which is the branch
|
|
85
|
+
* the `href` is there for, and the TODO names the element to restore for
|
|
86
|
+
* the empty case. Keeping both would emit `href` beside a non-anchor `as`,
|
|
87
|
+
* which the library does not type.
|
|
88
|
+
*
|
|
89
|
+
* Settled deliberately, and reviewers have raised it more than once. The
|
|
90
|
+
* alternative that keeps bloomer's own choice is a conditional --
|
|
91
|
+
* `as={href ? 'a' : 'span'}` -- which does typecheck, on `Button` as well
|
|
92
|
+
* as `Level.Item`. It was weighed and declined: it writes a conditional the
|
|
93
|
+
* author did not, it only works where the source `tag` is a literal, and it
|
|
94
|
+
* leaves a second shape to explain. A single element plus a TODO that names
|
|
95
|
+
* the other is the output this package prefers. Do not re-litigate without
|
|
96
|
+
* new information;
|
|
84
97
|
* - without `href` or `tag`, a target that defaults to an <a> gets
|
|
85
98
|
* `as={bareAs}` so bloomer's <div> stays a <div>.
|
|
86
99
|
*/
|
|
100
|
+
/**
|
|
101
|
+
* Did bloomer's `props.href ? 'a' : tag` take the `tag` branch for this value?
|
|
102
|
+
*
|
|
103
|
+
* True for `""`, `false` and `0`, and for `null` and `undefined` -- both reach
|
|
104
|
+
* `literalValueOf` as expressions, because it has no kind for them, but both
|
|
105
|
+
* are statically falsy and neither ever selected the anchor. `undefined` is
|
|
106
|
+
* shadowable (a parameter may be named it), so it is resolved by binding
|
|
107
|
+
* rather than by text, which is the rule this package applies to component
|
|
108
|
+
* references.
|
|
109
|
+
*
|
|
110
|
+
* Shared by `anchorWhenHref` and the `panel-block` handler: they are two
|
|
111
|
+
* copies of the same decision, and the second one missed these two literals.
|
|
112
|
+
*/
|
|
113
|
+
function isFalsyHref(path, attr) {
|
|
114
|
+
const expr = attr.value?.type === 'JSXExpressionContainer'
|
|
115
|
+
? attr.value.expression
|
|
116
|
+
: undefined;
|
|
117
|
+
if (expr) {
|
|
118
|
+
if (expr.type === 'NullLiteral')
|
|
119
|
+
return true;
|
|
120
|
+
if (expr.type === 'Literal' && expr.value === null)
|
|
121
|
+
return true;
|
|
122
|
+
if (expr.type === 'Identifier' && expr.name === 'undefined') {
|
|
123
|
+
return !path.scope?.lookup('undefined');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const literal = literalValueOf(attr);
|
|
127
|
+
return literal.kind !== 'expression' && !literal.value;
|
|
128
|
+
}
|
|
87
129
|
function anchorWhenHref(ctx, path, element, options = {}) {
|
|
88
130
|
const handled = [];
|
|
89
131
|
const tagAttr = findAttr(element, 'tag');
|
|
@@ -92,31 +134,65 @@ function anchorWhenHref(ctx, path, element, options = {}) {
|
|
|
92
134
|
// false `href` kept the default element. It selected nothing, and bestax
|
|
93
135
|
// types `href` as a string where it exists at all, so it is dropped.
|
|
94
136
|
const hrefLiteral = hrefAttr ? literalValueOf(hrefAttr) : undefined;
|
|
95
|
-
const hrefFalsy =
|
|
96
|
-
hrefLiteral.kind !== 'expression' &&
|
|
97
|
-
!hrefLiteral.value;
|
|
137
|
+
const hrefFalsy = hrefAttr !== undefined && isFalsyHref(path, hrefAttr);
|
|
98
138
|
if (hrefAttr && hrefFalsy) {
|
|
99
139
|
removeAttr(element, hrefAttr);
|
|
100
140
|
handled.push('href');
|
|
101
141
|
ctx.dirty = true;
|
|
102
142
|
}
|
|
103
143
|
if (hrefAttr && !hrefFalsy) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
144
|
+
// The href wins the element. bloomer read `props.href ? 'a' : tag`, so a
|
|
145
|
+
// `tag` here is the branch that did not run, and the anchor is what
|
|
146
|
+
// rendered. That holds for an expression too, so the `tag` still goes --
|
|
147
|
+
// but it is a live reference, so its removal is announced rather than
|
|
148
|
+
// silent, which is what the first version of this got wrong.
|
|
149
|
+
const tagValue = tagAttr ? literalValueOf(tagAttr) : undefined;
|
|
150
|
+
const dynamicTag = tagAttr !== undefined && tagValue?.kind !== 'string';
|
|
151
|
+
// The only removal in this pass that deletes an arbitrary expression, so
|
|
152
|
+
// it owes the same quote every other one gives: the reference may be the
|
|
153
|
+
// last thing keeping an import alive.
|
|
154
|
+
const dynamicTagSource = dynamicTag ? attrSource(ctx.j, tagAttr) : '';
|
|
111
155
|
if (tagAttr) {
|
|
112
156
|
removeAttr(element, tagAttr);
|
|
113
157
|
handled.push('tag');
|
|
114
158
|
ctx.dirty = true;
|
|
115
159
|
}
|
|
160
|
+
if (dynamicTag) {
|
|
161
|
+
addTodo(ctx, path, 'prop:tag', `bloomer rendered an <a> whenever \`href\` had a value, so the \`tag\` expression beside this one chose nothing and is removed${dynamicTagSource ? ` -- it read \`${dynamicTagSource}\`` : ''}. Restore it by hand if the \`href\` can be empty`);
|
|
162
|
+
}
|
|
163
|
+
// An `as` on the element is not bloomer's element choice -- it took no
|
|
164
|
+
// such prop -- so a literal one that is not the anchor is dropped. An
|
|
165
|
+
// expression stays: it is the author's own component, it is the element
|
|
166
|
+
// they meant, and on a target generic over `as` it takes the `href` with
|
|
167
|
+
// it. Rewriting it to `as="a"` swapped a router link for a plain anchor.
|
|
168
|
+
const asAttr = findAttr(element, 'as');
|
|
169
|
+
const asLiteral = asAttr ? literalValueOf(asAttr) : undefined;
|
|
170
|
+
const literalAs = asLiteral?.kind === 'string' ? asLiteral.value : undefined;
|
|
171
|
+
if (asAttr && asLiteral?.kind === 'string' && literalAs !== 'a') {
|
|
172
|
+
removeAttr(element, asAttr);
|
|
173
|
+
// Say so. bloomer had no `as`, so this one was spread onto the element
|
|
174
|
+
// as a stray DOM attribute and did nothing -- but in bestax `as` picks
|
|
175
|
+
// the element, so removing it is a decision about the author's code and
|
|
176
|
+
// not a no-op the way it was in the source.
|
|
177
|
+
addTodo(ctx, path, 'prop:as', `bloomer had no \`as\` prop, so \`as="${literalAs}"\` rode onto the element it rendered and did nothing; in bestax it would pick the element, and the \`href\` here needs the <a> -- it is removed rather than left to fight the anchor`);
|
|
178
|
+
ctx.dirty = true;
|
|
179
|
+
}
|
|
116
180
|
if (options.setAs && !findAttr(element, 'as')) {
|
|
117
181
|
addAttr(element, makeAttr(ctx.j, 'as', 'a'));
|
|
118
182
|
ctx.dirty = true;
|
|
119
183
|
}
|
|
184
|
+
if (hrefLiteral.kind === 'expression') {
|
|
185
|
+
// Name the element that was dropped where the source named it: it is
|
|
186
|
+
// the half of bloomer's runtime choice this output no longer renders.
|
|
187
|
+
const other = dynamicTag
|
|
188
|
+
? 'the element `tag` named'
|
|
189
|
+
: tagValue?.kind === 'string'
|
|
190
|
+
? `a <${tagValue.value}>`
|
|
191
|
+
: options.bareAs
|
|
192
|
+
? `a <${options.bareAs}>`
|
|
193
|
+
: 'its default tag';
|
|
194
|
+
addTodo(ctx, path, 'prop:href', `bloomer chose the element at runtime — an <a> when \`href\` had a value, ${other} when it did not. bestax's \`as\` is one element or the other, and this is the anchor; render the other by hand where the \`href\` is empty`);
|
|
195
|
+
}
|
|
120
196
|
}
|
|
121
197
|
else if (options.bareAs && !tagAttr && !findAttr(element, 'as')) {
|
|
122
198
|
addAttr(element, makeAttr(ctx.j, 'as', options.bareAs));
|
|
@@ -521,9 +597,7 @@ const SPECIALS = {
|
|
|
521
597
|
// The same rule as anchorWhenHref: bloomer chose the anchor with
|
|
522
598
|
// `props.href ? 'a' : tag`, so an empty or false href is no anchor.
|
|
523
599
|
const hrefAttr = findAttr(element, 'href');
|
|
524
|
-
const
|
|
525
|
-
const anchored = hrefAttr !== undefined &&
|
|
526
|
-
(hrefLiteral.kind === 'expression' || Boolean(hrefLiteral.value));
|
|
600
|
+
const anchored = hrefAttr !== undefined && !isFalsyHref(path, hrefAttr);
|
|
527
601
|
if (hrefAttr && !anchored) {
|
|
528
602
|
// A falsy href selected nothing in bloomer and cannot sit on a <div>.
|
|
529
603
|
removeAttr(element, hrefAttr);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAwDvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA86BpB"}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { HELPERLESS_TARGETS, MAPPING, NO_CLASSNAME_TARGETS, UNIVERSAL_PROPS, helperClassHint, resolveMapping, } from './mapping.js';
|
|
25
25
|
import { addTodo, attributesOf, jsxNameParts, literalValueOf, removeAttr, renameElement, } from '../_shared/jsx-utils.js';
|
|
26
26
|
import { applyPropAction, applyUniversalProps, mergeClass, } from '../_shared/props.js';
|
|
27
|
+
import { enforcePolymorphicProps } from '../_shared/polymorphic.js';
|
|
27
28
|
import { collectBoundNames, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
28
29
|
import { flattenResponsiveProps, RESPONSIVE_KINDS } from './responsive.js';
|
|
29
30
|
import { runSpecial } from './specials.js';
|
|
@@ -456,6 +457,9 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
456
457
|
}
|
|
457
458
|
}
|
|
458
459
|
applyUniversalProps(ctx, path, element, handled, UNIVERSAL_PROPS);
|
|
460
|
+
// Last: bestax's props follow `as`, so the element decides which of the
|
|
461
|
+
// props just written it can actually take.
|
|
462
|
+
enforcePolymorphicProps(ctx, path, element, target);
|
|
459
463
|
});
|
|
460
464
|
// ---- 2b. Value references to bloomer components ---------------------------
|
|
461
465
|
// Components can be referenced as plain values too (`as={Block}`, passed to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/rbx/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAuCnE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAiCtD,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOrD,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQvD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAShE,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/rbx/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAuCnE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAiCtD,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOrD,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQvD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAShE,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAktBpD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CA2FhD,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CAM3E"}
|
|
@@ -510,7 +510,11 @@ export const MAPPING = {
|
|
|
510
510
|
target: 'Dropdown.Item',
|
|
511
511
|
// `as` lives on DropdownItemProps ('a' | 'div' | 'button'), not on
|
|
512
512
|
// the Dropdown root.
|
|
513
|
-
props: {
|
|
513
|
+
props: {
|
|
514
|
+
as: AS_OK,
|
|
515
|
+
active: {},
|
|
516
|
+
onClick: {},
|
|
517
|
+
},
|
|
514
518
|
},
|
|
515
519
|
Divider: { status: 'mapped', target: 'Dropdown.Divider' },
|
|
516
520
|
Context: {
|
|
@@ -680,6 +684,14 @@ export const MAPPING = {
|
|
|
680
684
|
},
|
|
681
685
|
},
|
|
682
686
|
Item: {
|
|
687
|
+
// The `navbar-item` handler names a target only for the `dropdown`
|
|
688
|
+
// case; without one here the plain item resolved to no target at all,
|
|
689
|
+
// and `transform.ts` skips every prop pass for an element it cannot
|
|
690
|
+
// name — so `backgroundColor`/`marginless` on a plain `<Navbar.Item>`
|
|
691
|
+
// passed through as rbx wrote them while the same props on its
|
|
692
|
+
// `Navbar.Link` sibling migrated. The rename is a no-op (rbx and
|
|
693
|
+
// bestax spell it the same), which is why it looked right.
|
|
694
|
+
target: 'Navbar.Item',
|
|
683
695
|
status: 'mapped',
|
|
684
696
|
special: 'navbar-item',
|
|
685
697
|
props: { active: {}, onClick: {}, as: AS_OK },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/rbx/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/rbx/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA4DvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA0oCpB"}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { BADGE_PROPS, MAPPING, TOOLTIP_PROPS, UNIVERSAL_PROPS, resolveMapping, } from './mapping.js';
|
|
23
23
|
import { addTodo, attributesOf, findAttr, jsxNameParts, literalValueOf, removeAttr, renameElement, } from '../_shared/jsx-utils.js';
|
|
24
24
|
import { applyPropAction, applyUniversalProps } from '../_shared/props.js';
|
|
25
|
+
import { enforcePolymorphicProps } from '../_shared/polymorphic.js';
|
|
25
26
|
import { collectBoundNames, makeAliasRegistry, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
26
27
|
import { flattenResponsiveProps, RESPONSIVE_KINDS } from './responsive.js';
|
|
27
28
|
import { runSpecial } from './specials.js';
|
|
@@ -560,6 +561,12 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
560
561
|
}
|
|
561
562
|
}
|
|
562
563
|
applyUniversalProps(ctx, path, element, handled, UNIVERSAL_PROPS);
|
|
564
|
+
// After every prop pass: bestax's props follow `as`, so the element
|
|
565
|
+
// decides which of the props just written it can actually take. The
|
|
566
|
+
// wrappers below move neither `as` nor `href`, so this reads the final
|
|
567
|
+
// pair either way.
|
|
568
|
+
// rbx spells its element prop `as`, so a spread here really can carry one.
|
|
569
|
+
enforcePolymorphicProps(ctx, path, element, target, true);
|
|
563
570
|
// ---- 2a. badge/tooltip helper props → wrapping components ------------
|
|
564
571
|
// Last, so the inner element is already fully migrated. Both families are
|
|
565
572
|
// read off `element` — the props live there whichever wrapper is built
|
|
@@ -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,
|
|
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,CA6pBpD,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"}
|
|
@@ -345,6 +345,8 @@ export const MAPPING = {
|
|
|
345
345
|
status: 'mapped',
|
|
346
346
|
target: 'Control',
|
|
347
347
|
props: {
|
|
348
|
+
// `Control` declares `as` too, narrowed to div/p.
|
|
349
|
+
renderAs: { rename: 'as' },
|
|
348
350
|
fullwidth: { booleanToProp: { name: 'isExpanded' } },
|
|
349
351
|
loading: { booleanToProp: { name: 'isLoading' } },
|
|
350
352
|
iconType: {
|
|
@@ -470,6 +472,11 @@ export const MAPPING = {
|
|
|
470
472
|
target: 'Image',
|
|
471
473
|
special: 'image',
|
|
472
474
|
props: {
|
|
475
|
+
// bestax's `Image` does declare an `as`, narrowed to figure/div/p, so
|
|
476
|
+
// the universal `renderAs` TODO ("this bestax component has no `as`
|
|
477
|
+
// prop") was a false claim about the library and left the prop on the
|
|
478
|
+
// element. Renaming lets the narrowed-union check judge it.
|
|
479
|
+
renderAs: { rename: 'as' },
|
|
473
480
|
rounded: { booleanToProp: { name: 'isRounded' } },
|
|
474
481
|
fallback: {
|
|
475
482
|
todo: 'no fallback prop in bestax Image; handle onError yourself',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAQL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,aAAa,EAAE,CAAC;AA+yB9B,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,GACX,aAAa,CAIf;AAED,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAGjE,CAAC"}
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { RESPONSIVE_BREAKPOINTS, UNIVERSAL_PROPS } from './mapping.js';
|
|
11
11
|
import { addAttr, addTodo, attributesOf, buildJsxName, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, resolveBooleanish, } from '../_shared/jsx-utils.js';
|
|
12
|
-
import {
|
|
12
|
+
import { enforcePolymorphicProps } from '../_shared/polymorphic.js';
|
|
13
|
+
import { alignTarget, dropLinkAttrsForPlainTag, makeStripModifierProps, mergeClassName, applyIconProps, parseIconClasses, } from '../_shared/specials-utils.js';
|
|
13
14
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
15
|
/**
|
|
15
16
|
* The attributes a plain-element rewrite keeps: the modifier strip decides
|
|
@@ -18,7 +19,12 @@ import { alignTarget, makeStripModifierProps, mergeClassName, applyIconProps, pa
|
|
|
18
19
|
* takes as readily as the component did. `attributesOf` filters spreads out,
|
|
19
20
|
* so passing its result alone dropped `{...rest}` with no TODO.
|
|
20
21
|
*/
|
|
21
|
-
function keptAttrs(ctx, path, element, where) {
|
|
22
|
+
function keptAttrs(ctx, path, element, where, tag) {
|
|
23
|
+
// These replacements are built by hand rather than through
|
|
24
|
+
// `replaceWithPlain`, so the link-attribute cleanup has to be invoked here
|
|
25
|
+
// or it never runs for this source: `<Form.Help href="/x">` was becoming
|
|
26
|
+
// `<p className="help" href="/x">`.
|
|
27
|
+
dropLinkAttrsForPlainTag(ctx, path, element, tag, where);
|
|
22
28
|
const kept = new Set(stripModifierProps(ctx, path, attributesOf(element), where));
|
|
23
29
|
return (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute' || kept.has(a));
|
|
24
30
|
}
|
|
@@ -135,6 +141,10 @@ const SPECIALS = {
|
|
|
135
141
|
if (headingTruthy && !subtitleDynamic) {
|
|
136
142
|
removeAttr(element, headingAttr);
|
|
137
143
|
const className = mergeClassName(ctx, path, element, subtitleTruthy ? 'heading subtitle' : 'heading', 'Heading');
|
|
144
|
+
// The seventh hand-built plain rewrite in this file, and the one that
|
|
145
|
+
// does not go through `keptAttrs` -- so it needs the cleanup by name or
|
|
146
|
+
// `<Heading renderAs="a" href="/x">` lands an `href` on the `<p>`.
|
|
147
|
+
dropLinkAttrsForPlainTag(ctx, path, element, 'p', 'Heading');
|
|
138
148
|
const consumed = new Set(stripModifierProps(ctx, path, attributesOf(element).filter(a => !['size', 'weight', 'spaced', 'subtitle'].includes(a.name.name)), 'Heading'));
|
|
139
149
|
const rest = (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute' || consumed.has(a));
|
|
140
150
|
const replacement = plainElement(ctx.j, 'p', className, rest, element.children ?? []);
|
|
@@ -179,14 +189,33 @@ const SPECIALS = {
|
|
|
179
189
|
* RBC Card.Image takes Image props directly; bestax Card.Image is a plain
|
|
180
190
|
* wrapper — move the props onto a new inner <Image>.
|
|
181
191
|
*/
|
|
182
|
-
'card-image'(ctx,
|
|
192
|
+
'card-image'(ctx, path, element) {
|
|
183
193
|
const children = (element.children ?? []).filter((c) => !(c.type === 'JSXText' && c.value.trim() === ''));
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
// These props are about to become an `<Image>`'s, so they are judged
|
|
195
|
+
// against `Image` and not against the `Card.Image` wrapper they are
|
|
196
|
+
// written on. The special returns `replaced`, so the shared pass never
|
|
197
|
+
// sees the child it creates -- `<Card.Image href="/x">` was moving the
|
|
198
|
+
// `href` onto an `<Image>` that declares none.
|
|
199
|
+
const imageAttrsOf = () => (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXAttribute' && a.name.name !== 'className');
|
|
200
|
+
if (children.length > 0 || imageAttrsOf().length === 0) {
|
|
201
|
+
// Already wrapping its own content — nothing to restructure, and
|
|
202
|
+
// nothing here is destined for an `<Image>`. Bail before touching the
|
|
203
|
+
// props: renaming `renderAs` on this path left an `as` on the
|
|
204
|
+
// `Card.Image` wrapper, which has none, and put it beyond the reach of
|
|
205
|
+
// the ordinary `renderAs` mapping that would have flagged it.
|
|
188
206
|
return {};
|
|
189
207
|
}
|
|
208
|
+
// `renderAs` is renamed first: the mapping does that for a plain `Image`,
|
|
209
|
+
// but this path never reaches the prop passes, so without it the check
|
|
210
|
+
// below would look for an `as` that is still spelled the RBC way.
|
|
211
|
+
const renderAsAttr = findAttr(element, 'renderAs');
|
|
212
|
+
if (renderAsAttr)
|
|
213
|
+
renderAsAttr.name = ctx.j.jsxIdentifier('as');
|
|
214
|
+
enforcePolymorphicProps(ctx, path, element, 'Image');
|
|
215
|
+
// Read the attributes back AFTER that: it removes the ones `Image` cannot
|
|
216
|
+
// take, and a list captured beforehand would carry the removed nodes onto
|
|
217
|
+
// the child regardless.
|
|
218
|
+
const imageAttrs = imageAttrsOf();
|
|
190
219
|
for (const attr of imageAttrs) {
|
|
191
220
|
if (attr.name.name === 'size') {
|
|
192
221
|
const literal = literalValueOf(attr);
|
|
@@ -199,7 +228,7 @@ const SPECIALS = {
|
|
|
199
228
|
}
|
|
200
229
|
}
|
|
201
230
|
const inner = ctx.j.jsxElement(ctx.j.jsxOpeningElement(ctx.j.jsxIdentifier(ctx.reserve('Image')), imageAttrs, true), null, []);
|
|
202
|
-
element.openingElement.attributes =
|
|
231
|
+
element.openingElement.attributes = (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXAttribute' && a.name.name === 'className');
|
|
203
232
|
element.children = [inner];
|
|
204
233
|
element.openingElement.selfClosing = false;
|
|
205
234
|
// The walker's rename pass rewrites both names to the final (possibly
|
|
@@ -319,7 +348,7 @@ const SPECIALS = {
|
|
|
319
348
|
}
|
|
320
349
|
}
|
|
321
350
|
className = mergeClassName(ctx, path, element, className, 'Form.Label');
|
|
322
|
-
const rest = keptAttrs(ctx, path, element, 'Form.Label');
|
|
351
|
+
const rest = keptAttrs(ctx, path, element, 'Form.Label', 'label');
|
|
323
352
|
path.replace(plainElement(ctx.j, 'label', className, rest, element.children ?? []));
|
|
324
353
|
ctx.dirty = true;
|
|
325
354
|
return { replaced: true };
|
|
@@ -340,7 +369,7 @@ const SPECIALS = {
|
|
|
340
369
|
}
|
|
341
370
|
}
|
|
342
371
|
className = mergeClassName(ctx, path, element, className, 'Form.Help');
|
|
343
|
-
const rest = keptAttrs(ctx, path, element, 'Form.Help');
|
|
372
|
+
const rest = keptAttrs(ctx, path, element, 'Form.Help', 'p');
|
|
344
373
|
path.replace(plainElement(ctx.j, 'p', className, rest, element.children ?? []));
|
|
345
374
|
ctx.dirty = true;
|
|
346
375
|
return { replaced: true };
|
|
@@ -394,7 +423,7 @@ const SPECIALS = {
|
|
|
394
423
|
/** RBC Loader is a plain <div class="loader"> — keep exactly that. */
|
|
395
424
|
'plain-loader'(ctx, path, element) {
|
|
396
425
|
const className = mergeClassName(ctx, path, element, 'loader', 'Loader');
|
|
397
|
-
const rest = keptAttrs(ctx, path, element, 'Loader');
|
|
426
|
+
const rest = keptAttrs(ctx, path, element, 'Loader', 'div');
|
|
398
427
|
path.replace(plainElement(ctx.j, 'div', className, rest, element.children ?? []));
|
|
399
428
|
ctx.dirty = true;
|
|
400
429
|
return { replaced: true };
|
|
@@ -441,7 +470,7 @@ const SPECIALS = {
|
|
|
441
470
|
removeAttr(element, activeAttr);
|
|
442
471
|
}
|
|
443
472
|
className = mergeClassName(ctx, path, element, className, 'Panel.Tabs.Tab');
|
|
444
|
-
const rest = keptAttrs(ctx, path, element, 'Panel.Tabs.Tab');
|
|
473
|
+
const rest = keptAttrs(ctx, path, element, 'Panel.Tabs.Tab', 'a');
|
|
445
474
|
path.replace(plainElement(ctx.j, 'a', className, rest, element.children ?? []));
|
|
446
475
|
ctx.dirty = true;
|
|
447
476
|
return { replaced: true };
|
|
@@ -461,7 +490,7 @@ const SPECIALS = {
|
|
|
461
490
|
removeAttr(element, activeAttr);
|
|
462
491
|
}
|
|
463
492
|
liClass = mergeClassName(ctx, path, element, liClass, 'Breadcrumb.Item');
|
|
464
|
-
const anchorAttrs = keptAttrs(ctx, path, element, 'Breadcrumb.Item');
|
|
493
|
+
const anchorAttrs = keptAttrs(ctx, path, element, 'Breadcrumb.Item', 'a');
|
|
465
494
|
const children = element.children ?? [];
|
|
466
495
|
const solidChildren = children.filter((c) => !(c.type === 'JSXText' && c.value.trim() === ''));
|
|
467
496
|
// `<Breadcrumb.Item><a href>…</a></Breadcrumb.Item>` already carries its
|
|
@@ -497,7 +526,7 @@ const SPECIALS = {
|
|
|
497
526
|
return { replaced: true };
|
|
498
527
|
}
|
|
499
528
|
const className = mergeClassName(ctx, path, element, 'table-container', 'Table.Container');
|
|
500
|
-
const rest = keptAttrs(ctx, path, element, 'Table.Container');
|
|
529
|
+
const rest = keptAttrs(ctx, path, element, 'Table.Container', 'div');
|
|
501
530
|
path.replace(plainElement(ctx.j, 'div', className, rest, element.children ?? []));
|
|
502
531
|
ctx.dirty = true;
|
|
503
532
|
return { replaced: true };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA6CvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CAy8BpB"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { MAPPING, UNIVERSAL_PROPS, resolveMapping } from './mapping.js';
|
|
17
17
|
import { addTodo, attributesOf, jsxNameParts, renameElement, } from '../_shared/jsx-utils.js';
|
|
18
18
|
import { applyPropAction, applyUniversalProps } from '../_shared/props.js';
|
|
19
|
+
import { enforcePolymorphicProps } from '../_shared/polymorphic.js';
|
|
19
20
|
import { collectBoundNames, makeAliasRegistry, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
20
21
|
import { flattenResponsiveProps } from './responsive.js';
|
|
21
22
|
import { RESPONSIVE_KINDS, runSpecial } from './specials.js';
|
|
@@ -451,6 +452,9 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
451
452
|
}
|
|
452
453
|
}
|
|
453
454
|
applyUniversalProps(ctx, path, element, handled, UNIVERSAL_PROPS);
|
|
455
|
+
// Last: bestax's props follow `as`, so the element decides which of the
|
|
456
|
+
// props just written it can actually take.
|
|
457
|
+
enforcePolymorphicProps(ctx, path, element, target);
|
|
454
458
|
});
|
|
455
459
|
// ---- 2b. Value references to RBC components ---------------------------
|
|
456
460
|
// Components can be referenced as plain values too (`renderAs={Block}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bestax-migrate",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
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.
|
|
41
|
+
"@allxsmith/bestax-bulma": "^5.15.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@jest/globals": "^30.0.0",
|