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
|
@@ -16,7 +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 { collectBoundNames, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
19
|
+
import { collectBoundNames, makeAliasRegistry, makeReserve, nameOf, prefersTabs, resolvesToBinding, } from '../_shared/imports.js';
|
|
20
20
|
import { flattenResponsiveProps } from './responsive.js';
|
|
21
21
|
import { RESPONSIVE_KINDS, runSpecial } from './specials.js';
|
|
22
22
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -88,6 +88,21 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
88
88
|
addTodo(ctx, path, 'imports', 'deep react-bulma-components import paths are a v3 pattern; move to v4 named root imports first');
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
|
+
// A barrel re-export reaches the library without an import declaration,
|
|
92
|
+
// so nothing above saw it; it cannot be rewritten (a compound bestax
|
|
93
|
+
// target has no single name to re-export) and must not vanish silently.
|
|
94
|
+
root
|
|
95
|
+
.find(j.ExportNamedDeclaration)
|
|
96
|
+
.filter(p => String(p.node.source?.value ?? '') === RBC)
|
|
97
|
+
.forEach(p => {
|
|
98
|
+
addTodo(ctx, p, 'imports', `re-exports from '${RBC}' were not migrated; import the components, migrate them, then re-export the bestax ones by hand`);
|
|
99
|
+
});
|
|
100
|
+
root
|
|
101
|
+
.find(j.ExportAllDeclaration)
|
|
102
|
+
.filter(p => String(p.node.source?.value ?? '') === RBC)
|
|
103
|
+
.forEach(p => {
|
|
104
|
+
addTodo(ctx, p, 'imports', `\`export * from '${RBC}'\` was not migrated; re-export the bestax components you need by hand`);
|
|
105
|
+
});
|
|
91
106
|
// ---- 1a. Stylesheet imports (mode-driven) -----------------------------
|
|
92
107
|
// `bestax` (default): everything converges on the recommended combined
|
|
93
108
|
// bundle. `bulma`: plain Bulma v1 CSS plus the separate extras file.
|
|
@@ -150,68 +165,20 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
150
165
|
// The scope each binding was collected in, so a reference can be checked
|
|
151
166
|
// against the binding it actually resolves to rather than matched by name.
|
|
152
167
|
const programScope = root.find(j.Program).paths()[0]?.scope;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
for (const [owner, perScope] of aliasesByOwner) {
|
|
168
|
-
if (perScope.has(name))
|
|
169
|
-
owners.push(owner);
|
|
170
|
-
}
|
|
171
|
-
return owners;
|
|
172
|
-
};
|
|
173
|
-
const aliasAt = (name, at) => {
|
|
174
|
-
const owners = ownersFor(name);
|
|
175
|
-
if (owners.length === 0)
|
|
176
|
-
return undefined;
|
|
177
|
-
// Without a reference location there is nothing to walk, so the only
|
|
178
|
-
// owner is the best answer available. WITH one, always walk: an unrelated
|
|
179
|
-
// `Header` parameter in another function must not resolve to the alias
|
|
180
|
-
// merely because the alias is the only one by that name. A single-owner
|
|
181
|
-
// shortcut here rewrote such a parameter to `{ Header: Card.Header }`,
|
|
182
|
-
// which is not even valid syntax.
|
|
183
|
-
if (at === undefined) {
|
|
184
|
-
return owners.length === 1
|
|
185
|
-
? aliasesByOwner.get(owners[0]).get(name)
|
|
186
|
-
: undefined;
|
|
187
|
-
}
|
|
188
|
-
let cursor = at;
|
|
189
|
-
while (cursor) {
|
|
190
|
-
if (cursor.node !== undefined) {
|
|
191
|
-
const hit = aliasesByOwner.get(cursor.node)?.get(name);
|
|
192
|
-
if (hit) {
|
|
193
|
-
// The walk finds the nearest OWNER, but a binding can intervene
|
|
194
|
-
// between the reference and that owner: `function F(Header)` under
|
|
195
|
-
// a module-level `const { Header } = Card` shadows the alias, and
|
|
196
|
-
// resolving through it rewrote the parameter itself to
|
|
197
|
-
// `F(Card.Header)`. If the scope table still binds the name
|
|
198
|
-
// somewhere other than the owner, that nearer binding wins. (After
|
|
199
|
-
// the alias declaration is pruned the lookup returns nothing for
|
|
200
|
-
// the alias itself, which is the case this must keep allowing.)
|
|
201
|
-
const declaring = at.scope?.lookup(name)?.path?.node;
|
|
202
|
-
if (declaring !== undefined &&
|
|
203
|
-
declaring !== null &&
|
|
204
|
-
declaring !== cursor.node) {
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
return hit;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
cursor = cursor.parent;
|
|
211
|
-
}
|
|
212
|
-
return undefined;
|
|
213
|
-
};
|
|
214
|
-
const anyAlias = (name) => ownersFor(name).length > 0;
|
|
168
|
+
const { register: registerAlias, aliasAt, anyAlias } = makeAliasRegistry();
|
|
169
|
+
/**
|
|
170
|
+
* Locals a re-export names. Pruning the declaration that binds one of them
|
|
171
|
+
* (`const { Header } = Card; export { Header };`) leaves the specifier
|
|
172
|
+
* pointing at nothing, so those declarations are kept: the value-reference
|
|
173
|
+
* pass rewrites their initialiser to the bestax component, and the export
|
|
174
|
+
* goes on re-exporting the same member.
|
|
175
|
+
*/
|
|
176
|
+
const reExported = new Set();
|
|
177
|
+
root.find(j.ExportSpecifier).forEach(p => {
|
|
178
|
+
const local = p.node.local ?? p.node.exported;
|
|
179
|
+
if (local?.type === 'Identifier')
|
|
180
|
+
reExported.add(local.name);
|
|
181
|
+
});
|
|
215
182
|
// ---- 1b. Resolve `const { Input, Field: F } = Form` destructuring -----
|
|
216
183
|
root.find(j.VariableDeclarator).forEach(path => {
|
|
217
184
|
const node = path.node;
|
|
@@ -234,13 +201,8 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
234
201
|
prop.value?.type === 'Identifier') {
|
|
235
202
|
const owner = path.scope
|
|
236
203
|
?.path?.node;
|
|
237
|
-
let perScope = aliasesByOwner.get(owner);
|
|
238
|
-
if (!perScope) {
|
|
239
|
-
perScope = new Map();
|
|
240
|
-
aliasesByOwner.set(owner, perScope);
|
|
241
|
-
}
|
|
242
204
|
const target = [...basePath, prop.key.name];
|
|
243
|
-
|
|
205
|
+
registerAlias(owner, prop.value.name, target);
|
|
244
206
|
aliases.set(prop.value.name, target);
|
|
245
207
|
// Pruning the declaration is only safe if every use of the alias will
|
|
246
208
|
// actually be rewritten. For a name the table cannot map, the JSX is
|
|
@@ -261,7 +223,10 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
261
223
|
allResolved = false;
|
|
262
224
|
}
|
|
263
225
|
}
|
|
264
|
-
|
|
226
|
+
const exportsAnAlias = node.id.properties.some((prop) => (prop.type === 'ObjectProperty' || prop.type === 'Property') &&
|
|
227
|
+
prop.value?.type === 'Identifier' &&
|
|
228
|
+
reExported.has(prop.value.name));
|
|
229
|
+
if (allResolved && !exportsAnAlias) {
|
|
265
230
|
const declaration = path.parent;
|
|
266
231
|
if (declaration?.node?.type === 'VariableDeclaration' &&
|
|
267
232
|
declaration.node.declarations.length === 1) {
|
|
@@ -377,6 +342,16 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
377
342
|
bound.add(head);
|
|
378
343
|
});
|
|
379
344
|
ctx.reserve = makeReserve(ctx, bound);
|
|
345
|
+
// Names the passes actually asked for. `ctx.needed` is also seeded from an
|
|
346
|
+
// existing bestax import's specifiers below (so JSX reuses their locals),
|
|
347
|
+
// and that seeding alone must not turn a type-only specifier into a value
|
|
348
|
+
// import nobody needed.
|
|
349
|
+
const requested = new Set();
|
|
350
|
+
const baseReserve = ctx.reserve;
|
|
351
|
+
ctx.reserve = root => {
|
|
352
|
+
requested.add(root);
|
|
353
|
+
return baseReserve(root);
|
|
354
|
+
};
|
|
380
355
|
// Merge with an existing bestax import: reuse its locals verbatim.
|
|
381
356
|
// Only a declaration made entirely of NAMED specifiers can be merged into.
|
|
382
357
|
// `import * as Bulma from '@allxsmith/bestax-bulma'` matches the source too,
|
|
@@ -386,13 +361,24 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
386
361
|
const existingBestax = root
|
|
387
362
|
.find(j.ImportDeclaration, { source: { value: BESTAX } })
|
|
388
363
|
.paths()
|
|
389
|
-
.find(path =>
|
|
364
|
+
.find(path =>
|
|
365
|
+
// A type-only declaration cannot take a value specifier: merging a
|
|
366
|
+
// component into `import type { … }` erases it at runtime.
|
|
367
|
+
path.node.importKind !== 'type' &&
|
|
368
|
+
(path.node.specifiers ?? []).every((spec) => spec.type === 'ImportSpecifier'));
|
|
390
369
|
const preExistingImports = new Set();
|
|
391
370
|
if (existingBestax) {
|
|
392
371
|
for (const spec of existingBestax.node.specifiers ?? []) {
|
|
393
372
|
if (spec.type === 'ImportSpecifier' && spec.local) {
|
|
373
|
+
// The local name is reused either way, so the JSX never needs an
|
|
374
|
+
// alias. But an inline `type` specifier (`import { type Box }`) is
|
|
375
|
+
// not a value binding: it is not counted as already imported, so the
|
|
376
|
+
// component is written as a value below — onto this specifier, which
|
|
377
|
+
// keeps its name, rather than as a duplicate.
|
|
394
378
|
ctx.needed.set(nameOf(spec.imported), nameOf(spec.local));
|
|
395
|
-
|
|
379
|
+
if (spec.importKind !== 'type') {
|
|
380
|
+
preExistingImports.add(nameOf(spec.imported));
|
|
381
|
+
}
|
|
396
382
|
}
|
|
397
383
|
}
|
|
398
384
|
}
|
|
@@ -508,6 +494,10 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
508
494
|
parentType === 'JSXMemberExpression') {
|
|
509
495
|
return;
|
|
510
496
|
}
|
|
497
|
+
// A TS qualified name (`RBC.ButtonProps` in a type position) is erased at
|
|
498
|
+
// runtime, so it is not a value use of the namespace at all.
|
|
499
|
+
if (parentType === 'TSQualifiedName')
|
|
500
|
+
return;
|
|
511
501
|
// A namespace binding in a real value position. `RBC.Box` is still a
|
|
512
502
|
// mappable component reference, so fall through to the member-expression
|
|
513
503
|
// branch below with an empty prefix (the namespace itself is not part of
|
|
@@ -520,6 +510,46 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
520
510
|
parentNode.object === path.node &&
|
|
521
511
|
!parentNode.computed)) {
|
|
522
512
|
namespaceStillReferenced = true;
|
|
513
|
+
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`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
// Members, keys and signatures name a thing; they do not reference the
|
|
517
|
+
// import. An export specifier does, and is handled on its own: a flat
|
|
518
|
+
// target keeps the public name (\`export { SubTitle as Subtitle }\`), a
|
|
519
|
+
// dotted one cannot be re-exported under a member name and is flagged.
|
|
520
|
+
if (parentType === 'ExportSpecifier') {
|
|
521
|
+
if (parentNode.local !== path.node)
|
|
522
|
+
return;
|
|
523
|
+
const exportedName = nameOf(parentNode.exported ?? parentNode.local);
|
|
524
|
+
const mapping = aliasPath ? resolveMapping(aliasPath) : MAPPING[imported];
|
|
525
|
+
if (mapping &&
|
|
526
|
+
mapping.status !== 'todo' &&
|
|
527
|
+
mapping.target &&
|
|
528
|
+
!mapping.target.includes('.')) {
|
|
529
|
+
const local = ctx.reserve(mapping.target);
|
|
530
|
+
// Replace the node: recast reprints a specifier whose identifiers were
|
|
531
|
+
// swapped in place in its original shorthand form, dropping the `as`.
|
|
532
|
+
path.parent.replace(j.exportSpecifier.from({
|
|
533
|
+
local: j.identifier(local),
|
|
534
|
+
exported: j.identifier(exportedName),
|
|
535
|
+
}));
|
|
536
|
+
ctx.dirty = true;
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
ctx.retained.add(imported);
|
|
540
|
+
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`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (parentType === 'TSEnumMember' ||
|
|
544
|
+
((parentType === 'ObjectMethod' ||
|
|
545
|
+
parentType === 'ClassMethod' ||
|
|
546
|
+
parentType === 'ClassProperty' ||
|
|
547
|
+
parentType === 'PropertyDefinition' ||
|
|
548
|
+
parentType === 'MethodDefinition' ||
|
|
549
|
+
parentType === 'TSPropertySignature' ||
|
|
550
|
+
parentType === 'TSMethodSignature') &&
|
|
551
|
+
parentNode.key === path.node &&
|
|
552
|
+
!parentNode.computed)) {
|
|
523
553
|
return;
|
|
524
554
|
}
|
|
525
555
|
// Non-reference positions: member property names and object keys.
|
|
@@ -712,15 +742,61 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
712
742
|
if (imports.size > 0) {
|
|
713
743
|
const retainedNames = [...ctx.retained].sort((a, b) => a.localeCompare(b));
|
|
714
744
|
const freshNames = [...ctx.needed.entries()]
|
|
715
|
-
.filter(([imported]) => !preExistingImports.has(imported))
|
|
745
|
+
.filter(([imported]) => requested.has(imported) && !preExistingImports.has(imported))
|
|
716
746
|
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
717
747
|
const bestaxImport = freshNames.length > 0
|
|
718
748
|
? j.importDeclaration(freshNames.map(([imported, local]) => j.importSpecifier(j.identifier(imported), j.identifier(local))), j.stringLiteral(BESTAX))
|
|
719
749
|
: null;
|
|
750
|
+
/**
|
|
751
|
+
|
|
752
|
+
* A pruned declaration takes its comments with it — a licence header, an
|
|
753
|
+
|
|
754
|
+
* eslint directive. Hand them to what replaces it, or to the next statement.
|
|
755
|
+
|
|
756
|
+
*/
|
|
757
|
+
const carryComments = (node, importPath) => {
|
|
758
|
+
const comments = node.comments ?? [];
|
|
759
|
+
if (comments.length === 0)
|
|
760
|
+
return;
|
|
761
|
+
const body = importPath.parent?.node?.body ?? [];
|
|
762
|
+
const index = body.indexOf(node);
|
|
763
|
+
// The fresh specifiers are merged INTO an existing bestax import when there
|
|
764
|
+
// is one, and the `bestaxImport` node is then discarded — so the comments
|
|
765
|
+
// have to follow the declaration that survives, not the one that does not.
|
|
766
|
+
const carrier = (existingBestax ? existingBestax.node : bestaxImport) ??
|
|
767
|
+
(index >= 0 ? body[index + 1] : undefined);
|
|
768
|
+
if (carrier) {
|
|
769
|
+
carrier.comments = [...comments, ...(carrier.comments ?? [])];
|
|
770
|
+
}
|
|
771
|
+
else if (index > 0) {
|
|
772
|
+
// The import was the last statement: the comments stay where they
|
|
773
|
+
// were, after what precedes it.
|
|
774
|
+
const previous = body[index - 1];
|
|
775
|
+
previous.comments = [
|
|
776
|
+
...(previous.comments ?? []),
|
|
777
|
+
...comments.map((c) => ({
|
|
778
|
+
...c,
|
|
779
|
+
leading: false,
|
|
780
|
+
trailing: true,
|
|
781
|
+
})),
|
|
782
|
+
];
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
// The import was the only statement: the comments become the file's.
|
|
786
|
+
const program = importPath.parent?.node;
|
|
787
|
+
if (program)
|
|
788
|
+
program.comments = [...comments, ...(program.comments ?? [])];
|
|
789
|
+
}
|
|
790
|
+
node.comments = [];
|
|
791
|
+
};
|
|
720
792
|
let inserted = false;
|
|
721
793
|
// A retained RBC specifier must never collide with a bestax import local
|
|
722
794
|
// (possible when one component is both JSX-migrated and value-retained).
|
|
723
|
-
|
|
795
|
+
// Only a VALUE local can collide at runtime: a type-only specifier the
|
|
796
|
+
// seeding above recorded, and nothing promoted, binds no value.
|
|
797
|
+
const bestaxLocals = new Set([...ctx.needed.entries()]
|
|
798
|
+
.filter(([imported]) => requested.has(imported) || preExistingImports.has(imported))
|
|
799
|
+
.map(([, local]) => local));
|
|
724
800
|
for (const path of rbcImportPaths) {
|
|
725
801
|
const node = path.node;
|
|
726
802
|
const keepSpecifiers = (node.specifiers ?? []).filter((spec) => {
|
|
@@ -736,10 +812,23 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
736
812
|
});
|
|
737
813
|
if (!inserted) {
|
|
738
814
|
if (existingBestax && bestaxImport) {
|
|
739
|
-
existingBestax.node.specifiers
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
815
|
+
const current = existingBestax.node.specifiers ?? [];
|
|
816
|
+
const appended = [];
|
|
817
|
+
for (const fresh of bestaxImport.specifiers) {
|
|
818
|
+
// A type-only specifier for the same name becomes the value
|
|
819
|
+
// import (a value import carries the type too); appending a
|
|
820
|
+
// second `Box` beside `type Box` would be a duplicate identifier.
|
|
821
|
+
const typeOnly = current.find((spec) => spec.type === 'ImportSpecifier' &&
|
|
822
|
+
spec.importKind === 'type' &&
|
|
823
|
+
nameOf(spec.imported) === nameOf(fresh.imported));
|
|
824
|
+
if (typeOnly) {
|
|
825
|
+
typeOnly.importKind = null;
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
appended.push(fresh);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
existingBestax.node.specifiers = [...current, ...appended];
|
|
743
832
|
}
|
|
744
833
|
else if (bestaxImport) {
|
|
745
834
|
path.insertBefore(bestaxImport);
|
|
@@ -755,6 +844,7 @@ export default function transform(fileInfo, api, options = {}) {
|
|
|
755
844
|
}
|
|
756
845
|
}
|
|
757
846
|
else {
|
|
847
|
+
carryComments(node, path);
|
|
758
848
|
path.prune();
|
|
759
849
|
}
|
|
760
850
|
ctx.dirty = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sources/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sources/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAInD,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEnE;AAED,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAEtC"}
|
package/dist/sources/registry.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { reactBulmaComponents } from './react-bulma-components/index.js';
|
|
6
6
|
import { rbx } from './rbx/index.js';
|
|
7
|
+
import { bloomer } from './bloomer/index.js';
|
|
7
8
|
export const SOURCES = {
|
|
8
9
|
[reactBulmaComponents.name]: reactBulmaComponents,
|
|
9
10
|
[rbx.name]: rbx,
|
|
11
|
+
[bloomer.name]: bloomer,
|
|
10
12
|
};
|
|
11
13
|
export function getSource(name) {
|
|
12
14
|
return SOURCES[name];
|
package/dist/types.d.ts
CHANGED
|
@@ -38,6 +38,17 @@ export interface PropAction {
|
|
|
38
38
|
name: string;
|
|
39
39
|
value?: string;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Replace a boolean (valueless / `true`) attribute with this Bulma class,
|
|
43
|
+
* merged into `className` — for a modifier bestax has no prop for but
|
|
44
|
+
* Bulma v1 still ships. A `false` value is dropped; a dynamic one is a TODO.
|
|
45
|
+
*/
|
|
46
|
+
toClass?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Replace a literal string attribute with `prefix + value` merged into
|
|
49
|
+
* `className` (`isSize="large"` → `is-large`). A dynamic value is a TODO.
|
|
50
|
+
*/
|
|
51
|
+
toClassPrefix?: string;
|
|
41
52
|
/** Remove the attribute entirely. */
|
|
42
53
|
drop?: boolean;
|
|
43
54
|
/** Leave the attribute as-is and attach a TODO with this hint. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,qCAAqC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACzC;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAElD,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uEAAuE;IACvE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,mDAAmD;AACnD,MAAM,MAAM,SAAS,GAAG,CACtB,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,gBAAgB,KACtB,MAAM,GAAG,SAAS,CAAC;AAExB,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,aAAa,GAAG,SAAS,EACpC,OAAO,EAAE,gBAAgB,KACtB,MAAM,GAAG,IAAI,CAAC;AAEnB,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG,CAC/B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,SAAS,EAAE,aAAa,GAAG,SAAS,EACpC,OAAO,EAAE,WAAW,KACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;AAEpC,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bestax-migrate",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Codemods for migrating existing React apps from other Bulma libraries to @allxsmith/bestax-bulma",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"react-dom": "^19.2.8",
|
|
52
52
|
"ts-jest": "^29.4.12",
|
|
53
53
|
"typescript": "^6.0.3",
|
|
54
|
-
"@allxsmith/bestax-bulma": "^5.
|
|
54
|
+
"@allxsmith/bestax-bulma": "^5.15.0"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
|
@@ -66,10 +66,11 @@
|
|
|
66
66
|
"typecheck": "tsc --noEmit",
|
|
67
67
|
"validate:corpus": "pnpm run build && node scripts/validate-corpus.mjs",
|
|
68
68
|
"validate:corpus:rbx": "pnpm run build && node scripts/validate-corpus-rbx.mjs",
|
|
69
|
+
"validate:corpus:bloomer": "pnpm run build && node scripts/validate-corpus-bloomer.mjs",
|
|
69
70
|
"format": "prettier --write \"src/**/*.{ts,tsx}\" \"e2e/**/*.ts\"",
|
|
70
71
|
"format:check": "prettier --check \"src/**/*.{ts,tsx}\" \"e2e/**/*.ts\"",
|
|
71
72
|
"clean": "rimraf dist",
|
|
72
73
|
"release": "npx semantic-release"
|
|
73
74
|
},
|
|
74
|
-
"readme": "# bestax-migrate\n\n[](https://www.npmjs.com/package/bestax-migrate)\n[](https://www.npmjs.com/package/bestax-migrate)\n[](https://opensource.org/licenses/MIT)\n[](https://socket.dev/npm/package/bestax-migrate/overview)\n[](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[](https://www.bestpractices.dev/projects/14361)\n[](https://www.npmjs.com/package/bestax-migrate#provenance)\n[](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nCodemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — the actively maintained React component library for **Bulma v1**.\n\nCurrently supported source libraries:\n\n| Source | Status
|
|
75
|
+
"readme": "# bestax-migrate\n\n[](https://www.npmjs.com/package/bestax-migrate)\n[](https://www.npmjs.com/package/bestax-migrate)\n[](https://opensource.org/licenses/MIT)\n[](https://socket.dev/npm/package/bestax-migrate/overview)\n[](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[](https://www.bestpractices.dev/projects/14361)\n[](https://www.npmjs.com/package/bestax-migrate#provenance)\n[](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nCodemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — the actively maintained React component library for **Bulma v1**.\n\nCurrently supported source libraries:\n\n| Source | Status |\n| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |\n| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |\n| [`bloomer`](https://github.com/AlgusDark/bloomer) (0.6) | ✅ All 108 exports mapped (Font Awesome 4 icons and Bulma 0.4's `Nav` are flagged) |\n\nPass the source library as the first argument: `bestax-migrate <source> <paths…>`.\n\n## Requirements\n\n**Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the CLI\nexits immediately with an explicit upgrade message.\n\nThis applies only to the Node version the codemod itself runs on. It places no requirement on\nthe app being migrated — the source is read as text and is never executed.\n\n## Usage\n\n```bash\n# Preview what would change (no writes)\npnpm dlx bestax-migrate react-bulma-components src/ --dry\n# …or, for an rbx or bloomer app\npnpm dlx bestax-migrate rbx src/ --dry\npnpm dlx bestax-migrate bloomer src/ --dry\n\n# Apply the migration\npnpm dlx bestax-migrate react-bulma-components src/\n```\n\nnpm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`.\n\nThe codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to:\n\n- rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports)\n- rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …) — and, for bloomer, its flat names onto bestax's compounds (`CardHeaderTitle` → `Card.Header.Title`)\n- convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign=\"center\"` → `textAlign=\"centered\"`, …)\n- flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`) and bloomer's `isDisplay`/`isHidden` strings, arrays and objects (`isHidden=\"touch\"` → `visibilityTouch=\"hidden\"`)\n- restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → `<Icon name=…>`)\n- migrate stylesheets: CSS imports converge on `@allxsmith/bestax-bulma/bestax.css`, and SCSS files move from Bulma 0.9's `@import` + `$var !default` overrides to `@use 'bulma/sass' with (…)` plus `@use '@allxsmith/bestax-bulma/scss/extras'`\n- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them. A **bloomer** app declared its own Bulma 0.6, which is what gets bumped\n\nAnything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx), [bloomer](https://bestax.io/docs/guides/getting-started/migration/bloomer)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:\n\n```bash\nnpx skills add https://github.com/allxsmith/bestax --skill bestax-migrate\n```\n\n## Options\n\n| Flag | Description |\n| -------------------- | -------------------------------------------------------------- |\n| `--dry`, `-d` | Report what would change without writing files |\n| `--print`, `-p` | Print transformed sources to stdout |\n| `--extensions`, `-e` | File extensions to include (default `js,jsx,ts,tsx,scss,sass`) |\n| `--css <mode>` | Stylesheet target: `bestax` (default), `bulma`, or `keep` |\n| `--no-deps` | Skip updating package.json dependencies |\n\n## After the codemod\n\n1. Run your package manager's install (the codemod rewrote `package.json` but never installs)\n2. Search for `TODO(bestax-migrate)` and resolve each comment\n3. Typecheck/build and review the rendered app\n\nFull walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx) · [bloomer migration guide](https://bestax.io/docs/guides/getting-started/migration/bloomer).\n\n## Hardened by default\n\nA codemod rewrites your source in place, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/bestax-migrate#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **The libraries this tool migrates away from are never installed here** — source fixtures are read as text only, so no unmaintained third-party package enters the dependency tree.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, and CodeQL, Dependency Review, and Dependabot run continuously alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`bestax-migrate` can send one **anonymous** usage event after a successful\nrun — only if you opt in when asked (once, on an interactive terminal). The\nevent is just the run's shape — source library, CSS mode, the dry/deps flags, a\ncapped changed-file count, and per-rule TODO counts — plus the CLI version,\nNode major version, and OS platform name. Never file paths, file contents,\ncode, names, IPs, or machine IDs, and no identifier exists that could link two\nevents together. Opt out any time with `--no-telemetry`,\n`BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## License\n\nSource code licensed MIT\n"
|
|
75
76
|
}
|