bestax-migrate 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/sources/_shared/imports.d.ts +28 -0
- package/dist/sources/_shared/imports.d.ts.map +1 -1
- package/dist/sources/_shared/imports.js +62 -0
- package/dist/sources/_shared/jsx-utils.d.ts +17 -0
- package/dist/sources/_shared/jsx-utils.d.ts.map +1 -1
- package/dist/sources/_shared/jsx-utils.js +49 -1
- package/dist/sources/_shared/props.d.ts +17 -0
- package/dist/sources/_shared/props.d.ts.map +1 -1
- package/dist/sources/_shared/props.js +72 -3
- package/dist/sources/_shared/specials-utils.d.ts +49 -3
- package/dist/sources/_shared/specials-utils.d.ts.map +1 -1
- package/dist/sources/_shared/specials-utils.js +165 -5
- package/dist/sources/_shared/viewports.d.ts +17 -0
- package/dist/sources/_shared/viewports.d.ts.map +1 -0
- package/dist/sources/_shared/viewports.js +32 -0
- package/dist/sources/bloomer/deps.d.ts +17 -0
- package/dist/sources/bloomer/deps.d.ts.map +1 -0
- package/dist/sources/bloomer/deps.js +119 -0
- package/dist/sources/bloomer/index.d.ts +3 -0
- package/dist/sources/bloomer/index.d.ts.map +1 -0
- package/dist/sources/bloomer/index.js +10 -0
- package/dist/sources/bloomer/mapping.d.ts +84 -0
- package/dist/sources/bloomer/mapping.d.ts.map +1 -0
- package/dist/sources/bloomer/mapping.js +988 -0
- package/dist/sources/bloomer/responsive.d.ts +39 -0
- package/dist/sources/bloomer/responsive.d.ts.map +1 -0
- package/dist/sources/bloomer/responsive.js +259 -0
- package/dist/sources/bloomer/specials.d.ts +19 -0
- package/dist/sources/bloomer/specials.d.ts.map +1 -0
- package/dist/sources/bloomer/specials.js +774 -0
- package/dist/sources/bloomer/styles.d.ts +13 -0
- package/dist/sources/bloomer/styles.d.ts.map +1 -0
- package/dist/sources/bloomer/styles.js +17 -0
- package/dist/sources/bloomer/transform.d.ts +27 -0
- package/dist/sources/bloomer/transform.d.ts.map +1 -0
- package/dist/sources/bloomer/transform.js +826 -0
- package/dist/sources/rbx/mapping.d.ts.map +1 -1
- package/dist/sources/rbx/mapping.js +20 -18
- package/dist/sources/rbx/responsive.d.ts.map +1 -1
- package/dist/sources/rbx/responsive.js +1 -31
- package/dist/sources/rbx/specials.d.ts.map +1 -1
- package/dist/sources/rbx/specials.js +36 -98
- package/dist/sources/rbx/transform.d.ts.map +1 -1
- package/dist/sources/rbx/transform.js +168 -78
- package/dist/sources/react-bulma-components/mapping.js +4 -4
- package/dist/sources/react-bulma-components/responsive.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/responsive.js +2 -14
- package/dist/sources/react-bulma-components/specials.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/specials.js +45 -18
- package/dist/sources/react-bulma-components/transform.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/transform.js +168 -78
- package/dist/sources/registry.d.ts.map +1 -1
- package/dist/sources/registry.js +2 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -3
|
@@ -22,7 +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 { collectBoundNames, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
25
|
+
import { collectBoundNames, makeAliasRegistry, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
26
26
|
import { flattenResponsiveProps, RESPONSIVE_KINDS } from './responsive.js';
|
|
27
27
|
import { runSpecial } from './specials.js';
|
|
28
28
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -108,6 +108,21 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
108
108
|
addTodo(ctx, path, 'imports', `\`${source}\` reaches into rbx's internals; bestax's equivalents are its \`Theme\` and \`ConfigProvider\` helpers (https://bestax.io/docs/api/helpers/theme)`);
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
|
+
// A barrel re-export reaches the library without an import declaration,
|
|
112
|
+
// so nothing above saw it; it cannot be rewritten (a compound bestax
|
|
113
|
+
// target has no single name to re-export) and must not vanish silently.
|
|
114
|
+
root
|
|
115
|
+
.find(j.ExportNamedDeclaration)
|
|
116
|
+
.filter(p => String(p.node.source?.value ?? '') === RBX)
|
|
117
|
+
.forEach(p => {
|
|
118
|
+
addTodo(ctx, p, 'imports', `re-exports from '${RBX}' were not migrated; import the components, migrate them, then re-export the bestax ones by hand`);
|
|
119
|
+
});
|
|
120
|
+
root
|
|
121
|
+
.find(j.ExportAllDeclaration)
|
|
122
|
+
.filter(p => String(p.node.source?.value ?? '') === RBX)
|
|
123
|
+
.forEach(p => {
|
|
124
|
+
addTodo(ctx, p, 'imports', `\`export * from '${RBX}'\` was not migrated; re-export the bestax components you need by hand`);
|
|
125
|
+
});
|
|
111
126
|
// ---- 1a. Stylesheet imports (mode-driven) -----------------------------
|
|
112
127
|
// `bestax` (default): everything converges on the recommended combined
|
|
113
128
|
// bundle. `bulma`: plain Bulma v1 CSS plus the separate extras file.
|
|
@@ -199,68 +214,20 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
199
214
|
// The scope each binding was collected in, so a reference can be checked
|
|
200
215
|
// against the binding it actually resolves to rather than matched by name.
|
|
201
216
|
const programScope = root.find(j.Program).paths()[0]?.scope;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
for (const [owner, perScope] of aliasesByOwner) {
|
|
217
|
-
if (perScope.has(name))
|
|
218
|
-
owners.push(owner);
|
|
219
|
-
}
|
|
220
|
-
return owners;
|
|
221
|
-
};
|
|
222
|
-
const aliasAt = (name, at) => {
|
|
223
|
-
const owners = ownersFor(name);
|
|
224
|
-
if (owners.length === 0)
|
|
225
|
-
return undefined;
|
|
226
|
-
// Without a reference location there is nothing to walk, so the only
|
|
227
|
-
// owner is the best answer available. WITH one, always walk: an unrelated
|
|
228
|
-
// `Header` parameter in another function must not resolve to the alias
|
|
229
|
-
// merely because the alias is the only one by that name. A single-owner
|
|
230
|
-
// shortcut here rewrote such a parameter to `{ Header: Card.Header }`,
|
|
231
|
-
// which is not even valid syntax.
|
|
232
|
-
if (at === undefined) {
|
|
233
|
-
return owners.length === 1
|
|
234
|
-
? aliasesByOwner.get(owners[0]).get(name)
|
|
235
|
-
: undefined;
|
|
236
|
-
}
|
|
237
|
-
let cursor = at;
|
|
238
|
-
while (cursor) {
|
|
239
|
-
if (cursor.node !== undefined) {
|
|
240
|
-
const hit = aliasesByOwner.get(cursor.node)?.get(name);
|
|
241
|
-
if (hit) {
|
|
242
|
-
// The walk finds the nearest OWNER, but a binding can intervene
|
|
243
|
-
// between the reference and that owner: `function F(Header)` under
|
|
244
|
-
// a module-level `const { Header } = Card` shadows the alias, and
|
|
245
|
-
// resolving through it rewrote the parameter itself to
|
|
246
|
-
// `F(Card.Header)`. If the scope table still binds the name
|
|
247
|
-
// somewhere other than the owner, that nearer binding wins. (After
|
|
248
|
-
// the alias declaration is pruned the lookup returns nothing for
|
|
249
|
-
// the alias itself, which is the case this must keep allowing.)
|
|
250
|
-
const declaring = at.scope?.lookup(name)?.path?.node;
|
|
251
|
-
if (declaring !== undefined &&
|
|
252
|
-
declaring !== null &&
|
|
253
|
-
declaring !== cursor.node) {
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
|
-
return hit;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
cursor = cursor.parent;
|
|
260
|
-
}
|
|
261
|
-
return undefined;
|
|
262
|
-
};
|
|
263
|
-
const anyAlias = (name) => ownersFor(name).length > 0;
|
|
217
|
+
const { register: registerAlias, aliasAt, anyAlias } = makeAliasRegistry();
|
|
218
|
+
/**
|
|
219
|
+
* Locals a re-export names. Pruning the declaration that binds one of them
|
|
220
|
+
* (`const { Header } = Card; export { Header };`) leaves the specifier
|
|
221
|
+
* pointing at nothing, so those declarations are kept: the value-reference
|
|
222
|
+
* pass rewrites their initialiser to the bestax component, and the export
|
|
223
|
+
* goes on re-exporting the same member.
|
|
224
|
+
*/
|
|
225
|
+
const reExported = new Set();
|
|
226
|
+
root.find(j.ExportSpecifier).forEach(p => {
|
|
227
|
+
const local = p.node.local ?? p.node.exported;
|
|
228
|
+
if (local?.type === 'Identifier')
|
|
229
|
+
reExported.add(local.name);
|
|
230
|
+
});
|
|
264
231
|
// ---- 1b. Resolve `const { Item } = Card` destructuring -----------------
|
|
265
232
|
root.find(j.VariableDeclarator).forEach(path => {
|
|
266
233
|
const node = path.node;
|
|
@@ -283,13 +250,8 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
283
250
|
prop.value?.type === 'Identifier') {
|
|
284
251
|
const owner = path.scope
|
|
285
252
|
?.path?.node;
|
|
286
|
-
let perScope = aliasesByOwner.get(owner);
|
|
287
|
-
if (!perScope) {
|
|
288
|
-
perScope = new Map();
|
|
289
|
-
aliasesByOwner.set(owner, perScope);
|
|
290
|
-
}
|
|
291
253
|
const target = [...basePath, prop.key.name];
|
|
292
|
-
|
|
254
|
+
registerAlias(owner, prop.value.name, target);
|
|
293
255
|
aliases.set(prop.value.name, target);
|
|
294
256
|
// Pruning the declaration is only safe if every use of the alias will
|
|
295
257
|
// actually be rewritten. For a name the table cannot map, the JSX is
|
|
@@ -310,7 +272,10 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
310
272
|
allResolved = false;
|
|
311
273
|
}
|
|
312
274
|
}
|
|
313
|
-
|
|
275
|
+
const exportsAnAlias = node.id.properties.some((prop) => (prop.type === 'ObjectProperty' || prop.type === 'Property') &&
|
|
276
|
+
prop.value?.type === 'Identifier' &&
|
|
277
|
+
reExported.has(prop.value.name));
|
|
278
|
+
if (allResolved && !exportsAnAlias) {
|
|
314
279
|
const declaration = path.parent;
|
|
315
280
|
if (declaration?.node?.type === 'VariableDeclaration' &&
|
|
316
281
|
declaration.node.declarations.length === 1) {
|
|
@@ -426,6 +391,16 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
426
391
|
bound.add(head);
|
|
427
392
|
});
|
|
428
393
|
ctx.reserve = makeReserve(ctx, bound);
|
|
394
|
+
// Names the passes actually asked for. `ctx.needed` is also seeded from an
|
|
395
|
+
// existing bestax import's specifiers below (so JSX reuses their locals),
|
|
396
|
+
// and that seeding alone must not turn a type-only specifier into a value
|
|
397
|
+
// import nobody needed.
|
|
398
|
+
const requested = new Set();
|
|
399
|
+
const baseReserve = ctx.reserve;
|
|
400
|
+
ctx.reserve = root => {
|
|
401
|
+
requested.add(root);
|
|
402
|
+
return baseReserve(root);
|
|
403
|
+
};
|
|
429
404
|
// Merge with an existing bestax import: reuse its locals verbatim.
|
|
430
405
|
// Only a declaration that already uses NAMED specifiers can absorb more of
|
|
431
406
|
// them. `import * as Bestax from '…'` cannot: a namespace specifier may not
|
|
@@ -434,13 +409,24 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
434
409
|
const existingBestax = root
|
|
435
410
|
.find(j.ImportDeclaration, { source: { value: BESTAX } })
|
|
436
411
|
.paths()
|
|
437
|
-
.find(p =>
|
|
412
|
+
.find(p =>
|
|
413
|
+
// A type-only declaration cannot take a value specifier: merging a
|
|
414
|
+
// component into `import type { … }` erases it at runtime.
|
|
415
|
+
p.node.importKind !== 'type' &&
|
|
416
|
+
(p.node.specifiers ?? []).every((spec) => spec.type === 'ImportSpecifier'));
|
|
438
417
|
const preExistingImports = new Set();
|
|
439
418
|
if (existingBestax) {
|
|
440
419
|
for (const spec of existingBestax.node.specifiers ?? []) {
|
|
441
420
|
if (spec.type === 'ImportSpecifier' && spec.local) {
|
|
421
|
+
// The local name is reused either way, so the JSX never needs an
|
|
422
|
+
// alias. But an inline `type` specifier (`import { type Box }`) is
|
|
423
|
+
// not a value binding: it is not counted as already imported, so the
|
|
424
|
+
// component is written as a value below — onto this specifier, which
|
|
425
|
+
// keeps its name, rather than as a duplicate.
|
|
442
426
|
ctx.needed.set(nameOf(spec.imported), nameOf(spec.local));
|
|
443
|
-
|
|
427
|
+
if (spec.importKind !== 'type') {
|
|
428
|
+
preExistingImports.add(nameOf(spec.imported));
|
|
429
|
+
}
|
|
444
430
|
}
|
|
445
431
|
}
|
|
446
432
|
}
|
|
@@ -633,6 +619,10 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
633
619
|
parentType === 'JSXMemberExpression') {
|
|
634
620
|
return;
|
|
635
621
|
}
|
|
622
|
+
// A TS qualified name (`RBC.ButtonProps` in a type position) is erased at
|
|
623
|
+
// runtime, so it is not a value use of the namespace at all.
|
|
624
|
+
if (parentType === 'TSQualifiedName')
|
|
625
|
+
return;
|
|
636
626
|
// A namespace binding in a real value position. `rbx.Button` is still a
|
|
637
627
|
// mappable component reference, so fall through to the member-expression
|
|
638
628
|
// branch below with an empty prefix (the namespace itself is not part of
|
|
@@ -643,6 +633,46 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
643
633
|
parentNode.object === path.node &&
|
|
644
634
|
!parentNode.computed)) {
|
|
645
635
|
namespaceStillReferenced = true;
|
|
636
|
+
addTodo(ctx, path, 'value-reference', `\`${name}\` is the library's namespace import used as a plain value (destructured, or passed around); nothing reached through it was migrated — reference its components as \`${name}.Component\` or import them by name, then re-run`);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
// Members, keys and signatures name a thing; they do not reference the
|
|
640
|
+
// import. An export specifier does, and is handled on its own: a flat
|
|
641
|
+
// target keeps the public name (\`export { SubTitle as Subtitle }\`), a
|
|
642
|
+
// dotted one cannot be re-exported under a member name and is flagged.
|
|
643
|
+
if (parentType === 'ExportSpecifier') {
|
|
644
|
+
if (parentNode.local !== path.node)
|
|
645
|
+
return;
|
|
646
|
+
const exportedName = nameOf(parentNode.exported ?? parentNode.local);
|
|
647
|
+
const mapping = aliasPath ? resolveMapping(aliasPath) : MAPPING[imported];
|
|
648
|
+
if (mapping &&
|
|
649
|
+
mapping.status !== 'todo' &&
|
|
650
|
+
mapping.target &&
|
|
651
|
+
!mapping.target.includes('.')) {
|
|
652
|
+
const local = ctx.reserve(mapping.target);
|
|
653
|
+
// Replace the node: recast reprints a specifier whose identifiers were
|
|
654
|
+
// swapped in place in its original shorthand form, dropping the `as`.
|
|
655
|
+
path.parent.replace(j.exportSpecifier.from({
|
|
656
|
+
local: j.identifier(local),
|
|
657
|
+
exported: j.identifier(exportedName),
|
|
658
|
+
}));
|
|
659
|
+
ctx.dirty = true;
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
ctx.retained.add(imported);
|
|
663
|
+
addTodo(ctx, path, 'value-reference', `\`${name}\` is re-exported; ${mapping?.target ? `its bestax counterpart \`${mapping.target}\` is a member of \`${mapping.target.split('.')[0]}\` and cannot be re-exported under this name` : 'it has no bestax counterpart to re-export'} — migrate the consumers by hand`);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (parentType === 'TSEnumMember' ||
|
|
667
|
+
((parentType === 'ObjectMethod' ||
|
|
668
|
+
parentType === 'ClassMethod' ||
|
|
669
|
+
parentType === 'ClassProperty' ||
|
|
670
|
+
parentType === 'PropertyDefinition' ||
|
|
671
|
+
parentType === 'MethodDefinition' ||
|
|
672
|
+
parentType === 'TSPropertySignature' ||
|
|
673
|
+
parentType === 'TSMethodSignature') &&
|
|
674
|
+
parentNode.key === path.node &&
|
|
675
|
+
!parentNode.computed)) {
|
|
646
676
|
return;
|
|
647
677
|
}
|
|
648
678
|
// Non-reference positions: member property names and object keys.
|
|
@@ -835,15 +865,61 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
835
865
|
if (imports.size > 0) {
|
|
836
866
|
const retainedNames = [...ctx.retained].sort((a, b) => a.localeCompare(b));
|
|
837
867
|
const freshNames = [...ctx.needed.entries()]
|
|
838
|
-
.filter(([imported]) => !preExistingImports.has(imported))
|
|
868
|
+
.filter(([imported]) => requested.has(imported) && !preExistingImports.has(imported))
|
|
839
869
|
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
840
870
|
const bestaxImport = freshNames.length > 0
|
|
841
871
|
? j.importDeclaration(freshNames.map(([imported, local]) => j.importSpecifier(j.identifier(imported), j.identifier(local))), j.stringLiteral(BESTAX))
|
|
842
872
|
: null;
|
|
873
|
+
/**
|
|
874
|
+
|
|
875
|
+
* A pruned declaration takes its comments with it — a licence header, an
|
|
876
|
+
|
|
877
|
+
* eslint directive. Hand them to what replaces it, or to the next statement.
|
|
878
|
+
|
|
879
|
+
*/
|
|
880
|
+
const carryComments = (node, importPath) => {
|
|
881
|
+
const comments = node.comments ?? [];
|
|
882
|
+
if (comments.length === 0)
|
|
883
|
+
return;
|
|
884
|
+
const body = importPath.parent?.node?.body ?? [];
|
|
885
|
+
const index = body.indexOf(node);
|
|
886
|
+
// The fresh specifiers are merged INTO an existing bestax import when there
|
|
887
|
+
// is one, and the `bestaxImport` node is then discarded — so the comments
|
|
888
|
+
// have to follow the declaration that survives, not the one that does not.
|
|
889
|
+
const carrier = (existingBestax ? existingBestax.node : bestaxImport) ??
|
|
890
|
+
(index >= 0 ? body[index + 1] : undefined);
|
|
891
|
+
if (carrier) {
|
|
892
|
+
carrier.comments = [...comments, ...(carrier.comments ?? [])];
|
|
893
|
+
}
|
|
894
|
+
else if (index > 0) {
|
|
895
|
+
// The import was the last statement: the comments stay where they
|
|
896
|
+
// were, after what precedes it.
|
|
897
|
+
const previous = body[index - 1];
|
|
898
|
+
previous.comments = [
|
|
899
|
+
...(previous.comments ?? []),
|
|
900
|
+
...comments.map((c) => ({
|
|
901
|
+
...c,
|
|
902
|
+
leading: false,
|
|
903
|
+
trailing: true,
|
|
904
|
+
})),
|
|
905
|
+
];
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
// The import was the only statement: the comments become the file's.
|
|
909
|
+
const program = importPath.parent?.node;
|
|
910
|
+
if (program)
|
|
911
|
+
program.comments = [...comments, ...(program.comments ?? [])];
|
|
912
|
+
}
|
|
913
|
+
node.comments = [];
|
|
914
|
+
};
|
|
843
915
|
let inserted = false;
|
|
844
916
|
// A retained rbx specifier must never collide with a bestax import local
|
|
845
917
|
// (possible when one component is both JSX-migrated and value-retained).
|
|
846
|
-
|
|
918
|
+
// Only a VALUE local can collide at runtime: a type-only specifier the
|
|
919
|
+
// seeding above recorded, and nothing promoted, binds no value.
|
|
920
|
+
const bestaxLocals = new Set([...ctx.needed.entries()]
|
|
921
|
+
.filter(([imported]) => requested.has(imported) || preExistingImports.has(imported))
|
|
922
|
+
.map(([, local]) => local));
|
|
847
923
|
for (const path of rbxImportPaths) {
|
|
848
924
|
const node = path.node;
|
|
849
925
|
const keepSpecifiers = (node.specifiers ?? []).filter((spec) => {
|
|
@@ -862,10 +938,23 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
862
938
|
});
|
|
863
939
|
if (!inserted) {
|
|
864
940
|
if (existingBestax && bestaxImport) {
|
|
865
|
-
existingBestax.node.specifiers
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
941
|
+
const current = existingBestax.node.specifiers ?? [];
|
|
942
|
+
const appended = [];
|
|
943
|
+
for (const fresh of bestaxImport.specifiers) {
|
|
944
|
+
// A type-only specifier for the same name becomes the value
|
|
945
|
+
// import (a value import carries the type too); appending a
|
|
946
|
+
// second `Box` beside `type Box` would be a duplicate identifier.
|
|
947
|
+
const typeOnly = current.find((spec) => spec.type === 'ImportSpecifier' &&
|
|
948
|
+
spec.importKind === 'type' &&
|
|
949
|
+
nameOf(spec.imported) === nameOf(fresh.imported));
|
|
950
|
+
if (typeOnly) {
|
|
951
|
+
typeOnly.importKind = null;
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
appended.push(fresh);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
existingBestax.node.specifiers = [...current, ...appended];
|
|
869
958
|
}
|
|
870
959
|
else if (bestaxImport) {
|
|
871
960
|
path.insertBefore(bestaxImport);
|
|
@@ -892,6 +981,7 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
892
981
|
}
|
|
893
982
|
}
|
|
894
983
|
else {
|
|
984
|
+
carryComments(node, path);
|
|
895
985
|
path.prune();
|
|
896
986
|
}
|
|
897
987
|
ctx.dirty = true;
|
|
@@ -26,7 +26,7 @@ export const UNIVERSAL_PROPS = {
|
|
|
26
26
|
todo: 'this bestax component has no `as` prop; restructure the element instead',
|
|
27
27
|
},
|
|
28
28
|
domRef: {
|
|
29
|
-
todo: 'bestax-bulma
|
|
29
|
+
todo: 'bestax-bulma has no `domRef`. The form controls and `Button`, `LinkButton`, `Modal`, `Dropdown`, `Navbar` (plus `Navbar.Burger` and `Navbar.Link`), `Dialog`, `Sidebar`, `Toast` and `Carousel` forward a ref — on those, rename `domRef` to `ref`. Mind the `Navbar.Dropdown` collision: the `Navbar.Item` that wrapped your dropdown became bestax `Navbar.Dropdown` and forwards one, while your `Navbar.Dropdown` became `Navbar.DropdownMenu` and does not. The rest forward no ref: put the ref on a DOM child or wrap the component',
|
|
30
30
|
},
|
|
31
31
|
// ColorProps
|
|
32
32
|
backgroundColor: { rename: 'bgColor' },
|
|
@@ -555,12 +555,12 @@ export const MAPPING = {
|
|
|
555
555
|
props: {
|
|
556
556
|
show: { rename: 'active' },
|
|
557
557
|
onClose: {},
|
|
558
|
-
closeOnEsc: {
|
|
558
|
+
closeOnEsc: { rename: 'closeOnEscape' },
|
|
559
559
|
closeOnBlur: {
|
|
560
|
-
todo: 'bestax Modal background click
|
|
560
|
+
todo: 'bestax Modal closes on background click only in its legacy form; the Modal.Content/Modal.Card child selects the compound form, which renders only the children you wrote — add a `<Modal.Background>` whose `onClick` calls your close handler, or drop the prop if you passed false',
|
|
561
561
|
},
|
|
562
562
|
showClose: {
|
|
563
|
-
todo: 'bestax Modal renders
|
|
563
|
+
todo: 'bestax Modal renders a close button only in its legacy form; the Modal.Content/Modal.Card child selects the compound form, which renders only the children you wrote — add a `<Modal.Close variant="floating">` whose `onClick` calls your close handler, or drop the prop if you passed false',
|
|
564
564
|
},
|
|
565
565
|
document: { drop: true },
|
|
566
566
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responsive.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/responsive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,
|
|
1
|
+
{"version":3,"file":"responsive.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/responsive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAOL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAS9D,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,cAAc,GACnB,IAAI,CAkHN"}
|
|
@@ -11,25 +11,13 @@
|
|
|
11
11
|
* TODO. Non-object or dynamic values get a TODO.
|
|
12
12
|
*/
|
|
13
13
|
import { RESPONSIVE_BREAKPOINTS } from './mapping.js';
|
|
14
|
-
import { addAttr, addTodo, attributesOf, makeAttr, removeAttr, } from '../_shared/jsx-utils.js';
|
|
14
|
+
import { addAttr, addTodo, attributesOf, literalOf, makeAttr, removeAttr, } from '../_shared/jsx-utils.js';
|
|
15
15
|
const TEXT_ALIGN_MAP = {
|
|
16
16
|
center: 'centered',
|
|
17
17
|
justify: 'justified',
|
|
18
18
|
left: 'left',
|
|
19
19
|
right: 'right',
|
|
20
20
|
};
|
|
21
|
-
function literalOf(prop) {
|
|
22
|
-
const v = prop.value;
|
|
23
|
-
if (!v)
|
|
24
|
-
return undefined;
|
|
25
|
-
if (v.type === 'StringLiteral')
|
|
26
|
-
return v.value;
|
|
27
|
-
if (v.type === 'NumericLiteral')
|
|
28
|
-
return v.value;
|
|
29
|
-
if (v.type === 'BooleanLiteral')
|
|
30
|
-
return v.value;
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
21
|
export function flattenResponsiveProps(ctx, path, element, kind) {
|
|
34
22
|
const { j } = ctx;
|
|
35
23
|
for (const attr of [...attributesOf(element)]) {
|
|
@@ -57,7 +45,7 @@ export function flattenResponsiveProps(ctx, path, element, kind) {
|
|
|
57
45
|
continue;
|
|
58
46
|
}
|
|
59
47
|
const key = prop.key?.name ?? prop.key?.value;
|
|
60
|
-
const literal = literalOf(prop);
|
|
48
|
+
const literal = literalOf(prop.value);
|
|
61
49
|
if (key === 'display' && typeof literal === 'string') {
|
|
62
50
|
if (literal === 'hidden') {
|
|
63
51
|
addAttr(element, makeAttr(j, `visibility${suffix}`, 'hidden'));
|
|
@@ -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;AACjC,OAAO,
|
|
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;AACjC,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,aAAa,EAAE,CAAC;AAgxB9B,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,8 +9,19 @@
|
|
|
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 { alignTarget, makeStripModifierProps, mergeClassName, parseIconClasses, } from '../_shared/specials-utils.js';
|
|
12
|
+
import { alignTarget, makeStripModifierProps, mergeClassName, applyIconProps, parseIconClasses, } from '../_shared/specials-utils.js';
|
|
13
13
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
|
+
/**
|
|
15
|
+
* The attributes a plain-element rewrite keeps: the modifier strip decides
|
|
16
|
+
* which NAMED props survive, and the element's spreads ride along in their
|
|
17
|
+
* original places — they are the caller's own props, which a plain element
|
|
18
|
+
* takes as readily as the component did. `attributesOf` filters spreads out,
|
|
19
|
+
* so passing its result alone dropped `{...rest}` with no TODO.
|
|
20
|
+
*/
|
|
21
|
+
function keptAttrs(ctx, path, element, where) {
|
|
22
|
+
const kept = new Set(stripModifierProps(ctx, path, attributesOf(element), where));
|
|
23
|
+
return (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute' || kept.has(a));
|
|
24
|
+
}
|
|
14
25
|
const stripModifierProps = makeStripModifierProps(UNIVERSAL_PROPS, RESPONSIVE_BREAKPOINTS);
|
|
15
26
|
const SPECIALS = {
|
|
16
27
|
/**
|
|
@@ -61,7 +72,18 @@ const SPECIALS = {
|
|
|
61
72
|
// (`remove={false}`, `remove=""`, `remove={0}`) → just drop the prop.
|
|
62
73
|
removeAttr(element, attr);
|
|
63
74
|
ctx.dirty = true;
|
|
64
|
-
|
|
75
|
+
if (resolved !== 'truthy')
|
|
76
|
+
return {};
|
|
77
|
+
// The universal `domRef` advice lists `Button` among the components that
|
|
78
|
+
// forward a ref, but this element is no longer a Button: `Delete` is a
|
|
79
|
+
// plain function component. Claim the prop so that advice cannot fire
|
|
80
|
+
// here, and say what actually applies to the target we picked.
|
|
81
|
+
const handledProps = [];
|
|
82
|
+
if (findAttr(element, 'domRef')) {
|
|
83
|
+
addTodo(ctx, path, 'prop:domRef', 'this `<Button remove>` became bestax `<Delete>`, which forwards no ref — the general `domRef` advice does not apply here; put the ref on a DOM child or wrap the component');
|
|
84
|
+
handledProps.push('domRef');
|
|
85
|
+
}
|
|
86
|
+
return { target: 'Delete', handledProps };
|
|
65
87
|
}
|
|
66
88
|
addTodo(ctx, path, 'prop:remove', '`remove` has a dynamic value; this can render as either a Button or a Delete cross — split the branch by hand');
|
|
67
89
|
return {};
|
|
@@ -113,7 +135,8 @@ const SPECIALS = {
|
|
|
113
135
|
if (headingTruthy && !subtitleDynamic) {
|
|
114
136
|
removeAttr(element, headingAttr);
|
|
115
137
|
const className = mergeClassName(ctx, path, element, subtitleTruthy ? 'heading subtitle' : 'heading', 'Heading');
|
|
116
|
-
const
|
|
138
|
+
const consumed = new Set(stripModifierProps(ctx, path, attributesOf(element).filter(a => !['size', 'weight', 'spaced', 'subtitle'].includes(a.name.name)), 'Heading'));
|
|
139
|
+
const rest = (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute' || consumed.has(a));
|
|
117
140
|
const replacement = plainElement(ctx.j, 'p', className, rest, element.children ?? []);
|
|
118
141
|
path.replace(replacement);
|
|
119
142
|
ctx.dirty = true;
|
|
@@ -296,7 +319,7 @@ const SPECIALS = {
|
|
|
296
319
|
}
|
|
297
320
|
}
|
|
298
321
|
className = mergeClassName(ctx, path, element, className, 'Form.Label');
|
|
299
|
-
const rest =
|
|
322
|
+
const rest = keptAttrs(ctx, path, element, 'Form.Label');
|
|
300
323
|
path.replace(plainElement(ctx.j, 'label', className, rest, element.children ?? []));
|
|
301
324
|
ctx.dirty = true;
|
|
302
325
|
return { replaced: true };
|
|
@@ -317,7 +340,7 @@ const SPECIALS = {
|
|
|
317
340
|
}
|
|
318
341
|
}
|
|
319
342
|
className = mergeClassName(ctx, path, element, className, 'Form.Help');
|
|
320
|
-
const rest =
|
|
343
|
+
const rest = keptAttrs(ctx, path, element, 'Form.Help');
|
|
321
344
|
path.replace(plainElement(ctx.j, 'p', className, rest, element.children ?? []));
|
|
322
345
|
ctx.dirty = true;
|
|
323
346
|
return { replaced: true };
|
|
@@ -347,7 +370,9 @@ const SPECIALS = {
|
|
|
347
370
|
const value = cls && cls.value && cls.value.type === 'StringLiteral'
|
|
348
371
|
? cls.value.value
|
|
349
372
|
: null;
|
|
350
|
-
|
|
373
|
+
const read = value ? parseIconClasses(value) : null;
|
|
374
|
+
// An app's own class on the <i> has no home on bestax's glyph.
|
|
375
|
+
return read && read.leftovers.length === 0 ? read : null;
|
|
351
376
|
})()
|
|
352
377
|
: null;
|
|
353
378
|
if (!parsed) {
|
|
@@ -355,11 +380,8 @@ const SPECIALS = {
|
|
|
355
380
|
return {};
|
|
356
381
|
}
|
|
357
382
|
if (!findAttr(element, 'name') && !findAttr(element, 'icon')) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
addAttr(element, makeAttr(ctx.j, 'library', parsed.library));
|
|
361
|
-
if (parsed.variant)
|
|
362
|
-
addAttr(element, makeAttr(ctx.j, 'variant', parsed.variant));
|
|
383
|
+
applyIconProps(ctx, path, element, parsed);
|
|
384
|
+
return {};
|
|
363
385
|
}
|
|
364
386
|
element.children = [];
|
|
365
387
|
if (element.closingElement) {
|
|
@@ -372,7 +394,7 @@ const SPECIALS = {
|
|
|
372
394
|
/** RBC Loader is a plain <div class="loader"> — keep exactly that. */
|
|
373
395
|
'plain-loader'(ctx, path, element) {
|
|
374
396
|
const className = mergeClassName(ctx, path, element, 'loader', 'Loader');
|
|
375
|
-
const rest =
|
|
397
|
+
const rest = keptAttrs(ctx, path, element, 'Loader');
|
|
376
398
|
path.replace(plainElement(ctx.j, 'div', className, rest, element.children ?? []));
|
|
377
399
|
ctx.dirty = true;
|
|
378
400
|
return { replaced: true };
|
|
@@ -419,7 +441,7 @@ const SPECIALS = {
|
|
|
419
441
|
removeAttr(element, activeAttr);
|
|
420
442
|
}
|
|
421
443
|
className = mergeClassName(ctx, path, element, className, 'Panel.Tabs.Tab');
|
|
422
|
-
const rest =
|
|
444
|
+
const rest = keptAttrs(ctx, path, element, 'Panel.Tabs.Tab');
|
|
423
445
|
path.replace(plainElement(ctx.j, 'a', className, rest, element.children ?? []));
|
|
424
446
|
ctx.dirty = true;
|
|
425
447
|
return { replaced: true };
|
|
@@ -439,7 +461,7 @@ const SPECIALS = {
|
|
|
439
461
|
removeAttr(element, activeAttr);
|
|
440
462
|
}
|
|
441
463
|
liClass = mergeClassName(ctx, path, element, liClass, 'Breadcrumb.Item');
|
|
442
|
-
const anchorAttrs =
|
|
464
|
+
const anchorAttrs = keptAttrs(ctx, path, element, 'Breadcrumb.Item');
|
|
443
465
|
const children = element.children ?? [];
|
|
444
466
|
const solidChildren = children.filter((c) => !(c.type === 'JSXText' && c.value.trim() === ''));
|
|
445
467
|
// `<Breadcrumb.Item><a href>…</a></Breadcrumb.Item>` already carries its
|
|
@@ -475,7 +497,7 @@ const SPECIALS = {
|
|
|
475
497
|
return { replaced: true };
|
|
476
498
|
}
|
|
477
499
|
const className = mergeClassName(ctx, path, element, 'table-container', 'Table.Container');
|
|
478
|
-
const rest =
|
|
500
|
+
const rest = keptAttrs(ctx, path, element, 'Table.Container');
|
|
479
501
|
path.replace(plainElement(ctx.j, 'div', className, rest, element.children ?? []));
|
|
480
502
|
ctx.dirty = true;
|
|
481
503
|
return { replaced: true };
|
|
@@ -496,9 +518,14 @@ const SPECIALS = {
|
|
|
496
518
|
solid[0].openingElement.name.name === 'a';
|
|
497
519
|
if (alreadyAnchor)
|
|
498
520
|
return {};
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
521
|
+
const anchorKept = new Set(stripModifierProps(ctx, path, attributesOf(element).filter(a => !['active', 'className'].includes(a.name.name)), 'Tabs.Tab'));
|
|
522
|
+
// The anchor takes everything that is not the tab's own state, spreads
|
|
523
|
+
// included: they were the caller's props on the element that becomes the
|
|
524
|
+
// <a>, and `attributesOf` filters them out.
|
|
525
|
+
const anchorAttrs = (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute' || anchorKept.has(a));
|
|
526
|
+
for (const attr of [...(element.openingElement.attributes ?? [])]) {
|
|
527
|
+
if (attr.type === 'JSXSpreadAttribute' ||
|
|
528
|
+
!['active', 'className'].includes(attr.name.name)) {
|
|
502
529
|
removeAttr(element, attr);
|
|
503
530
|
}
|
|
504
531
|
}
|
|
@@ -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;AA4CvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CAq8BpB"}
|