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
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* Source-agnostic. `makeStripModifierProps` is a factory precisely because
|
|
8
8
|
* the modifier vocabulary is the one thing here that differs per source.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { addAttrOnce } from './props.js';
|
|
11
|
+
import { addAttr, addTodo, attributesOf, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, } from './jsx-utils.js';
|
|
11
12
|
/** Pick a target based on a literal align-style prop, removing the prop. */
|
|
12
13
|
export function alignTarget(ctx, path, element, prop, targets, fallback) {
|
|
13
14
|
const attr = findAttr(element, prop);
|
|
@@ -77,7 +78,10 @@ export function mergeClassName(ctx, path, element, base, where) {
|
|
|
77
78
|
: `dynamic ${where} className; re-apply it to the emitted element by hand`);
|
|
78
79
|
return base;
|
|
79
80
|
}
|
|
80
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Parse an icon-font class string (`fas fa-home fa-spin`, `mdi mdi-account`)
|
|
83
|
+
* into bestax Icon props. Returns null when no icon name can be read.
|
|
84
|
+
*/
|
|
81
85
|
export function parseIconClasses(className) {
|
|
82
86
|
const tokens = className.trim().split(/\s+/);
|
|
83
87
|
const faVariant = {
|
|
@@ -109,16 +113,172 @@ export function parseIconClasses(className) {
|
|
|
109
113
|
const FA_MODIFIER = /^fa-(?:solid|regular|brands|light|duotone|thin|sharp|classic|2xs|xs|sm|lg|xl|2xl|\d{1,2}x|fw|ul|li|border|inverse|stack|stack-1x|stack-2x|pull-(?:left|right)|spin|spin-pulse|spin-reverse|pulse|beat|fade|beat-fade|bounce|flash|shake|swap-opacity|rotate-(?:90|180|270|by)|flip-(?:horizontal|vertical|both))$/;
|
|
110
114
|
const faName = tokens.find(t => /^fa-/.test(t) && !FA_MODIFIER.test(t));
|
|
111
115
|
if (faStyle && faName) {
|
|
116
|
+
// The style word itself (`fa-solid`) is not a feature; `fa` is FA4's
|
|
117
|
+
// style token and, when it accompanies a v5+ style, harmless.
|
|
118
|
+
const features = tokens.filter(t => FA_MODIFIER.test(t) && !faVariant[t]);
|
|
119
|
+
const leftovers = tokens.filter(t => t !== faStyle && t !== faName && t !== 'fa' && !features.includes(t));
|
|
112
120
|
return {
|
|
113
121
|
name: faName.replace(/^fa-/, ''),
|
|
114
122
|
library: 'fa',
|
|
115
123
|
variant: faVariant[faStyle],
|
|
124
|
+
features,
|
|
125
|
+
leftovers,
|
|
116
126
|
};
|
|
117
127
|
}
|
|
118
128
|
if (tokens.includes('mdi')) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
// MDI's documented modifiers: sizes, spin, rotation, flips, light/dark,
|
|
130
|
+
// inactive. Any other `mdi-*` token is the icon — whichever comes first,
|
|
131
|
+
// since `mdi mdi-24px mdi-account` is as legal as the other order.
|
|
132
|
+
const MDI_MODIFIER = /^mdi-(?:(?:18|24|36|48)px|spin|rotate-(?:45|90|135|180|225|270|315)|flip-[hv]|light|dark|inactive)$/;
|
|
133
|
+
const mdiName = tokens.find(t => /^mdi-/.test(t) && t !== 'mdi' && !MDI_MODIFIER.test(t));
|
|
134
|
+
if (mdiName) {
|
|
135
|
+
const features = tokens.filter(t => MDI_MODIFIER.test(t));
|
|
136
|
+
const leftovers = tokens.filter(t => t !== 'mdi' && t !== mdiName && !features.includes(t));
|
|
137
|
+
return {
|
|
138
|
+
name: mdiName.replace(/^mdi-/, ''),
|
|
139
|
+
library: 'mdi',
|
|
140
|
+
features,
|
|
141
|
+
leftovers,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
122
144
|
}
|
|
123
145
|
return null;
|
|
124
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Write a parsed icon onto a bestax `Icon`-shaped element: `name`, then
|
|
149
|
+
* `library`/`variant` when known, then `features` — one string, or an array
|
|
150
|
+
* literal when there are several — and make the element self-closing.
|
|
151
|
+
*/
|
|
152
|
+
export function applyIconProps(ctx, path, element, parsed) {
|
|
153
|
+
const { j } = ctx;
|
|
154
|
+
// The source forwarded HTML props, so the element may already carry a
|
|
155
|
+
// `name` (or any of these): the existing one is kept and the clash named,
|
|
156
|
+
// rather than writing a duplicate attribute.
|
|
157
|
+
const once = (attr) => addAttrOnce(ctx, path, element, 'className', attr);
|
|
158
|
+
once(makeAttr(j, 'name', parsed.name));
|
|
159
|
+
if (parsed.library)
|
|
160
|
+
once(makeAttr(j, 'library', parsed.library));
|
|
161
|
+
if (parsed.variant)
|
|
162
|
+
once(makeAttr(j, 'variant', parsed.variant));
|
|
163
|
+
if (parsed.features.length === 1) {
|
|
164
|
+
once(makeAttr(j, 'features', parsed.features[0]));
|
|
165
|
+
}
|
|
166
|
+
else if (parsed.features.length > 1) {
|
|
167
|
+
once(j.jsxAttribute(j.jsxIdentifier('features'), j.jsxExpressionContainer(j.arrayExpression(parsed.features.map(f => j.stringLiteral(f))))));
|
|
168
|
+
}
|
|
169
|
+
element.children = [];
|
|
170
|
+
element.openingElement.selfClosing = true;
|
|
171
|
+
element.closingElement = null;
|
|
172
|
+
ctx.dirty = true;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Turn a literal modifier prop into an `is-*` class fragment for an element
|
|
176
|
+
* on its way to becoming plain HTML. Returns the class (or undefined) and
|
|
177
|
+
* always removes the attribute.
|
|
178
|
+
*/
|
|
179
|
+
export function modifierClass(ctx, path, element, prop, base, where) {
|
|
180
|
+
const attr = findAttr(element, prop);
|
|
181
|
+
if (!attr)
|
|
182
|
+
return base;
|
|
183
|
+
const literal = literalValueOf(attr);
|
|
184
|
+
removeAttr(element, attr);
|
|
185
|
+
if (literal.kind === 'string' || literal.kind === 'number') {
|
|
186
|
+
return `${base} is-${literal.value}`;
|
|
187
|
+
}
|
|
188
|
+
addTodo(ctx, path, `prop:${prop}`, `dynamic ${where} \`${prop}\`; add the matching is-* class by hand`);
|
|
189
|
+
return base;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A source that puts `as` (or its own name for it) on every component can
|
|
193
|
+
* land, through a handler that picks its target from a prop value, on a
|
|
194
|
+
* bestax component that has no `as` — Media.Item resolves to
|
|
195
|
+
* Left/Content/Right where only Left declares one. Opting into `as` in the
|
|
196
|
+
* mapping table cannot express that, so the target has to be checked after
|
|
197
|
+
* it is picked.
|
|
198
|
+
*/
|
|
199
|
+
export function restrictAsToTargets(ctx, path, element, target, allowed, prop = 'as') {
|
|
200
|
+
const asAttr = findAttr(element, prop);
|
|
201
|
+
if (!asAttr || (target && allowed.includes(target)))
|
|
202
|
+
return;
|
|
203
|
+
removeAttr(element, asAttr);
|
|
204
|
+
addTodo(ctx, path, `prop:${prop}`, `bestax's \`${target}\` has no \`as\` prop (of the targets this can resolve to, only ${allowed
|
|
205
|
+
.map(a => `\`${a}\``)
|
|
206
|
+
.join(' / ')} does); render the tag directly or restructure`);
|
|
207
|
+
ctx.dirty = true;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The two structural rewrites every source needs, bound to that source's own
|
|
211
|
+
* attribute strip (its modifier vocabulary is the one thing that differs):
|
|
212
|
+
* replacing an element with plain HTML, and folding a wrapper onto the single
|
|
213
|
+
* child it exists to wrap.
|
|
214
|
+
*/
|
|
215
|
+
export function makeStructuralHelpers(strip) {
|
|
216
|
+
/** Replace the element with a plain HTML tag carrying `className`. */
|
|
217
|
+
function replaceWithPlain(ctx, path, element, tag, className, where) {
|
|
218
|
+
const merged = mergeClassName(ctx, path, element, className, where);
|
|
219
|
+
const kept = new Set(strip(ctx, path, attributesOf(element), where));
|
|
220
|
+
// Spread attributes pass through untouched, in their original places:
|
|
221
|
+
// they are the caller's own props, and a plain element takes them as
|
|
222
|
+
// readily as the component did, with the same JSX precedence. The Bulma
|
|
223
|
+
// class the element exists for is written right AFTER the last spread,
|
|
224
|
+
// so a `className` inside a spread cannot override it — the source
|
|
225
|
+
// merged such a className with its own; a plain element cannot, so the
|
|
226
|
+
// spread is named.
|
|
227
|
+
const all = element.openingElement.attributes ?? [];
|
|
228
|
+
const spreads = all.filter((a) => a.type === 'JSXSpreadAttribute');
|
|
229
|
+
const ordered = all.filter((a) => a.type === 'JSXSpreadAttribute' || kept.has(a));
|
|
230
|
+
if (spreads.length > 0 && merged) {
|
|
231
|
+
addTodo(ctx, path, 'plain-element', `${where} became a plain <${tag}> with \`className="${merged}"\`; a \`className\` inside its spread prop(s) would have been merged with that class by the source, and is overridden here — merge it by hand`);
|
|
232
|
+
}
|
|
233
|
+
path.replace(plainElement(ctx.j, tag, merged, ordered, element.children ?? []));
|
|
234
|
+
ctx.dirty = true;
|
|
235
|
+
return { replaced: true };
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A wrapper that owns the modifiers, where bestax's component renders that
|
|
239
|
+
* wrapper itself: move the wrapper's (already-converted) attributes onto
|
|
240
|
+
* its single child and replace the wrapper with it — otherwise both would
|
|
241
|
+
* rename to the same bestax component and nest, which is invalid.
|
|
242
|
+
*
|
|
243
|
+
* Returns null when the element isn't the single-JSX-child shape, or the
|
|
244
|
+
* child is not one of the components this wrapper exists to wrap (a
|
|
245
|
+
* ratio-box `<iframe>`, a native `<select>`), so the caller can keep the
|
|
246
|
+
* wrapper.
|
|
247
|
+
*/
|
|
248
|
+
function collapseOntoChild(ctx, path, element, where, expected) {
|
|
249
|
+
const children = (element.children ?? []).filter((c) => !(c.type === 'JSXText' && c.value.trim() === ''));
|
|
250
|
+
if (children.length !== 1 || children[0].type !== 'JSXElement') {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const child = children[0];
|
|
254
|
+
const childPath = ctx.resolve?.(child.openingElement?.name);
|
|
255
|
+
const accepted = Array.isArray(expected) ? expected : [expected];
|
|
256
|
+
if (!childPath || !accepted.includes(childPath.join('.')))
|
|
257
|
+
return null;
|
|
258
|
+
// Spreads have no name to collide on; they move across as written — as
|
|
259
|
+
// one block, in source order, placed FIRST so the child's own explicit
|
|
260
|
+
// props still win over them, the same rule the named branch keeps.
|
|
261
|
+
const spreads = (element.openingElement.attributes ?? []).filter((a) => a.type === 'JSXSpreadAttribute');
|
|
262
|
+
for (const spread of spreads)
|
|
263
|
+
removeAttr(element, spread);
|
|
264
|
+
if (spreads.length > 0) {
|
|
265
|
+
child.openingElement.attributes = [
|
|
266
|
+
...spreads,
|
|
267
|
+
...(child.openingElement.attributes ?? []),
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
for (const attr of [...(element.openingElement.attributes ?? [])]) {
|
|
271
|
+
const name = attr.name.name;
|
|
272
|
+
if (findAttr(child, name)) {
|
|
273
|
+
addTodo(ctx, path, `prop:${name}`, `\`${where}\` folded into its child, but both set \`${name}\`; the child's value was kept — reconcile by hand`);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
removeAttr(element, attr);
|
|
277
|
+
addAttr(child, attr);
|
|
278
|
+
}
|
|
279
|
+
path.replace(child);
|
|
280
|
+
ctx.dirty = true;
|
|
281
|
+
return { replaced: true };
|
|
282
|
+
}
|
|
283
|
+
return { replaceWithPlain, collapseOntoChild };
|
|
284
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulma's responsive viewports and the suffix bestax puts on its per-viewport
|
|
3
|
+
* helper props (`displayTabletOnly`, `visibilityTouch`, …).
|
|
4
|
+
*
|
|
5
|
+
* bestax's `validViewports` carries all nine, and the `display*` and
|
|
6
|
+
* `visibility*` families declare a prop for every one of them. The
|
|
7
|
+
* typography families (`textSize*`, `textAlign*`) declare only the five
|
|
8
|
+
* non-`only`, non-`touch` viewports — a source that flattens those must
|
|
9
|
+
* consult `TYPOGRAPHY_VIEWPORTS` rather than this table.
|
|
10
|
+
*
|
|
11
|
+
* Source-agnostic: keyed by Bulma's own suffixes, which every source library
|
|
12
|
+
* spells the same way.
|
|
13
|
+
*/
|
|
14
|
+
export declare const VIEWPORT_SUFFIX: Record<string, string>;
|
|
15
|
+
/** The viewports bestax's `textSize*` / `textAlign*` props exist for. */
|
|
16
|
+
export declare const TYPOGRAPHY_VIEWPORTS: readonly ["mobile", "tablet", "desktop", "widescreen", "fullhd"];
|
|
17
|
+
//# sourceMappingURL=viewports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewports.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/viewports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAUlD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,kEAMvB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulma's responsive viewports and the suffix bestax puts on its per-viewport
|
|
3
|
+
* helper props (`displayTabletOnly`, `visibilityTouch`, …).
|
|
4
|
+
*
|
|
5
|
+
* bestax's `validViewports` carries all nine, and the `display*` and
|
|
6
|
+
* `visibility*` families declare a prop for every one of them. The
|
|
7
|
+
* typography families (`textSize*`, `textAlign*`) declare only the five
|
|
8
|
+
* non-`only`, non-`touch` viewports — a source that flattens those must
|
|
9
|
+
* consult `TYPOGRAPHY_VIEWPORTS` rather than this table.
|
|
10
|
+
*
|
|
11
|
+
* Source-agnostic: keyed by Bulma's own suffixes, which every source library
|
|
12
|
+
* spells the same way.
|
|
13
|
+
*/
|
|
14
|
+
export const VIEWPORT_SUFFIX = {
|
|
15
|
+
mobile: 'Mobile',
|
|
16
|
+
tablet: 'Tablet',
|
|
17
|
+
'tablet-only': 'TabletOnly',
|
|
18
|
+
touch: 'Touch',
|
|
19
|
+
desktop: 'Desktop',
|
|
20
|
+
'desktop-only': 'DesktopOnly',
|
|
21
|
+
widescreen: 'Widescreen',
|
|
22
|
+
'widescreen-only': 'WidescreenOnly',
|
|
23
|
+
fullhd: 'Fullhd',
|
|
24
|
+
};
|
|
25
|
+
/** The viewports bestax's `textSize*` / `textAlign*` props exist for. */
|
|
26
|
+
export const TYPOGRAPHY_VIEWPORTS = [
|
|
27
|
+
'mobile',
|
|
28
|
+
'tablet',
|
|
29
|
+
'desktop',
|
|
30
|
+
'widescreen',
|
|
31
|
+
'fullhd',
|
|
32
|
+
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* package.json migration: swap bloomer for @allxsmith/bestax-bulma, move the
|
|
3
|
+
* app's own Bulma to v1, and replace the dead node-sass with dart-sass. Pure
|
|
4
|
+
* data-in/data-out — the CLI owns file IO, and no package manager is ever
|
|
5
|
+
* invoked (the report tells the user to install).
|
|
6
|
+
*
|
|
7
|
+
* bloomer is the react-bulma-components case, not the rbx one: it never
|
|
8
|
+
* depended on Bulma itself (its README says "npm install bulma"), so there is
|
|
9
|
+
* no pinned Bulma to free and no extension package to report — the manifest
|
|
10
|
+
* carries whatever 0.6-era `bulma` the app declared, and that is what gets
|
|
11
|
+
* bumped. `create-react-class` and `prop-types` were bloomer's dependencies,
|
|
12
|
+
* not the app's; if they appear here the app declared them for its own
|
|
13
|
+
* reasons, so they are left alone.
|
|
14
|
+
*/
|
|
15
|
+
import type { DependenciesUpdate } from '../../types.js';
|
|
16
|
+
export declare const updateDependencies: DependenciesUpdate;
|
|
17
|
+
//# sourceMappingURL=deps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAUzD,eAAO,MAAM,kBAAkB,EAAE,kBAsHhC,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* package.json migration: swap bloomer for @allxsmith/bestax-bulma, move the
|
|
3
|
+
* app's own Bulma to v1, and replace the dead node-sass with dart-sass. Pure
|
|
4
|
+
* data-in/data-out — the CLI owns file IO, and no package manager is ever
|
|
5
|
+
* invoked (the report tells the user to install).
|
|
6
|
+
*
|
|
7
|
+
* bloomer is the react-bulma-components case, not the rbx one: it never
|
|
8
|
+
* depended on Bulma itself (its README says "npm install bulma"), so there is
|
|
9
|
+
* no pinned Bulma to free and no extension package to report — the manifest
|
|
10
|
+
* carries whatever 0.6-era `bulma` the app declared, and that is what gets
|
|
11
|
+
* bumped. `create-react-class` and `prop-types` were bloomer's dependencies,
|
|
12
|
+
* not the app's; if they appear here the app declared them for its own
|
|
13
|
+
* reasons, so they are left alone.
|
|
14
|
+
*/
|
|
15
|
+
import { isPreV1 } from '../_shared/semver-range.js';
|
|
16
|
+
const BESTAX_RANGE = '^5';
|
|
17
|
+
const BULMA_RANGE = '^1.0.4';
|
|
18
|
+
// Bulma v1's sass tree uses `color.channel(…)` — needs dart-sass ≥ 1.79.
|
|
19
|
+
const SASS_RANGE = '^1.79.0';
|
|
20
|
+
const DEP_SECTIONS = ['dependencies', 'devDependencies'];
|
|
21
|
+
export const updateDependencies = (filePath, pkg, collector, options) => {
|
|
22
|
+
const changes = [];
|
|
23
|
+
const next = pkg;
|
|
24
|
+
const section = (name) => (next[name] ?? undefined);
|
|
25
|
+
const note = (message) => {
|
|
26
|
+
changes.push(message);
|
|
27
|
+
collector?.add({ file: filePath, line: null, rule: 'deps', message });
|
|
28
|
+
};
|
|
29
|
+
// bloomer goes away entirely.
|
|
30
|
+
let removedSource = false;
|
|
31
|
+
for (const name of DEP_SECTIONS) {
|
|
32
|
+
const deps = section(name);
|
|
33
|
+
if (deps && 'bloomer' in deps) {
|
|
34
|
+
delete deps.bloomer;
|
|
35
|
+
removedSource = true;
|
|
36
|
+
note(`removed bloomer from ${name}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// The transform deliberately RETAINS imports for components with no bestax
|
|
40
|
+
// equivalent (Tile, the Bulma 0.4 Nav family) so a partially migrated app
|
|
41
|
+
// still runs. Removing the package from the manifest strands exactly those
|
|
42
|
+
// imports once the user runs the install the report asks for, so say so
|
|
43
|
+
// rather than letting them find out at build time.
|
|
44
|
+
if (removedSource && options.sourceStillImported) {
|
|
45
|
+
collector?.add({
|
|
46
|
+
file: filePath,
|
|
47
|
+
line: null,
|
|
48
|
+
rule: 'deps',
|
|
49
|
+
message: 'bloomer was removed from package.json, but some files still import it for components with no bestax equivalent — resolve those `TODO(bestax-migrate)` imports before installing, or re-add bloomer until you have',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// @allxsmith/bestax-bulma comes in (runtime dependency).
|
|
53
|
+
const dependencies = (next.dependencies ??= {});
|
|
54
|
+
if (!dependencies['@allxsmith/bestax-bulma'] &&
|
|
55
|
+
!section('devDependencies')?.['@allxsmith/bestax-bulma']) {
|
|
56
|
+
dependencies['@allxsmith/bestax-bulma'] = BESTAX_RANGE;
|
|
57
|
+
note(`added @allxsmith/bestax-bulma ${BESTAX_RANGE} to dependencies`);
|
|
58
|
+
}
|
|
59
|
+
// Bulma: bump a pre-1 range; add only when sources still reference bulma/…
|
|
60
|
+
// directly (otherwise it arrives transitively via bestax-bulma).
|
|
61
|
+
let bulmaDeclared = false;
|
|
62
|
+
for (const name of DEP_SECTIONS) {
|
|
63
|
+
const deps = section(name);
|
|
64
|
+
if (deps?.bulma) {
|
|
65
|
+
bulmaDeclared = true;
|
|
66
|
+
if (isPreV1(deps.bulma)) {
|
|
67
|
+
deps.bulma = BULMA_RANGE;
|
|
68
|
+
note(`bumped bulma to ${BULMA_RANGE} in ${name} (was pre-1.0)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!bulmaDeclared && options.bulmaReferenced) {
|
|
73
|
+
dependencies.bulma = BULMA_RANGE;
|
|
74
|
+
note(`added bulma ${BULMA_RANGE} to dependencies (sources import bulma/… directly)`);
|
|
75
|
+
}
|
|
76
|
+
// bestax-bulma requires React 18/19; bloomer peer-depended on React ^16.2.
|
|
77
|
+
// Report only — a React major upgrade is the app's own migration step.
|
|
78
|
+
for (const name of DEP_SECTIONS) {
|
|
79
|
+
const range = section(name)?.react;
|
|
80
|
+
if (range && /^[~^]?(?:[0-9]|1[0-7])(?:[.x]|$)/.test(range.trim())) {
|
|
81
|
+
collector?.add({
|
|
82
|
+
file: filePath,
|
|
83
|
+
line: null,
|
|
84
|
+
rule: 'peer-deps',
|
|
85
|
+
message: `react ${range} predates bestax-bulma's peer range (^18 || ^19) — upgrade react and react-dom to 18 or 19 before installing`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Font Awesome older than 6 conflicts with bestax-bulma's optional peer
|
|
90
|
+
// range and makes `npm install` fail with ERESOLVE. Report only — icon
|
|
91
|
+
// names change across FA majors, so upgrading is the app's decision. (A
|
|
92
|
+
// bloomer-era app more often loads Font Awesome 4 from a CDN <link>, which
|
|
93
|
+
// no manifest pass can see; the Icon conversion flags those in the code.)
|
|
94
|
+
for (const name of DEP_SECTIONS) {
|
|
95
|
+
const range = section(name)?.['@fortawesome/fontawesome-free'];
|
|
96
|
+
if (range && /^[~^]?[0-5](?:[.x]|$)/.test(range.trim())) {
|
|
97
|
+
collector?.add({
|
|
98
|
+
file: filePath,
|
|
99
|
+
line: null,
|
|
100
|
+
rule: 'peer-deps',
|
|
101
|
+
message: `@fortawesome/fontawesome-free ${range} predates bestax-bulma's optional peer range (^6.7.2 || ^7.0.0) — upgrade it, or install with \`npm install --legacy-peer-deps\``,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// node-sass is dead; dart-sass replaces it in the same section.
|
|
106
|
+
for (const name of DEP_SECTIONS) {
|
|
107
|
+
const deps = section(name);
|
|
108
|
+
if (deps && 'node-sass' in deps) {
|
|
109
|
+
delete deps['node-sass'];
|
|
110
|
+
note(`removed node-sass from ${name}`);
|
|
111
|
+
const sassDeclared = DEP_SECTIONS.some(s => section(s)?.sass);
|
|
112
|
+
if (!sassDeclared) {
|
|
113
|
+
deps.sass = SASS_RANGE;
|
|
114
|
+
note(`added sass ${SASS_RANGE} to ${name} (replaces node-sass)`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return changes.length > 0 ? next : null;
|
|
119
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,eAAO,MAAM,OAAO,EAAE,eAMrB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import transform from './transform.js';
|
|
2
|
+
import { transformStyles } from './styles.js';
|
|
3
|
+
import { updateDependencies } from './deps.js';
|
|
4
|
+
export const bloomer = {
|
|
5
|
+
name: 'bloomer',
|
|
6
|
+
label: 'bloomer (0.6) → @allxsmith/bestax-bulma',
|
|
7
|
+
transform,
|
|
8
|
+
transformStyles,
|
|
9
|
+
updateDependencies,
|
|
10
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bloomer (0.6) → @allxsmith/bestax-bulma mapping tables.
|
|
3
|
+
*
|
|
4
|
+
* Data only — no AST work. `MAPPING` is the single source of truth for what
|
|
5
|
+
* the transform does with each bloomer export; `BLOOMER_EXPORTS` vendors
|
|
6
|
+
* bloomer's public surface so `mapping-coverage.test.ts` can close it in both
|
|
7
|
+
* directions.
|
|
8
|
+
*
|
|
9
|
+
* bloomer's shape is the reverse of rbx's: every export is a FLAT name
|
|
10
|
+
* (`CardHeaderTitle`, `NavbarItem`), while bestax groups the same components
|
|
11
|
+
* into dotted compounds (`Card.Header.Title`, `Navbar.Item`). So most rows
|
|
12
|
+
* here rename a flat identifier onto a dotted target — a shape the rename and
|
|
13
|
+
* value-reference passes already build, since the first segment of a dotted
|
|
14
|
+
* target is what gets imported.
|
|
15
|
+
*
|
|
16
|
+
* Its modifier vocabulary is already bestax's for the booleans (`isActive`,
|
|
17
|
+
* `isLoading`, `isOutlined`, …) — those are `{}` entries, present so the
|
|
18
|
+
* universal pass never sees them. What differs is the value-carrying props:
|
|
19
|
+
* `isColor` → `color`, `isSize` → `size` (claimed per component, because its
|
|
20
|
+
* type differs per component: numeric on Title/Subtitle, sizes-or-fractions on
|
|
21
|
+
* Column, `'128x128'` on Image, `'large'` elsewhere), `isAlign` → whatever
|
|
22
|
+
* the target calls it, and `tag` → `as` where bestax declares one.
|
|
23
|
+
*/
|
|
24
|
+
import type { ComponentMapping, PropAction } from '../../types.js';
|
|
25
|
+
/**
|
|
26
|
+
* `render` (`Bulma.Render`) let a component hand its computed props to a
|
|
27
|
+
* custom renderer. bestax has no render-prop escape hatch, and there is no
|
|
28
|
+
* way to rewrite a function value into markup, so it is always a TODO.
|
|
29
|
+
*/
|
|
30
|
+
export declare const RENDER_TODO: PropAction;
|
|
31
|
+
/**
|
|
32
|
+
* Helper props `withHelpersModifiers` mixes into EVERY bloomer component.
|
|
33
|
+
* Applied after each component's own prop map, to whatever attributes are
|
|
34
|
+
* left.
|
|
35
|
+
*
|
|
36
|
+
* `isDisplay` and `isHidden` are deliberately absent: they are consumed
|
|
37
|
+
* structurally by responsive.ts before this pass runs (their values are
|
|
38
|
+
* strings, arrays or objects that flatten to several bestax props), and
|
|
39
|
+
* bestax has no prop of either name, so a leftover would be an
|
|
40
|
+
* excess-property type error rather than a TODO.
|
|
41
|
+
*/
|
|
42
|
+
export declare const UNIVERSAL_PROPS: Record<string, PropAction>;
|
|
43
|
+
/**
|
|
44
|
+
* The helper props responsive.ts consumes. Named here so the plain-element
|
|
45
|
+
* path can strip them from an element that is becoming intrinsic HTML: the
|
|
46
|
+
* structural pass runs before the rename step, which a `replaced: true`
|
|
47
|
+
* special skips.
|
|
48
|
+
*/
|
|
49
|
+
export declare const RESPONSIVE_PROPS: Record<string, string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* bestax targets that extend only React's HTML attributes — no
|
|
52
|
+
* `BulmaClassesProps` — so bloomer's universal helpers have no prop to land
|
|
53
|
+
* on. The transform turns each into a TODO naming the Bulma class instead of
|
|
54
|
+
* emitting an excess-property type error.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Targets whose `className` cannot be used: `Navbar.Divider` and
|
|
58
|
+
* `Pagination.Ellipsis` spread `{...props}` AFTER their own className (so one
|
|
59
|
+
* written here replaces the class that makes them work), and
|
|
60
|
+
* `Dropdown.Divider` is a bare `React.FC` that declares no props at all and
|
|
61
|
+
* renders its own `<hr>` regardless. A helper on one of these is named, never
|
|
62
|
+
* converted.
|
|
63
|
+
*/
|
|
64
|
+
export declare const NO_CLASSNAME_TARGETS: Set<string>;
|
|
65
|
+
export declare const HELPERLESS_TARGETS: Set<string>;
|
|
66
|
+
/** The Bulma class a helper prop stood for, for the hint on those targets. */
|
|
67
|
+
export declare function helperClassHint(name: string, value: string | number | boolean | undefined): string | null;
|
|
68
|
+
/** Bloomer's fraction and width spellings → bestax's `BulmaColumnSize` names. */
|
|
69
|
+
export declare const COLUMN_SIZE_MAP: Record<string, string>;
|
|
70
|
+
export declare const MAPPING: Record<string, ComponentMapping>;
|
|
71
|
+
/**
|
|
72
|
+
* bloomer's public export surface, vendored from `src/index.ts` at the SHA
|
|
73
|
+
* `scripts/validate-corpus-bloomer.mjs` pins, in that file's order. Every
|
|
74
|
+
* export is flat, so no entry carries sub-paths; the shape matches the other
|
|
75
|
+
* sources' tables so the coverage and doc tests port unchanged.
|
|
76
|
+
*
|
|
77
|
+
* `mapping-coverage.test.ts` walks this against `MAPPING` in both directions,
|
|
78
|
+
* so bloomer coverage cannot silently regress and `MAPPING` cannot grow an
|
|
79
|
+
* entry for something bloomer never exported.
|
|
80
|
+
*/
|
|
81
|
+
export declare const BLOOMER_EXPORTS: Record<string, string[]>;
|
|
82
|
+
/** Walk a dotted component path through `MAPPING`. */
|
|
83
|
+
export declare function resolveMapping(path: string[]): ComponentMapping | undefined;
|
|
84
|
+
//# sourceMappingURL=mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/sources/bloomer/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA2BnE;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,UAEzB,CAAC;AA+CF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsBtD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAG1D,CAAC;AAEF;;;;;GAKG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,aAI/B,CAAC;AAEH,eAAO,MAAM,kBAAkB,aAqB7B,CAAC;AAEH,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAC3C,MAAM,GAAG,IAAI,CA0Bf;AAED,iFAAiF;AACjF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlD,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAsoBpD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAiIpD,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CAM3E"}
|