bestax-migrate 2.19.0 → 2.21.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/dist/sources/bulma-classes/class-map.d.ts +43 -6
- package/dist/sources/bulma-classes/class-map.d.ts.map +1 -1
- package/dist/sources/bulma-classes/class-map.js +121 -6
- package/dist/sources/bulma-classes/plan.d.ts +16 -0
- package/dist/sources/bulma-classes/plan.d.ts.map +1 -1
- package/dist/sources/bulma-classes/plan.js +98 -21
- package/dist/sources/bulma-classes/rules.d.ts.map +1 -1
- package/dist/sources/bulma-classes/rules.js +2 -2
- package/dist/sources/bulma-classes/transform.d.ts.map +1 -1
- package/dist/sources/bulma-classes/transform.js +170 -5
- package/package.json +1 -1
|
@@ -58,9 +58,28 @@ export interface Absorbs {
|
|
|
58
58
|
* Where the target puts the attributes it is given. On the child
|
|
59
59
|
* (`SelectBase`): the child's become the target's, and the element may
|
|
60
60
|
* carry none but a `key`. On the element (`Breadcrumb`): the element's
|
|
61
|
-
* stay, and the child may carry none.
|
|
61
|
+
* stay, and the child may carry none. Split (`Menu.Item`): the ones in
|
|
62
|
+
* `elementProps` go on the element and the rest on the child, so each may
|
|
63
|
+
* carry only its own share.
|
|
62
64
|
*/
|
|
63
|
-
readonly attributesOn: 'child' | 'element';
|
|
65
|
+
readonly attributesOn: 'child' | 'element' | 'split';
|
|
66
|
+
/**
|
|
67
|
+
* With the attributes split: the ones the target puts on the element. The
|
|
68
|
+
* element's `key` stays with the component in its place.
|
|
69
|
+
*/
|
|
70
|
+
readonly elementProps?: readonly string[];
|
|
71
|
+
/**
|
|
72
|
+
* An element the target renders after the child, from among its children
|
|
73
|
+
* (`Menu.Item`'s nested list, a `Menu.List`). The element converts beside
|
|
74
|
+
* one of these after the child, bare, and nothing else: it becomes
|
|
75
|
+
* `target` and moves in after the child's children. That target renders no
|
|
76
|
+
* class of its own only inside another one, so the element converts with
|
|
77
|
+
* it only inside one too.
|
|
78
|
+
*/
|
|
79
|
+
readonly after?: {
|
|
80
|
+
readonly tag: string;
|
|
81
|
+
readonly target: string;
|
|
82
|
+
};
|
|
64
83
|
/**
|
|
65
84
|
* With the attributes on the element: the child's that the target takes as
|
|
66
85
|
* props of its own and writes back on the child (`Image`'s `src` and `alt`).
|
|
@@ -130,6 +149,12 @@ export interface RootEntry {
|
|
|
130
149
|
readonly untypedAttrs?: readonly string[];
|
|
131
150
|
/** The target's props type requires children. */
|
|
132
151
|
readonly requiresChildren?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* The target picks its render by whether it has children, so the element
|
|
154
|
+
* converts only around HTML elements written out: an expression could come
|
|
155
|
+
* out empty (`Icon` renders from `name` when `children` is `undefined`).
|
|
156
|
+
*/
|
|
157
|
+
readonly needsElementChildren?: boolean;
|
|
133
158
|
/**
|
|
134
159
|
* The target renders its children inside an element of its own (`Card`
|
|
135
160
|
* inside `.card-content`) unless one of them is one of these parts, so the
|
|
@@ -144,11 +169,15 @@ export interface RootEntry {
|
|
|
144
169
|
readonly when?: string;
|
|
145
170
|
};
|
|
146
171
|
/**
|
|
147
|
-
* The target provides context bestax
|
|
148
|
-
*
|
|
149
|
-
* stays markup: that component would render differently.
|
|
172
|
+
* The target provides context the bestax components inside it read (`what`
|
|
173
|
+
* it does, for the TODO), so an element around one already in the file
|
|
174
|
+
* stays markup: that component would render differently. `readBy` names
|
|
175
|
+
* them when not every bestax component does.
|
|
150
176
|
*/
|
|
151
|
-
readonly providesContext?:
|
|
177
|
+
readonly providesContext?: {
|
|
178
|
+
readonly what: string;
|
|
179
|
+
readonly readBy?: readonly string[];
|
|
180
|
+
};
|
|
152
181
|
/**
|
|
153
182
|
* The target renders its root class only when no other of itself is around
|
|
154
183
|
* it (`Menu.List` drops `.menu-list` when nested), so the element converts
|
|
@@ -243,6 +272,12 @@ export declare const VIEWPORTS: readonly ["mobile", "tablet", "tablet-only", "to
|
|
|
243
272
|
/** `tablet-only` → `TabletOnly`: the suffix bestax puts on per-viewport props. */
|
|
244
273
|
export declare function viewportSuffix(viewport: string): string;
|
|
245
274
|
export declare const ROOTS: Readonly<Record<string, RootEntry>>;
|
|
275
|
+
/**
|
|
276
|
+
* Elements found by where they sit rather than by a class, keyed by the root
|
|
277
|
+
* of the list around them: a `<li>` whose nearest element is a `.menu-list`,
|
|
278
|
+
* a `Menu.List`, or the bare `<ul>` nested in one of its items.
|
|
279
|
+
*/
|
|
280
|
+
export declare const PLACED: Readonly<Record<string, RootEntry>>;
|
|
246
281
|
/**
|
|
247
282
|
* Which root wins when an element carries two (`column box`): layout before
|
|
248
283
|
* surface, so the element keeps the role that decides where it sits. The
|
|
@@ -292,6 +327,8 @@ export declare const PASSTHROUGH: ReadonlyArray<{
|
|
|
292
327
|
export declare function passthroughReason(token: string): string | undefined;
|
|
293
328
|
/** The table's entry for a class. */
|
|
294
329
|
export declare function rootFor(token: string): RootEntry | undefined;
|
|
330
|
+
/** The entry for an element found by the root of the list around it. */
|
|
331
|
+
export declare function placedFor(list: string): RootEntry | undefined;
|
|
295
332
|
/**
|
|
296
333
|
* A class that decides what its element is, for a TODO: a root the table
|
|
297
334
|
* converts or a family's outermost class. Parts and `plain` roots are not.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-map.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/class-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,MAAM,GAChB;IAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAE9E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,uBAAuB;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB
|
|
1
|
+
{"version":3,"file":"class-map.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/class-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -672,7 +672,9 @@ export const ROOTS = {
|
|
|
672
672
|
whenEmpty: true,
|
|
673
673
|
when: 'is-horizontal',
|
|
674
674
|
},
|
|
675
|
-
providesContext:
|
|
675
|
+
providesContext: {
|
|
676
|
+
what: 'tells the bestax form controls inside it to skip wrappers of their own',
|
|
677
|
+
},
|
|
676
678
|
ownProps: [
|
|
677
679
|
'horizontal',
|
|
678
680
|
'grouped',
|
|
@@ -724,7 +726,9 @@ export const ROOTS = {
|
|
|
724
726
|
}),
|
|
725
727
|
...tokens('is-', ['small', 'medium', 'large'], 'size'),
|
|
726
728
|
},
|
|
727
|
-
providesContext:
|
|
729
|
+
providesContext: {
|
|
730
|
+
what: 'tells the bestax form controls inside it to skip wrappers of their own',
|
|
731
|
+
},
|
|
728
732
|
ownProps: [
|
|
729
733
|
'as',
|
|
730
734
|
'hasIconsLeft',
|
|
@@ -1225,7 +1229,32 @@ export const ROOTS = {
|
|
|
1225
1229
|
'dropdown-content': part(),
|
|
1226
1230
|
'dropdown-item': part(),
|
|
1227
1231
|
'dropdown-divider': part(),
|
|
1228
|
-
|
|
1232
|
+
// With children, `Icon` renders `.icon` around them as given, and writes an
|
|
1233
|
+
// `aria-label` (`"icon"` unless it's given one).
|
|
1234
|
+
icon: {
|
|
1235
|
+
...BASE,
|
|
1236
|
+
target: 'Icon',
|
|
1237
|
+
tag: 'span',
|
|
1238
|
+
modifiers: tokens('is-', ['small', 'medium', 'large'], 'size'),
|
|
1239
|
+
defaults: { 'aria-label': 'icon' },
|
|
1240
|
+
requiresChildren: true,
|
|
1241
|
+
needsElementChildren: true,
|
|
1242
|
+
ownProps: [
|
|
1243
|
+
'textColor',
|
|
1244
|
+
'bgColor',
|
|
1245
|
+
'color',
|
|
1246
|
+
'name',
|
|
1247
|
+
'library',
|
|
1248
|
+
'variant',
|
|
1249
|
+
'features',
|
|
1250
|
+
'libraryFeatures',
|
|
1251
|
+
'size',
|
|
1252
|
+
'ariaLabel',
|
|
1253
|
+
'icon',
|
|
1254
|
+
'containerClassName',
|
|
1255
|
+
],
|
|
1256
|
+
passThrough: ['style'],
|
|
1257
|
+
},
|
|
1229
1258
|
'icon-text': todo('IconText', 'bestax `IconText` pairs with `Icon`, which renders its own `<i>`'),
|
|
1230
1259
|
// `Image` renders `.image` and the `<img>` inside it together, from `src`
|
|
1231
1260
|
// and `alt`. A ratio stays a class: as `size`, it adds `has-ratio` too.
|
|
@@ -1284,7 +1313,8 @@ export const ROOTS = {
|
|
|
1284
1313
|
],
|
|
1285
1314
|
},
|
|
1286
1315
|
// `Menu`, `Menu.Label` and `Menu.List` take every helper prop, `color` and
|
|
1287
|
-
// `backgroundColor` included. The items inside a list
|
|
1316
|
+
// `backgroundColor` included. The items inside a list carry no class, so
|
|
1317
|
+
// `PLACED` finds them by where they sit.
|
|
1288
1318
|
menu: {
|
|
1289
1319
|
...BASE,
|
|
1290
1320
|
target: 'Menu',
|
|
@@ -1313,7 +1343,7 @@ export const ROOTS = {
|
|
|
1313
1343
|
topLevelOnly: true,
|
|
1314
1344
|
ownProps: [],
|
|
1315
1345
|
},
|
|
1316
|
-
'menu-item': todo('Menu.Item', 'bestax `Menu.Item` renders the `<li>`
|
|
1346
|
+
'menu-item': todo('Menu.Item', 'bestax `Menu.Item` renders the element inside its `<li>` with no class but `is-active`'),
|
|
1317
1347
|
message: todo('Message', 'bestax `Message` always wraps its children in `.message-body`'),
|
|
1318
1348
|
'message-header': part(),
|
|
1319
1349
|
'message-body': part(),
|
|
@@ -1506,7 +1536,59 @@ export const ROOTS = {
|
|
|
1506
1536
|
ownProps: ['active'],
|
|
1507
1537
|
},
|
|
1508
1538
|
'panel-icon': todo('Panel.Icon', 'bestax `Panel.Icon` renders through `Icon`, which always writes an `aria-label`'),
|
|
1509
|
-
|
|
1539
|
+
// With no `Tabs.Content` among its children, `Tabs` renders `.tabs` around
|
|
1540
|
+
// them as given. It has no text or background color prop, and its `color`
|
|
1541
|
+
// is deprecated, since Bulma styles no colored tabs.
|
|
1542
|
+
tabs: {
|
|
1543
|
+
...BASE,
|
|
1544
|
+
target: 'Tabs',
|
|
1545
|
+
tag: 'div',
|
|
1546
|
+
textColor: null,
|
|
1547
|
+
bgColor: null,
|
|
1548
|
+
modifiers: {
|
|
1549
|
+
...tokens('is-', ['centered', 'right', 'left'], 'align'),
|
|
1550
|
+
...tokens('is-', ['small', 'medium', 'large'], 'size'),
|
|
1551
|
+
...flags({
|
|
1552
|
+
'is-fullwidth': 'isFullwidth',
|
|
1553
|
+
'is-boxed': 'boxed',
|
|
1554
|
+
'is-toggle': 'toggle',
|
|
1555
|
+
'is-toggle-rounded': 'rounded',
|
|
1556
|
+
}),
|
|
1557
|
+
},
|
|
1558
|
+
omits: Object.fromEntries(COMPONENT_COLORS.map(color => [
|
|
1559
|
+
`is-${color}`,
|
|
1560
|
+
'`color` is deprecated, since Bulma styles no colored tabs',
|
|
1561
|
+
])),
|
|
1562
|
+
providesContext: {
|
|
1563
|
+
what: 'passes its active tab to the tabs and panels inside it, and renders differently around a `Tabs.Content`',
|
|
1564
|
+
readBy: [
|
|
1565
|
+
'Tabs.Tab',
|
|
1566
|
+
'Tab',
|
|
1567
|
+
'Tabs.Content',
|
|
1568
|
+
'TabsContent',
|
|
1569
|
+
'Tabs.Content.Item',
|
|
1570
|
+
'TabsContent.Item',
|
|
1571
|
+
'TabContentItem',
|
|
1572
|
+
],
|
|
1573
|
+
},
|
|
1574
|
+
ownProps: [
|
|
1575
|
+
'align',
|
|
1576
|
+
'size',
|
|
1577
|
+
'isFullwidth',
|
|
1578
|
+
'isFullWidth',
|
|
1579
|
+
'fullwidth',
|
|
1580
|
+
'boxed',
|
|
1581
|
+
'toggle',
|
|
1582
|
+
'rounded',
|
|
1583
|
+
'color',
|
|
1584
|
+
'value',
|
|
1585
|
+
'onChange',
|
|
1586
|
+
'defaultValue',
|
|
1587
|
+
'vertical',
|
|
1588
|
+
'side',
|
|
1589
|
+
'expanded',
|
|
1590
|
+
],
|
|
1591
|
+
},
|
|
1510
1592
|
// ---- Wrappers a component renders from a prop ---------------------------------
|
|
1511
1593
|
// Each renders the wrapper with its own class and nothing else, so the
|
|
1512
1594
|
// element folds only when it carries no attribute and no other class.
|
|
@@ -1552,6 +1634,34 @@ function part() {
|
|
|
1552
1634
|
function plain(why) {
|
|
1553
1635
|
return { status: 'plain', why };
|
|
1554
1636
|
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Elements found by where they sit rather than by a class, keyed by the root
|
|
1639
|
+
* of the list around them: a `<li>` whose nearest element is a `.menu-list`,
|
|
1640
|
+
* a `Menu.List`, or the bare `<ul>` nested in one of its items.
|
|
1641
|
+
*/
|
|
1642
|
+
export const PLACED = {
|
|
1643
|
+
// `Menu.Item` renders the `<li>` and the `<a>` inside it together. Its
|
|
1644
|
+
// helper props go on the `<a>`, so the `<li>`'s classes all stay classes.
|
|
1645
|
+
'menu-list': {
|
|
1646
|
+
...BASE,
|
|
1647
|
+
target: 'Menu.Item',
|
|
1648
|
+
tag: 'li',
|
|
1649
|
+
textColor: null,
|
|
1650
|
+
bgColor: null,
|
|
1651
|
+
noHelpers: true,
|
|
1652
|
+
requiresChildren: true,
|
|
1653
|
+
absorbs: {
|
|
1654
|
+
tag: 'a',
|
|
1655
|
+
attributesOn: 'split',
|
|
1656
|
+
elementProps: ['id', 'title', 'role', 'tabIndex', 'style', 'data-testid'],
|
|
1657
|
+
modifiers: flags({ 'is-active': 'active' }),
|
|
1658
|
+
after: { tag: 'ul', target: 'Menu.List' },
|
|
1659
|
+
},
|
|
1660
|
+
numberAttrs: ['tabIndex'],
|
|
1661
|
+
ownProps: ['as', 'active'],
|
|
1662
|
+
passThrough: ['id', 'title', 'role', 'tabIndex', 'style', 'data-testid'],
|
|
1663
|
+
},
|
|
1664
|
+
};
|
|
1555
1665
|
/**
|
|
1556
1666
|
* Which root wins when an element carries two (`column box`): layout before
|
|
1557
1667
|
* surface, so the element keeps the role that decides where it sits. The
|
|
@@ -1618,6 +1728,7 @@ export const PRECEDENCE = [
|
|
|
1618
1728
|
export const FORWARDS_REF = [
|
|
1619
1729
|
'Button',
|
|
1620
1730
|
'Link',
|
|
1731
|
+
'Menu.Item',
|
|
1621
1732
|
'Navbar',
|
|
1622
1733
|
'Navbar.Item',
|
|
1623
1734
|
'Control',
|
|
@@ -1821,6 +1932,10 @@ function own(record, key) {
|
|
|
1821
1932
|
export function rootFor(token) {
|
|
1822
1933
|
return own(ROOTS, token);
|
|
1823
1934
|
}
|
|
1935
|
+
/** The entry for an element found by the root of the list around it. */
|
|
1936
|
+
export function placedFor(list) {
|
|
1937
|
+
return own(PLACED, list);
|
|
1938
|
+
}
|
|
1824
1939
|
/**
|
|
1825
1940
|
* A class that decides what its element is, for a TODO: a root the table
|
|
1826
1941
|
* converts or a family's outermost class. Parts and `plain` roots are not.
|
|
@@ -77,6 +77,17 @@ export interface ElementFacts {
|
|
|
77
77
|
* reach into it with `cloneElement`.
|
|
78
78
|
*/
|
|
79
79
|
onlyChildOf?: string;
|
|
80
|
+
/**
|
|
81
|
+
* The root of the list the element is an item of (`menu-list`), when that
|
|
82
|
+
* list is one `PLACED` names: the element converts as that entry, whatever
|
|
83
|
+
* classes it carries.
|
|
84
|
+
*/
|
|
85
|
+
itemOf?: string;
|
|
86
|
+
/**
|
|
87
|
+
* The element's `className` holds no class, so it renders `class=""`
|
|
88
|
+
* (`tokens` is empty either way).
|
|
89
|
+
*/
|
|
90
|
+
emptyClass?: boolean;
|
|
80
91
|
}
|
|
81
92
|
export interface ChildFacts {
|
|
82
93
|
tag: string;
|
|
@@ -127,6 +138,11 @@ export interface Conversion {
|
|
|
127
138
|
renames: Array<[from: string, to: string]>;
|
|
128
139
|
/** Child attributes a prop above renders instead. */
|
|
129
140
|
drop: string[];
|
|
141
|
+
/**
|
|
142
|
+
* What the element after the child becomes (`Menu.List`), when it has
|
|
143
|
+
* one: it moves in after the child's children.
|
|
144
|
+
*/
|
|
145
|
+
after?: string;
|
|
130
146
|
};
|
|
131
147
|
/**
|
|
132
148
|
* The target renders the element's children itself, from a prop above:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* the whole element with a TODO (an attribute or tag the component would
|
|
10
10
|
* render differently).
|
|
11
11
|
*/
|
|
12
|
-
import { FORWARDS_REF, HELPER_PROPS, HELPER_TOKENS, inTagSet, legacyHint, modifierFor, PRECEDENCE, rootFor, WRAPPER_OWN_PROPS, wrapperFor, } from './class-map.js';
|
|
12
|
+
import { FORWARDS_REF, HELPER_PROPS, HELPER_TOKENS, inTagSet, legacyHint, modifierFor, placedFor, PRECEDENCE, rootFor, WRAPPER_OWN_PROPS, wrapperFor, } from './class-map.js';
|
|
13
13
|
import { ruleId } from './rules.js';
|
|
14
14
|
function wrapperEntry(tag) {
|
|
15
15
|
const target = wrapperFor(tag);
|
|
@@ -67,14 +67,24 @@ export function plan(facts) {
|
|
|
67
67
|
});
|
|
68
68
|
return { conversion: null, todos };
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
// An item of a list is what it is by where it sits, so its own classes all
|
|
71
|
+
// stay: none of them decides what it becomes.
|
|
72
|
+
const placed = facts.itemOf ? placedFor(facts.itemOf) : undefined;
|
|
73
|
+
const wrapper = placed
|
|
74
|
+
? undefined
|
|
75
|
+
: tokens.find(token => rootFor(token)?.status === 'fold');
|
|
71
76
|
if (wrapper)
|
|
72
77
|
return planFold(facts, wrapper, todos);
|
|
73
|
-
const root =
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
const root = placed
|
|
79
|
+
? undefined
|
|
80
|
+
: tokens
|
|
81
|
+
.filter(token => rootFor(token)?.status === 'mapped')
|
|
82
|
+
.sort((a, b) => precedence(a) - precedence(b))[0];
|
|
76
83
|
let entry;
|
|
77
|
-
if (
|
|
84
|
+
if (placed) {
|
|
85
|
+
entry = placed;
|
|
86
|
+
}
|
|
87
|
+
else if (root) {
|
|
78
88
|
entry = rootFor(root);
|
|
79
89
|
}
|
|
80
90
|
else {
|
|
@@ -102,12 +112,18 @@ export function plan(facts) {
|
|
|
102
112
|
if (facts.onlyChildOf) {
|
|
103
113
|
return refuse('only-child', target, `this element is the only child of \`<${facts.onlyChildOf}>\`, which may hand it props or a ref with \`cloneElement\` (next/link's legacy behavior, a tooltip, a Radix \`asChild\` trigger) that bestax \`${target}\` would not take the same way; convert it by hand if \`<${facts.onlyChildOf}>\` only renders its children`);
|
|
104
114
|
}
|
|
115
|
+
// An element converts with no class left only when found by where it sits,
|
|
116
|
+
// and then an empty `className` would go with nothing to render it.
|
|
117
|
+
if (placed && facts.emptyClass) {
|
|
118
|
+
return refuse('attr', 'className', `this element has an empty \`className\`, which renders \`class=""\`, and bestax \`${target}\` renders it with no class attribute; drop the empty \`className\`, then re-run`);
|
|
119
|
+
}
|
|
105
120
|
// The attributes the target is given: the element's, or its only child's
|
|
106
121
|
// when the target renders that child itself and puts them there.
|
|
107
122
|
let attributes = facts.attributes;
|
|
108
123
|
let absorbed;
|
|
109
124
|
if (entry.absorbs) {
|
|
110
|
-
const
|
|
125
|
+
const where = root ? `\`.${root}\`` : `the <${tag}>`;
|
|
126
|
+
const outcome = absorb(facts, where, entry, target, refuse);
|
|
111
127
|
if (!('todos' in outcome)) {
|
|
112
128
|
({ attributes, absorbed } = outcome);
|
|
113
129
|
}
|
|
@@ -172,8 +188,17 @@ export function plan(facts) {
|
|
|
172
188
|
if (entry.requiresChildren && !facts.hasChildren) {
|
|
173
189
|
return refuse('children', target, `bestax \`${target}\` requires children, and this element has none; keep it as markup`);
|
|
174
190
|
}
|
|
175
|
-
|
|
176
|
-
|
|
191
|
+
const provides = entry.providesContext;
|
|
192
|
+
const reader = provides &&
|
|
193
|
+
facts.bestaxInside?.find(name => !provides.readBy || provides.readBy.includes(name));
|
|
194
|
+
if (provides && reader) {
|
|
195
|
+
return refuse('context', target, `bestax \`${target}\` ${provides.what}, and this element already holds \`${reader}\`, which could render differently inside it; keep this element as markup, or convert it and check that component by hand`);
|
|
196
|
+
}
|
|
197
|
+
if (entry.needsElementChildren &&
|
|
198
|
+
facts.hasChildren &&
|
|
199
|
+
!facts.soleChild &&
|
|
200
|
+
!facts.childElements?.length) {
|
|
201
|
+
return refuse('children', target, `bestax \`${target}\` picks what it renders by whether it has children, and it can't be told from here that these never come out empty, so this converts only around HTML elements written out; keep it as markup`);
|
|
177
202
|
}
|
|
178
203
|
if (entry.adoptsIdFrom && !attributes.has('id')) {
|
|
179
204
|
// Context follows the render tree, not the file: a component of the app
|
|
@@ -301,7 +326,7 @@ export function plan(facts) {
|
|
|
301
326
|
}
|
|
302
327
|
}
|
|
303
328
|
// A wrapper exists only to carry helper props; with none, leave the tag.
|
|
304
|
-
if (!root && writes.size === 0)
|
|
329
|
+
if (!root && !placed && writes.size === 0)
|
|
305
330
|
return { conversion: null, todos };
|
|
306
331
|
// ---- The tag ------------------------------------------------------------
|
|
307
332
|
let renders = entry.tag;
|
|
@@ -380,24 +405,46 @@ export function plan(facts) {
|
|
|
380
405
|
};
|
|
381
406
|
}
|
|
382
407
|
/** `a`, `a or b`, `a, b or c`, each in backticks. */
|
|
383
|
-
function orList(names) {
|
|
408
|
+
function orList(names, word = 'or') {
|
|
384
409
|
const quoted = names.map(name => `\`${name}\``);
|
|
385
410
|
return quoted.length > 1
|
|
386
|
-
? `${quoted.slice(0, -1).join(', ')}
|
|
411
|
+
? `${quoted.slice(0, -1).join(', ')} ${word} ${quoted[quoted.length - 1]}`
|
|
387
412
|
: quoted[0];
|
|
388
413
|
}
|
|
414
|
+
/** `a`, `a and b`, `a, b and c`, each in backticks. */
|
|
415
|
+
function andList(names) {
|
|
416
|
+
return orList(names, 'and');
|
|
417
|
+
}
|
|
389
418
|
/**
|
|
390
419
|
* The only child an entry's target renders itself (`.select`'s `<select>`):
|
|
391
420
|
* what its classes become, and the attributes the target is given. It
|
|
392
421
|
* converts with the element only when the target would render it exactly
|
|
393
|
-
* as written.
|
|
422
|
+
* as written. `where` names the element for a message.
|
|
394
423
|
*/
|
|
395
|
-
function absorb(facts,
|
|
424
|
+
function absorb(facts, where, entry, target, refuse) {
|
|
396
425
|
const spec = entry.absorbs;
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
426
|
+
const after = spec.after;
|
|
427
|
+
// With an element after the child, the two are the element's children.
|
|
428
|
+
const [child, next, ...more] = after
|
|
429
|
+
? (facts.childElements ?? [])
|
|
430
|
+
: [facts.soleChild];
|
|
431
|
+
const itself = `bestax \`${target}\` renders the <${spec.tag}> inside ${where} itself`;
|
|
432
|
+
if (child?.tag !== spec.tag ||
|
|
433
|
+
more.length > 0 ||
|
|
434
|
+
(next && next.tag !== after?.tag)) {
|
|
435
|
+
return refuse('children', target, after
|
|
436
|
+
? `${itself}, and a \`${after.target}\` after it from among its children, so this element converts only around a single <${spec.tag}>, and at most one <${after.tag}> after it, with nothing else beside them`
|
|
437
|
+
: `${itself}, so this element converts only around a single <${spec.tag}>, with nothing else beside it`);
|
|
438
|
+
}
|
|
439
|
+
if (next &&
|
|
440
|
+
(next.tokens !== undefined ||
|
|
441
|
+
next.attributes.size > 0 ||
|
|
442
|
+
next.hasSpread ||
|
|
443
|
+
next.isEmpty)) {
|
|
444
|
+
return refuse('children', after.target, `${itself}, and the <${after.tag}> after it as a \`${after.target}\`, which this converts only bare and holding its items, as Bulma nests one; keep this element as markup, or convert it by hand`);
|
|
445
|
+
}
|
|
446
|
+
if (entry.requiresChildren && child.isEmpty && !next) {
|
|
447
|
+
return refuse('children', target, `bestax \`${target}\` requires children, and renders the <${spec.tag}>'s as its own, so this element converts only when the <${spec.tag}> inside holds something; keep it as markup`);
|
|
401
448
|
}
|
|
402
449
|
if (child.hasSpread) {
|
|
403
450
|
return refuse('spread', target, `the <${spec.tag}> inside spreads props, which bestax \`${target}\` may read differently than the <${spec.tag}> did; convert the two to \`${target}\` by hand`);
|
|
@@ -422,6 +469,38 @@ function absorb(facts, root, entry, target, refuse) {
|
|
|
422
469
|
props.push([write.prop, write.value ?? true]);
|
|
423
470
|
}
|
|
424
471
|
}
|
|
472
|
+
// A number literal is carried over as written, like any other expression.
|
|
473
|
+
const given = (value) => typeof value === 'number' ? null : value;
|
|
474
|
+
if (spec.attributesOn === 'split') {
|
|
475
|
+
// Each keeps its own share: the target puts `elementProps` on the
|
|
476
|
+
// element, and everything else on the child.
|
|
477
|
+
const onElement = spec.elementProps ?? [];
|
|
478
|
+
const stray = [...facts.attributes.keys()].find(name => name !== 'key' && !onElement.includes(name));
|
|
479
|
+
if (stray !== undefined) {
|
|
480
|
+
return refuse('attr', stray, `bestax \`${target}\` puts ${andList(['className', ...onElement])} on the <${facts.tag}>, and everything else on the <${spec.tag}> inside, so this element's \`${stray}\` would move there; keep this element as markup`);
|
|
481
|
+
}
|
|
482
|
+
const moved = [...child.attributes.keys()].find(name => name === 'key' || onElement.includes(name));
|
|
483
|
+
if (moved !== undefined) {
|
|
484
|
+
return refuse('attr', moved, moved === 'key'
|
|
485
|
+
? `the <${spec.tag}> inside has a \`key\`, which would move up to bestax \`${target}\` in place of this element's; keep this element as markup`
|
|
486
|
+
: `bestax \`${target}\` puts \`${moved}\` on the <${facts.tag}>, so the <${spec.tag}>'s would move there; keep this element as markup`);
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
attributes: new Map([
|
|
490
|
+
...facts.attributes,
|
|
491
|
+
...[...child.attributes].map(([name, value]) => [
|
|
492
|
+
name,
|
|
493
|
+
given(value),
|
|
494
|
+
]),
|
|
495
|
+
]),
|
|
496
|
+
absorbed: {
|
|
497
|
+
props,
|
|
498
|
+
renames: [],
|
|
499
|
+
drop: [],
|
|
500
|
+
...(next && { after: after.target }),
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
}
|
|
425
504
|
if (spec.attributesOn === 'element') {
|
|
426
505
|
// The ones the target takes as its own props move up with the child's
|
|
427
506
|
// other props, and it writes them back on the child.
|
|
@@ -439,7 +518,7 @@ function absorb(facts, root, entry, target, refuse) {
|
|
|
439
518
|
// nothing but its `key`, which stays with the component in its place.
|
|
440
519
|
const own = [...facts.attributes.keys()].find(name => name !== 'key');
|
|
441
520
|
if (own !== undefined) {
|
|
442
|
-
return refuse('attr', own, `bestax \`${target}\` puts the attributes it is given on the <${spec.tag}> inside
|
|
521
|
+
return refuse('attr', own, `bestax \`${target}\` puts the attributes it is given on the <${spec.tag}> inside ${where}, so this element's \`${own}\` would move there; move it onto the <${spec.tag}> if that is what you want, then re-run`);
|
|
443
522
|
}
|
|
444
523
|
if (child.attributes.has('key')) {
|
|
445
524
|
return refuse('attr', 'key', `the <${spec.tag}> inside has a \`key\`, which would move up to bestax \`${target}\` in place of this element's; keep this element as markup`);
|
|
@@ -454,8 +533,6 @@ function absorb(facts, root, entry, target, refuse) {
|
|
|
454
533
|
return refuse('attr', name, `bestax \`${target}\` renders \`${name}\` on the <${spec.tag}> and \`.${token}\` on this element together, from one prop, so the two convert only as a pair, with \`${name}\` written bare; keep this element as markup`);
|
|
455
534
|
}
|
|
456
535
|
}
|
|
457
|
-
// A number literal is carried over as written, like any other expression.
|
|
458
|
-
const given = (value) => typeof value === 'number' ? null : value;
|
|
459
536
|
const attributes = new Map();
|
|
460
537
|
const renames = [];
|
|
461
538
|
for (const [name, value] of child.attributes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,eAAO,MAAM,KAAK,EAAE,WAAW,CAAC,MAAM,CAapC,CAAC;AA2BH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK1D"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* tests sweep every refusal the table can produce and hold each id to the
|
|
11
11
|
* vocabulary, so a gap is still caught.
|
|
12
12
|
*/
|
|
13
|
-
import { HELPER_PROPS, LEGACY_09, ROOTS, WRAPPERS, WRAPPER_OWN_PROPS, } from './class-map.js';
|
|
13
|
+
import { HELPER_PROPS, LEGACY_09, PLACED, ROOTS, WRAPPERS, WRAPPER_OWN_PROPS, } from './class-map.js';
|
|
14
14
|
export const KINDS = new Set([
|
|
15
15
|
'attr',
|
|
16
16
|
'children',
|
|
@@ -28,7 +28,7 @@ export const KINDS = new Set([
|
|
|
28
28
|
const VOCABULARY = new Set([
|
|
29
29
|
...Object.keys(ROOTS),
|
|
30
30
|
...Object.keys(LEGACY_09),
|
|
31
|
-
...Object.values(ROOTS).flatMap(entry => [
|
|
31
|
+
...[...Object.values(ROOTS), ...Object.values(PLACED)].flatMap(entry => [
|
|
32
32
|
...(entry.target ? [entry.target] : []),
|
|
33
33
|
...(entry.ownProps ?? []),
|
|
34
34
|
...(entry.passThrough ?? []),
|
|
@@ -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;AA+
|
|
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+xBvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CA2nBpB"}
|
|
@@ -23,7 +23,7 @@ import { rewriteStylesheetImports } from '../_shared/css-imports.js';
|
|
|
23
23
|
import { BESTAX, buildBestaxImport, placeBestaxImport, seedBestaxImport, } from '../_shared/bestax-import.js';
|
|
24
24
|
import { plan, } from './plan.js';
|
|
25
25
|
import { REACT_RUNTIMES, } from './project.js';
|
|
26
|
-
import { ROOTS, rootFor } from './class-map.js';
|
|
26
|
+
import { PLACED, placedFor, ROOTS, rootFor } from './class-map.js';
|
|
27
27
|
import { ruleId } from './rules.js';
|
|
28
28
|
/**
|
|
29
29
|
* A part imported under its flat export (`MenuList`, `FieldLabel`) is the
|
|
@@ -86,6 +86,11 @@ function afterTagName(comment) {
|
|
|
86
86
|
function spliceClassName(element, replacement, carried = []) {
|
|
87
87
|
const attrs = element.openingElement.attributes;
|
|
88
88
|
const classAttr = findAttr(element, 'className');
|
|
89
|
+
// An element found by where it sits may have no className to replace.
|
|
90
|
+
if (!classAttr) {
|
|
91
|
+
attrs.push(...replacement);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
89
94
|
const kept = replacement.includes(classAttr);
|
|
90
95
|
const moving = [...(kept ? [] : (classAttr.comments ?? [])), ...carried];
|
|
91
96
|
if (moving.length > 0) {
|
|
@@ -549,6 +554,67 @@ function reachesReact(child) {
|
|
|
549
554
|
return isContent(child);
|
|
550
555
|
return /[^ \t\r\n]/.test(child.value) || !/[\r\n]/.test(child.value);
|
|
551
556
|
}
|
|
557
|
+
/**
|
|
558
|
+
* The string a JSX text renders: each line trimmed where it meets a line
|
|
559
|
+
* break, blank lines dropped, and the rest joined by a space.
|
|
560
|
+
*/
|
|
561
|
+
function jsxTextRenders(value) {
|
|
562
|
+
const lines = value.replace(/\t/g, ' ').split(/\r\n|\n|\r/);
|
|
563
|
+
let lastFilled = 0;
|
|
564
|
+
lines.forEach((line, index) => {
|
|
565
|
+
if (/[^ ]/.test(line))
|
|
566
|
+
lastFilled = index;
|
|
567
|
+
});
|
|
568
|
+
let out = '';
|
|
569
|
+
lines.forEach((line, index) => {
|
|
570
|
+
let trimmed = line;
|
|
571
|
+
if (index > 0)
|
|
572
|
+
trimmed = trimmed.replace(/^ +/, '');
|
|
573
|
+
if (index < lines.length - 1)
|
|
574
|
+
trimmed = trimmed.replace(/ +$/, '');
|
|
575
|
+
if (trimmed)
|
|
576
|
+
out += index === lastFilled ? trimmed : `${trimmed} `;
|
|
577
|
+
});
|
|
578
|
+
return out;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* `children` with `next` after them, each on a line of its own. The list
|
|
582
|
+
* is new, so recast prints it afresh, and it prints a text with its leading
|
|
583
|
+
* whitespace gone and its entities decoded (`<` as `<`); a text that
|
|
584
|
+
* would render differently that way goes in as the string it renders.
|
|
585
|
+
*/
|
|
586
|
+
function withAfter(j, children, next) {
|
|
587
|
+
const line = () => j.jsxText('\n');
|
|
588
|
+
const kept = children.flatMap((child) => {
|
|
589
|
+
if (child.type !== 'JSXText')
|
|
590
|
+
return [child];
|
|
591
|
+
const renders = jsxTextRenders(child.value);
|
|
592
|
+
if (renders === '')
|
|
593
|
+
return /[\r\n]/.test(child.value) ? [line()] : [];
|
|
594
|
+
// No raw text to compare means no telling it holds no entity.
|
|
595
|
+
const plain = child.extra?.raw === child.value &&
|
|
596
|
+
jsxTextRenders(child.value.trim()) === renders;
|
|
597
|
+
return [
|
|
598
|
+
plain
|
|
599
|
+
? j.jsxText(child.value.trim())
|
|
600
|
+
: j.jsxExpressionContainer(j.stringLiteral(renders)),
|
|
601
|
+
];
|
|
602
|
+
});
|
|
603
|
+
const blank = (node) => node?.type === 'JSXText' && node.value === '\n';
|
|
604
|
+
while (blank(kept[0]))
|
|
605
|
+
kept.shift();
|
|
606
|
+
while (blank(kept[kept.length - 1]))
|
|
607
|
+
kept.pop();
|
|
608
|
+
return [...(kept.length > 0 ? [line(), ...kept] : []), line(), next, line()];
|
|
609
|
+
}
|
|
610
|
+
/** A call to an array's `map` or `flatMap`, whose results render in place. */
|
|
611
|
+
function isMapCall(call) {
|
|
612
|
+
const callee = call.callee;
|
|
613
|
+
return ((callee?.type === 'MemberExpression' ||
|
|
614
|
+
callee?.type === 'OptionalMemberExpression') &&
|
|
615
|
+
!callee.computed &&
|
|
616
|
+
(callee.property?.name === 'map' || callee.property?.name === 'flatMap'));
|
|
617
|
+
}
|
|
552
618
|
/** Whitespace React drops, which nothing is lost with. A comment is kept. */
|
|
553
619
|
function dropped(child) {
|
|
554
620
|
return child.type === 'JSXText' && !reachesReact(child);
|
|
@@ -776,6 +842,51 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
776
842
|
}
|
|
777
843
|
return { bestax, other, classes };
|
|
778
844
|
};
|
|
845
|
+
/**
|
|
846
|
+
* The root of the list an element is an item of, when `PLACED` names it:
|
|
847
|
+
* a `<li>` whose nearest element is a `.menu-list`, a `Menu.List` already
|
|
848
|
+
* in the file, or the bare `<ul>` nested directly in one of those items.
|
|
849
|
+
*/
|
|
850
|
+
const itemOf = (elementPath) => {
|
|
851
|
+
const name = elementPath.node.openingElement.name;
|
|
852
|
+
if (name.type !== 'JSXIdentifier')
|
|
853
|
+
return undefined;
|
|
854
|
+
let up = elementPath.parent;
|
|
855
|
+
while (up && up.node?.type !== 'JSXElement') {
|
|
856
|
+
// Handed to a function, the element renders wherever that puts it;
|
|
857
|
+
// an array's own `map` renders it in place.
|
|
858
|
+
if (up.node?.type === 'CallExpression' && !isMapCall(up.node)) {
|
|
859
|
+
return undefined;
|
|
860
|
+
}
|
|
861
|
+
up = up.parent;
|
|
862
|
+
}
|
|
863
|
+
const root = up ? listRoot(up) : undefined;
|
|
864
|
+
return root && placedFor(root).tag === name.name ? root : undefined;
|
|
865
|
+
};
|
|
866
|
+
const listRoot = (listPath) => {
|
|
867
|
+
const list = listPath.node;
|
|
868
|
+
const target = bestaxTarget(list, listPath);
|
|
869
|
+
if (target) {
|
|
870
|
+
return Object.keys(PLACED).find(root => rootFor(root)?.target === target);
|
|
871
|
+
}
|
|
872
|
+
const name = list.openingElement.name;
|
|
873
|
+
if (name.type !== 'JSXIdentifier')
|
|
874
|
+
return undefined;
|
|
875
|
+
const classAttr = findAttr(list, 'className');
|
|
876
|
+
if (classAttr) {
|
|
877
|
+
const root = (staticClassName(classAttr) ?? '')
|
|
878
|
+
.split(/\s+/)
|
|
879
|
+
.find(token => placedFor(token));
|
|
880
|
+
return root && rootFor(root)?.tag === name.name ? root : undefined;
|
|
881
|
+
}
|
|
882
|
+
const holder = listPath.parent;
|
|
883
|
+
if (holder?.node?.type !== 'JSXElement')
|
|
884
|
+
return undefined;
|
|
885
|
+
const root = itemOf(holder);
|
|
886
|
+
return root && placedFor(root).absorbs?.after?.tag === name.name
|
|
887
|
+
? root
|
|
888
|
+
: undefined;
|
|
889
|
+
};
|
|
779
890
|
// `converts` is whether the element would become a component with its
|
|
780
891
|
// classes written out, so it holds for a computed className too.
|
|
781
892
|
const elements = [];
|
|
@@ -789,10 +900,11 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
789
900
|
continue;
|
|
790
901
|
}
|
|
791
902
|
const classAttr = findAttr(element, 'className');
|
|
792
|
-
|
|
903
|
+
const item = itemOf(elementPath);
|
|
904
|
+
if (!classAttr && !item)
|
|
793
905
|
continue;
|
|
794
906
|
const attributes = attributesBesides(element, classAttr, attributeValue);
|
|
795
|
-
const className = staticClassName(classAttr);
|
|
907
|
+
const className = classAttr ? staticClassName(classAttr) : '';
|
|
796
908
|
const surrounding = around(elementPath);
|
|
797
909
|
const sole = soleChild(element);
|
|
798
910
|
const facts = {
|
|
@@ -813,6 +925,8 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
813
925
|
componentsAround: surrounding.other,
|
|
814
926
|
classesAround: surrounding.classes,
|
|
815
927
|
onlyChildOf: onlyChildOf(elementPath, bestaxLocals),
|
|
928
|
+
...(item && { itemOf: item }),
|
|
929
|
+
...(classAttr && className?.trim() === '' && { emptyClass: true }),
|
|
816
930
|
};
|
|
817
931
|
// A joiner call the codemod can read converts exactly: its fixed classes
|
|
818
932
|
// as for a static className, and each condition on a flag as its prop.
|
|
@@ -869,6 +983,44 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
869
983
|
elements.unshift({ path: elementPath, plan: result, converts });
|
|
870
984
|
}
|
|
871
985
|
}
|
|
986
|
+
// An item's nested list becomes a list component that renders its root
|
|
987
|
+
// class unless another is around it (`Menu.List`), so an item with one
|
|
988
|
+
// converts only inside a list that is one, or becomes one here. Outermost
|
|
989
|
+
// first: an item that stays markup leaves its nested list a plain one.
|
|
990
|
+
const becomesAfter = new Set();
|
|
991
|
+
for (const found of elements) {
|
|
992
|
+
const conversion = found.plan.conversion;
|
|
993
|
+
const after = conversion?.absorbs?.after;
|
|
994
|
+
if (!after)
|
|
995
|
+
continue;
|
|
996
|
+
let inside = false;
|
|
997
|
+
for (let up = found.path.parent; up && !inside; up = up.parent) {
|
|
998
|
+
if (up.node?.type !== 'JSXElement')
|
|
999
|
+
continue;
|
|
1000
|
+
inside =
|
|
1001
|
+
becomesAfter.has(up.node) ||
|
|
1002
|
+
(bestaxTarget(up.node, up) ??
|
|
1003
|
+
planned.get(up.node)?.conversion?.target) === after;
|
|
1004
|
+
}
|
|
1005
|
+
if (inside) {
|
|
1006
|
+
const [, next] = (found.path.node.children ?? []).filter((node) => node.type === 'JSXElement');
|
|
1007
|
+
becomesAfter.add(next);
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
const root = Object.keys(ROOTS).find(key => ROOTS[key].target === after);
|
|
1011
|
+
found.plan = {
|
|
1012
|
+
conversion: null,
|
|
1013
|
+
todos: [
|
|
1014
|
+
...found.plan.todos,
|
|
1015
|
+
{
|
|
1016
|
+
rule: ruleId('context', conversion.target),
|
|
1017
|
+
message: `bestax \`${conversion.target}\` renders the list nested in it as a \`${after}\`, which renders \`.${root}\` unless another \`${after}\` is around it, and no list around this element is one or becomes one here; keep it as markup, or convert the list around it first, then re-run`,
|
|
1018
|
+
},
|
|
1019
|
+
],
|
|
1020
|
+
};
|
|
1021
|
+
found.converts = false;
|
|
1022
|
+
planned.set(found.path.node, found.plan);
|
|
1023
|
+
}
|
|
872
1024
|
if (elements.length === 0)
|
|
873
1025
|
return print();
|
|
874
1026
|
// ---- 3. File-level gates ------------------------------------------------------
|
|
@@ -952,8 +1104,11 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
952
1104
|
continue;
|
|
953
1105
|
const element = elementPath.node;
|
|
954
1106
|
// A target that renders the element's only child itself is written in
|
|
955
|
-
// that child's place, so the child's children stay where they are.
|
|
956
|
-
|
|
1107
|
+
// that child's place, so the child's children stay where they are. One
|
|
1108
|
+
// that renders an element after it takes that one in after them.
|
|
1109
|
+
const [child, next] = conversion.absorbs
|
|
1110
|
+
? (element.children ?? []).filter((node) => node.type === 'JSXElement')
|
|
1111
|
+
: [];
|
|
957
1112
|
// The element's tags go, and so do the child's tag names: their comments
|
|
958
1113
|
// move to the name that stays. The child's tags themselves stay, and
|
|
959
1114
|
// keep their own.
|
|
@@ -1032,6 +1187,16 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
1032
1187
|
}
|
|
1033
1188
|
elementPath.replace(child);
|
|
1034
1189
|
written = child;
|
|
1190
|
+
const after = conversion.absorbs.after;
|
|
1191
|
+
if (after) {
|
|
1192
|
+
const [listHead, ...listRest] = after.split('.');
|
|
1193
|
+
renameElement(j, next, [ctx.reserve(listHead), ...listRest].join('.'));
|
|
1194
|
+
if (!child.closingElement) {
|
|
1195
|
+
child.openingElement.selfClosing = false;
|
|
1196
|
+
child.closingElement = j.jsxClosingElement(element.closingElement.name);
|
|
1197
|
+
}
|
|
1198
|
+
child.children = withAfter(j, child.children ?? [], next);
|
|
1199
|
+
}
|
|
1035
1200
|
}
|
|
1036
1201
|
// After the splice, so a prop the conversion wrote is found as well as
|
|
1037
1202
|
// an attribute the element already had.
|