bestax-migrate 2.1.6 → 2.2.1

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.
Files changed (64) hide show
  1. package/README.md +13 -8
  2. package/dist/cli.d.ts +53 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +159 -9
  5. package/dist/sources/_shared/imports.d.ts +46 -0
  6. package/dist/sources/_shared/imports.d.ts.map +1 -0
  7. package/dist/sources/_shared/imports.js +152 -0
  8. package/dist/sources/{react-bulma-components → _shared}/jsx-utils.d.ts +20 -12
  9. package/dist/sources/_shared/jsx-utils.d.ts.map +1 -0
  10. package/dist/sources/{react-bulma-components → _shared}/jsx-utils.js +15 -10
  11. package/dist/sources/_shared/make-styles-transform.d.ts +37 -0
  12. package/dist/sources/_shared/make-styles-transform.d.ts.map +1 -0
  13. package/dist/sources/_shared/make-styles-transform.js +440 -0
  14. package/dist/sources/_shared/props.d.ts +20 -0
  15. package/dist/sources/_shared/props.d.ts.map +1 -0
  16. package/dist/sources/{react-bulma-components → _shared}/props.js +11 -7
  17. package/dist/sources/_shared/semver-range.d.ts +31 -0
  18. package/dist/sources/_shared/semver-range.d.ts.map +1 -0
  19. package/dist/sources/_shared/semver-range.js +186 -0
  20. package/dist/sources/_shared/specials-utils.d.ts +45 -0
  21. package/dist/sources/_shared/specials-utils.d.ts.map +1 -0
  22. package/dist/sources/_shared/specials-utils.js +124 -0
  23. package/dist/sources/rbx/deps.d.ts +18 -0
  24. package/dist/sources/rbx/deps.d.ts.map +1 -0
  25. package/dist/sources/rbx/deps.js +192 -0
  26. package/dist/sources/rbx/index.d.ts +3 -0
  27. package/dist/sources/rbx/index.d.ts.map +1 -0
  28. package/dist/sources/rbx/index.js +10 -0
  29. package/dist/sources/rbx/mapping.d.ts +49 -0
  30. package/dist/sources/rbx/mapping.d.ts.map +1 -0
  31. package/dist/sources/rbx/mapping.js +924 -0
  32. package/dist/sources/rbx/responsive.d.ts +30 -0
  33. package/dist/sources/rbx/responsive.d.ts.map +1 -0
  34. package/dist/sources/rbx/responsive.js +279 -0
  35. package/dist/sources/rbx/specials.d.ts +21 -0
  36. package/dist/sources/rbx/specials.d.ts.map +1 -0
  37. package/dist/sources/rbx/specials.js +706 -0
  38. package/dist/sources/rbx/styles.d.ts +21 -0
  39. package/dist/sources/rbx/styles.d.ts.map +1 -0
  40. package/dist/sources/rbx/styles.js +29 -0
  41. package/dist/sources/rbx/transform.d.ts +25 -0
  42. package/dist/sources/rbx/transform.d.ts.map +1 -0
  43. package/dist/sources/rbx/transform.js +911 -0
  44. package/dist/sources/react-bulma-components/deps.d.ts.map +1 -1
  45. package/dist/sources/react-bulma-components/deps.js +17 -3
  46. package/dist/sources/react-bulma-components/responsive.d.ts +1 -1
  47. package/dist/sources/react-bulma-components/responsive.d.ts.map +1 -1
  48. package/dist/sources/react-bulma-components/responsive.js +1 -1
  49. package/dist/sources/react-bulma-components/specials.d.ts +3 -9
  50. package/dist/sources/react-bulma-components/specials.d.ts.map +1 -1
  51. package/dist/sources/react-bulma-components/specials.js +4 -91
  52. package/dist/sources/react-bulma-components/styles.d.ts +9 -9
  53. package/dist/sources/react-bulma-components/styles.d.ts.map +1 -1
  54. package/dist/sources/react-bulma-components/styles.js +17 -389
  55. package/dist/sources/react-bulma-components/transform.d.ts.map +1 -1
  56. package/dist/sources/react-bulma-components/transform.js +378 -101
  57. package/dist/sources/registry.d.ts.map +1 -1
  58. package/dist/sources/registry.js +2 -0
  59. package/dist/types.d.ts +8 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +8 -7
  62. package/dist/sources/react-bulma-components/jsx-utils.d.ts.map +0 -1
  63. package/dist/sources/react-bulma-components/props.d.ts +0 -15
  64. package/dist/sources/react-bulma-components/props.d.ts.map +0 -1
@@ -1,7 +1,11 @@
1
1
  /**
2
- * Small AST helpers shared by the react-bulma-components transform passes.
3
- * All functions operate on the babel/tsx AST that jscodeshift's `tsx` parser
4
- * produces, via the `j` builder API.
2
+ * Small AST helpers shared by every source's transform passes. All functions
3
+ * operate on the babel/tsx AST that jscodeshift's `tsx` parser produces, via
4
+ * the `j` builder API.
5
+ *
6
+ * Nothing here may know which source library is being migrated — this module
7
+ * is imported by all of them (`react-bulma-components`, `rbx`, …), so any
8
+ * library-specific table belongs in that source's own `mapping.ts`.
5
9
  */
6
10
  /** Flatten a JSXIdentifier / JSXMemberExpression chain into name parts. */
7
11
  export function jsxNameParts(name) {
@@ -71,13 +75,14 @@ export function literalValueOf(attr) {
71
75
  return { kind: 'expression' };
72
76
  }
73
77
  /**
74
- * Classify a JSX attribute by the truthiness RBC evaluates it with at
75
- * runtime (`!!value`): every statically-known literal (boolean, string, or
76
- * number) resolves to `'truthy'`/`'falsy'`; only a genuine expression is
77
- * `'expression'`. This is the single place the six call sites that branch on
78
- * a "booleanish" prop (Button `remove`, Heading `heading`/`subtitle`,
79
- * `booleanToProp`, the `active` ladders, Field `multiline`) agree on what
80
- * counts as resolvable, so they can't drift out of sync with each other.
78
+ * Classify a JSX attribute by the truthiness the source library evaluates it
79
+ * with at runtime (`!!value`): every statically-known literal (boolean,
80
+ * string, or number) resolves to `'truthy'`/`'falsy'`; only a genuine
81
+ * expression is `'expression'`. This is the single place every call site that
82
+ * branches on a "booleanish" prop (RBC's Button `remove`, Heading
83
+ * `heading`/`subtitle`, `booleanToProp`, the `active` ladders, Field
84
+ * `multiline`; rbx's bare-boolean modifiers) agrees on what counts as
85
+ * resolvable, so they can't drift out of sync with each other.
81
86
  */
82
87
  export function resolveBooleanish(attr) {
83
88
  const literal = literalValueOf(attr);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Stylesheet migration, shared by every source: Bulma 0.9-era Sass
3
+ * (`@import` + `$var !default` overrides, `_all` aggregator partials) →
4
+ * Bulma v1 modules (`@use "bulma/sass" with (…)`), plus the bestax extras.
5
+ *
6
+ * Sass has no jscodeshift parser, so this is a conservative line-based text
7
+ * transform: it only rewrites patterns it can prove safe and leaves a
8
+ * `// TODO(bestax-migrate): …` everywhere else (line comments are valid in
9
+ * both SCSS and indented Sass).
10
+ *
11
+ * Everything about Bulma itself is source-independent — the only per-source
12
+ * parts are which package's own stylesheet specifiers to retire and which
13
+ * migration guide to link, so those arrive through `StylesConfig`.
14
+ */
15
+ import type { StylesTransform } from '../../types.js';
16
+ export interface StylesConfig {
17
+ /** npm package name of the source library, e.g. `rbx`. */
18
+ packageName: string;
19
+ /** URL of that source's migration guide, linked from every TODO. */
20
+ guideUrl: string;
21
+ /**
22
+ * Regex fragments (not literals) for the path suffixes that, together with
23
+ * the bare package name, count as the package's *root* stylesheet rather
24
+ * than one of its partials — these are replaced with a Bulma v1 root
25
+ * instead of being dropped.
26
+ */
27
+ rootStylesheetSuffixes: string[];
28
+ }
29
+ /**
30
+ * Build the stylesheet transform for one source library.
31
+ *
32
+ * `packageName` drives two regexes — one matching any `@import` of that
33
+ * package, one narrowing to its *root* stylesheet (which becomes a Bulma v1
34
+ * root rather than being dropped). Everything else is Bulma-side and shared.
35
+ */
36
+ export declare function makeStylesTransform(config: StylesConfig): StylesTransform;
37
+ //# sourceMappingURL=make-styles-transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"make-styles-transform.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/make-styles-transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,gBAAgB,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC;AAyMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,CA6UzE"}
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Stylesheet migration, shared by every source: Bulma 0.9-era Sass
3
+ * (`@import` + `$var !default` overrides, `_all` aggregator partials) →
4
+ * Bulma v1 modules (`@use "bulma/sass" with (…)`), plus the bestax extras.
5
+ *
6
+ * Sass has no jscodeshift parser, so this is a conservative line-based text
7
+ * transform: it only rewrites patterns it can prove safe and leaves a
8
+ * `// TODO(bestax-migrate): …` everywhere else (line comments are valid in
9
+ * both SCSS and indented Sass).
10
+ *
11
+ * Everything about Bulma itself is source-independent — the only per-source
12
+ * parts are which package's own stylesheet specifiers to retire and which
13
+ * migration guide to link, so those arrive through `StylesConfig`.
14
+ */
15
+ import path from 'node:path';
16
+ const TODO = 'TODO(bestax-migrate)';
17
+ const EXTRAS_USE = "@use '@allxsmith/bestax-bulma/scss/extras';";
18
+ /** Bulma v1 sass tree — directories that replaced the 0.9 `_all` partials. */
19
+ const V1_DIRS = new Set([
20
+ 'utilities',
21
+ 'base',
22
+ 'elements',
23
+ 'form',
24
+ 'components',
25
+ 'grid',
26
+ 'layout',
27
+ 'helpers',
28
+ 'themes',
29
+ ]);
30
+ /** Known Bulma v1 leaf partials (path after `bulma/sass/`). */
31
+ const V1_LEAVES = new Set([
32
+ 'utilities/initial-variables',
33
+ 'utilities/derived-variables',
34
+ 'utilities/css-variables',
35
+ 'utilities/mixins',
36
+ 'utilities/functions',
37
+ 'utilities/controls',
38
+ 'utilities/extends',
39
+ 'themes/light',
40
+ 'themes/dark',
41
+ 'themes/setup',
42
+ ]);
43
+ /**
44
+ * The 0.9 root imports that become `@use "bulma/sass"`. The specifier may be
45
+ * bare (`bulma/bulma`), tilde-prefixed (webpack), or a relative path into
46
+ * node_modules (`../../node_modules/bulma/bulma`, common under Parcel) —
47
+ * relative prefixes are preserved in the emitted `@use`.
48
+ */
49
+ const ROOT_IMPORT = /^(\s*)@import\s+(['"])((?:\.\.?\/)+node_modules\/|~)?bulma\/(?:bulma(?:\.sass|\.scss)?|css\/bulma(?:\.min)?\.css)\2\s*;?\s*$/;
50
+ /** 0.9 partial imports under bulma/sass/… (same prefix forms as the root). */
51
+ const PARTIAL_IMPORT = /^(\s*)@import\s+(['"])((?:\.\.?\/)+node_modules\/|~)?bulma\/sass\/([\w/-]+)\2\s*;?\s*$/;
52
+ /** A preserved path prefix for the rewritten `@use` (tilde never survives). */
53
+ function keptPrefix(prefix) {
54
+ return prefix && prefix !== '~' ? prefix : '';
55
+ }
56
+ /** Any other line that still `@import`s something bulma-ish. */
57
+ const OTHER_BULMA_IMPORT = /^\s*@import\s+['"][^'"]*bulma[^'"]*['"]/;
58
+ /**
59
+ * Third-party Bulma extension packages (`bulma-checkradio`, `bulma-switch`,
60
+ * …) — 0.9-era add-ons whose v1 compatibility varies; several are covered
61
+ * by the bestax extras. The `bulma-` package name must start at a specifier
62
+ * segment boundary (right after the quote/`~`, or after a `/`) so this does
63
+ * not also match a name that merely *contains* `bulma-`, like
64
+ * `react-bulma-components` (see EXTENSION_IMPORT's own regression test).
65
+ */
66
+ const EXTENSION_IMPORT = /^\s*@import\s+['"](?:~|[^'"]*\/)?bulma-([\w-]+?)(?:\/|\.|['"])/;
67
+ /**
68
+ * The source library's own stylesheet. This is not a third-party extension:
69
+ * it is the library being migrated away from, and `deps.ts` removes its
70
+ * package.json entry in the same run, so leaving the import in place breaks
71
+ * the Sass build. Any `react-bulma-components/…` specifier is dead the same
72
+ * way — the documented v3 setup paths (the bundled CSS, the "advanced" Sass
73
+ * entry point), but also deep partials and extensionless forms — so, like
74
+ * `transform.ts`, we match on the package prefix rather than enumerating
75
+ * shapes. The prefix may be bare, `~`-prefixed, or a relative node_modules
76
+ * path (the Parcel form `ROOT_IMPORT` also supports).
77
+ */
78
+ /**
79
+ * The subset of RBC stylesheet imports that are the library's own *root*: the
80
+ * bare package specifier, the documented Sass entry point (`src/index`), and
81
+ * the bundled v3 CSS (`dist/react-bulma-components(.min).css`). These pull in
82
+ * the whole library, which `bulma/sass` supersedes — so when the file already
83
+ * has a Bulma root, dropping one of these loses nothing. A deep RBC partial
84
+ * (`src/components/navbar.sass`) is NOT a root: `bulma/sass` doesn't carry a
85
+ * given partial's styles, so removing one silently would drop CSS. We flag
86
+ * those instead (the package's "never a silent skip" rule).
87
+ */
88
+ /**
89
+ * A Bulma module root already pulled in via `@use` — the file's own
90
+ * `@use 'bulma/sass'` (any prefix, configured or not) or the bestax bundle
91
+ * (`scss/bestax`, which itself loads `bulma/sass with (…)`). When one is
92
+ * present we must not emit a second root: a duplicate `bulma/sass` namespace,
93
+ * or reconfiguring an already-loaded module, is a hard Sass error.
94
+ */
95
+ const USE_BULMA_ROOT = /^\s*@use\s+(['"])(?:~|(?:\.\.?\/)+node_modules\/)?(?:bulma\/sass|@allxsmith\/bestax-bulma\/(?:src\/)?scss\/bestax)\1/;
96
+ /** Any `$name: value;` declaration (with or without `!default`). */
97
+ const VAR_DECL = /^\s*\$([\w-]+)\s*:\s*(.+?)\s*(!default)?\s*;\s*$/;
98
+ /**
99
+ * Strips Sass block comments (slash-star … star-slash spans) from a value
100
+ * before it is analyzed for foldability or top-level commas. A comment can
101
+ * carry an unbalanced quote (an apostrophe in `user's`) or a stray `(`/`$`
102
+ * that would otherwise fool the character scanners into mis-reading the
103
+ * value's structure; the comment is inert to Sass, so removing it for
104
+ * analysis is safe. The emitted value still carries the comment verbatim.
105
+ *
106
+ * The scan tracks quote state first and only recognizes a block comment
107
+ * outside a string, in one pass — a bare regex would treat a slash-star …
108
+ * star-slash-shaped span that straddles two quoted strings (opening marker in
109
+ * one string, closing marker in the next) as a comment and delete the real
110
+ * top-level comma between them, so a genuine list would fold unparenthesized.
111
+ * An unterminated comment consumes the rest of the value.
112
+ */
113
+ function stripSassComments(value) {
114
+ let result = '';
115
+ let quote = null;
116
+ let escaped = false;
117
+ for (let i = 0; i < value.length; i += 1) {
118
+ const char = value[i];
119
+ if (quote) {
120
+ result += char;
121
+ if (escaped) {
122
+ escaped = false;
123
+ }
124
+ else if (char === '\\') {
125
+ escaped = true;
126
+ }
127
+ else if (char === quote) {
128
+ quote = null;
129
+ }
130
+ continue;
131
+ }
132
+ if (char === '/' && value[i + 1] === '*') {
133
+ const end = value.indexOf('*/', i + 2);
134
+ if (end === -1)
135
+ break; // unterminated comment — drop the rest
136
+ i = end + 1; // resume after the closing */
137
+ continue;
138
+ }
139
+ if (char === '"' || char === "'")
140
+ quote = char;
141
+ result += char;
142
+ }
143
+ return result;
144
+ }
145
+ /**
146
+ * A value is fold-safe for `with (…)` when it is a plain literal — no
147
+ * function calls, interpolation, variable references, or at-rules. Hex
148
+ * colors (`#ff6b35`) are fine; `#{…}` interpolation is not. Block comments are
149
+ * ignored for this test — their contents don't make the value non-literal.
150
+ */
151
+ function isFoldableValue(value) {
152
+ return !/[()$@]|#\{/.test(stripSassComments(value));
153
+ }
154
+ /**
155
+ * Whether `value` has a comma outside any quoted string — a bare Sass list
156
+ * (`'Nunito', sans-serif`) that must be parenthesized before it can sit
157
+ * inside `with (…)`, or Dart Sass reads the comma as an argument separator
158
+ * instead of a list delimiter. Block comments are stripped first so a quote or
159
+ * comma inside one (an apostrophe in `user's`) can't skew the scan.
160
+ */
161
+ function hasTopLevelComma(value) {
162
+ let quote = null;
163
+ let escaped = false;
164
+ for (const char of stripSassComments(value)) {
165
+ if (escaped) {
166
+ escaped = false;
167
+ continue;
168
+ }
169
+ if (char === '\\') {
170
+ escaped = true;
171
+ continue;
172
+ }
173
+ if (quote) {
174
+ if (char === quote)
175
+ quote = null;
176
+ continue;
177
+ }
178
+ if (char === '"' || char === "'") {
179
+ quote = char;
180
+ }
181
+ else if (char === ',') {
182
+ return true;
183
+ }
184
+ }
185
+ return false;
186
+ }
187
+ /** Formats a folded value for the `with (…)` clause, parenthesizing a bare list. */
188
+ function formatFoldedValue(value) {
189
+ return hasTopLevelComma(value) ? `(${value})` : value;
190
+ }
191
+ function report(collector, file, line, rule, message) {
192
+ collector?.add({ file, line, rule, message });
193
+ }
194
+ /**
195
+ * Build the stylesheet transform for one source library.
196
+ *
197
+ * `packageName` drives two regexes — one matching any `@import` of that
198
+ * package, one narrowing to its *root* stylesheet (which becomes a Bulma v1
199
+ * root rather than being dropped). Everything else is Bulma-side and shared.
200
+ */
201
+ export function makeStylesTransform(config) {
202
+ const { packageName, guideUrl: GUIDE } = config;
203
+ const pkg = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
204
+ // Already regex fragments — see StylesConfig.
205
+ const roots = config.rootStylesheetSuffixes.join('|');
206
+ /**
207
+ * Any @import of the source package's own stylesheets. The path after the
208
+ * package name is optional: `@import '~rbx'` resolves through the package's
209
+ * `style`/`sass` field and is a plausible spelling for a short package name,
210
+ * and SOURCE_ROOT_STYLESHEET below already accepts that bare form — so
211
+ * requiring the slash here made the bare case unreachable.
212
+ */
213
+ const SOURCE_STYLE_IMPORT = new RegExp(`^(\\s*)@import\\s+(['"])((?:\\.\\.?/)+node_modules/|~)?${pkg}(?:/[^'"]*)?\\2\\s*;?\\s*(?://[^\\n]*)?\\s*$`);
214
+ /** Cheap "is this file even about us?" pre-filter (see the guard below). */
215
+ const SOURCE_MENTION = new RegExp(pkg);
216
+ /** …narrowed to the ones that stand in for a whole Bulma root. */
217
+ const SOURCE_ROOT_STYLESHEET = new RegExp(`^\\s*@import\\s+(['"])(?:(?:\\.\\.?/)+node_modules/|~)?${pkg}${roots ? `(?:${roots})?` : ''}\\1\\s*;?\\s*(?://[^\\n]*)?\\s*$`);
218
+ const transformStyles = (filePath, source, collector, options) => {
219
+ const cssMode = options.cssMode ?? 'bestax';
220
+ // Cheap pre-filter. It must accept a file that names only the SOURCE
221
+ // package: rbx's documented Sass entry is `@import '~rbx/rbx'` and rbx
222
+ // pulls Bulma in itself, so such a file contains no "bulma" substring at
223
+ // all. Bailing on it left a dead import behind with no TODO and no report
224
+ // entry while deps.ts deleted the package in the same run — a silent
225
+ // build break, which is exactly what this package promises never to do.
226
+ if (!/bulma/.test(source) && !SOURCE_MENTION.test(source))
227
+ return null;
228
+ // Indented-syntax files: flag only — rewriting without a parser is unsafe.
229
+ if (path.extname(filePath) === '.sass') {
230
+ if (source.includes(`${TODO}`))
231
+ return null;
232
+ report(collector, filePath, 1, 'sass', `indented-syntax file references Bulma; convert its @import lines to @use "bulma/sass" by hand — see ${GUIDE}`);
233
+ return `// ${TODO}: convert Bulma 0.9 @import lines to @use "bulma/sass" with (…) — see ${GUIDE}\n${source}`;
234
+ }
235
+ const lines = source.split('\n');
236
+ const out = [];
237
+ let changed = false;
238
+ let extrasAdded = source.includes('@allxsmith/bestax-bulma/scss');
239
+ // Pass 1: locate the file's Bulma root and the safe leading variable
240
+ // overrides that fold into it. A real `bulma/…` @import root wins; otherwise
241
+ // the RBC stylesheet, which we rewrite into a root the same way — so a file
242
+ // that starts from RBC and one that starts from a bulma @import converge on
243
+ // the identical shape. A pre-existing `@use` root means we create no new one.
244
+ const rootImportIndex = lines.findIndex(line => ROOT_IMPORT.test(line));
245
+ // DELIBERATELY any source specifier, not just the root stylesheet. A
246
+ // reviewer flagged that a lone deep partial (`~rbx/base/helpers/badge`) is
247
+ // rewritten into a full `@use 'bulma/sass'` as though it were the whole
248
+ // framework. That is the intended behaviour, and it predates rbx: the
249
+ // react-bulma-components suite asserts it by name ("regardless of
250
+ // specifier shape"). The reasoning holds for both sources — every one of
251
+ // these imports is dead once deps.ts removes the package, and a file that
252
+ // styled itself from the library needs SOME Bulma root afterwards, so
253
+ // emitting one beats dropping it and leaving the file unstyled.
254
+ const sourceRootIndex = lines.findIndex(line => SOURCE_STYLE_IMPORT.test(line) && !line.includes(TODO));
255
+ const hasUseBulmaRoot = lines.some(line => USE_BULMA_ROOT.test(line));
256
+ const foldTargetIndex = hasUseBulmaRoot
257
+ ? -1
258
+ : rootImportIndex !== -1
259
+ ? rootImportIndex
260
+ : sourceRootIndex;
261
+ const foldableVars = [];
262
+ const unsafeVarLines = [];
263
+ if (foldTargetIndex !== -1) {
264
+ for (let i = 0; i < foldTargetIndex; i += 1) {
265
+ const line = lines[i];
266
+ const match = line.match(VAR_DECL);
267
+ if (match && isFoldableValue(match[2])) {
268
+ foldableVars.push({
269
+ index: i,
270
+ name: match[1],
271
+ value: match[2].trim(),
272
+ });
273
+ }
274
+ else if (/^\s*\$[\w-]+\s*:/.test(line)) {
275
+ unsafeVarLines.push(i);
276
+ }
277
+ }
278
+ }
279
+ const folded = new Set(foldableVars.map(v => v.index));
280
+ /**
281
+ * Emit a Bulma module root at `indent` (folding the leading var overrides
282
+ * gathered above into `with (…)`, flagging any unsafe ones), then, in
283
+ * bestax mode, the extras. `prefix` is a preserved relative-path prefix
284
+ * (raw-file toolchains resolve paths, not package specifiers) or ''.
285
+ */
286
+ const emitBulmaRoot = (indent, prefix, lineNo) => {
287
+ const bulmaSass = `${prefix}bulma/sass`;
288
+ if (foldableVars.length > 0) {
289
+ out.push(`${indent}@use '${bulmaSass}' with (`);
290
+ foldableVars.forEach(({ name, value }, index) => {
291
+ const comma = index < foldableVars.length - 1 ? ',' : '';
292
+ out.push(`${indent} $${name}: ${formatFoldedValue(value)}${comma}`);
293
+ });
294
+ out.push(`${indent});`);
295
+ }
296
+ else {
297
+ out.push(`${indent}@use '${bulmaSass}';`);
298
+ }
299
+ if (unsafeVarLines.length > 0) {
300
+ out.push(`${indent}// ${TODO}: the variable override(s) above use computed values; move them into the @use "bulma/sass" with (…) configuration by hand`);
301
+ report(collector, filePath, lineNo, 'sass', 'variable overrides with computed values could not be folded into `with (…)`');
302
+ }
303
+ if (cssMode === 'bestax' && !extrasAdded) {
304
+ out.push(prefix
305
+ ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
306
+ : `${indent}${EXTRAS_USE}`);
307
+ extrasAdded = true;
308
+ }
309
+ };
310
+ for (let i = 0; i < lines.length; i += 1) {
311
+ const line = lines[i];
312
+ if (i === rootImportIndex) {
313
+ const rootMatch = line.match(ROOT_IMPORT);
314
+ // A path-prefixed root import means the toolchain resolves raw file
315
+ // paths, not package specifiers — point at the shipped files directly.
316
+ emitBulmaRoot(rootMatch?.[1] ?? '', keptPrefix(rootMatch?.[3]), i + 1);
317
+ changed = true;
318
+ continue;
319
+ }
320
+ if (folded.has(i)) {
321
+ changed = true; // folded into with(...) above
322
+ continue;
323
+ }
324
+ const partial = line.match(PARTIAL_IMPORT);
325
+ if (partial) {
326
+ const [, indent, , rawPrefix, importPath] = partial;
327
+ const prefix = keptPrefix(rawPrefix);
328
+ // Partial-only files (modular builds with no root import) still need
329
+ // the extras for the bestax components' styling; the extras tree only
330
+ // reads Bulma utilities, never configures them, so this is load-safe.
331
+ const pushExtras = () => {
332
+ if (cssMode !== 'bestax' || extrasAdded || rootImportIndex !== -1) {
333
+ return;
334
+ }
335
+ out.push(prefix
336
+ ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
337
+ : `${indent}${EXTRAS_USE}`);
338
+ extrasAdded = true;
339
+ };
340
+ const segments = importPath.split('/');
341
+ const last = segments[segments.length - 1].replace(/^_/, '');
342
+ const dir = segments[0];
343
+ if ((last === 'all' || last === 'index') && V1_DIRS.has(dir)) {
344
+ out.push(`${indent}@use '${prefix}bulma/sass/${dir}';`);
345
+ pushExtras();
346
+ changed = true;
347
+ continue;
348
+ }
349
+ const leaf = segments.map(s => s.replace(/^_/, '')).join('/');
350
+ if (V1_DIRS.has(leaf) || V1_LEAVES.has(leaf)) {
351
+ out.push(`${indent}@use '${prefix}bulma/sass/${leaf}';`);
352
+ pushExtras();
353
+ changed = true;
354
+ continue;
355
+ }
356
+ out.push(`${indent}// ${TODO}: this Bulma 0.9 partial path no longer exists in Bulma v1; find its replacement under bulma/sass/ — see ${GUIDE}`);
357
+ out.push(line);
358
+ report(collector, filePath, i + 1, 'sass', `Bulma 0.9 partial path \`bulma/sass/${importPath}\` has no direct v1 equivalent`);
359
+ changed = true;
360
+ continue;
361
+ }
362
+ const sourceStyle = line.match(SOURCE_STYLE_IMPORT);
363
+ if (sourceStyle && !line.includes(TODO)) {
364
+ const indent = sourceStyle[1];
365
+ const prefix = keptPrefix(sourceStyle[3]);
366
+ if (i === sourceRootIndex &&
367
+ rootImportIndex === -1 &&
368
+ !hasUseBulmaRoot) {
369
+ // The library's own stylesheet never resolves post-migration (deps.ts
370
+ // removes react-bulma-components in the same run), and it is this
371
+ // file's only Bulma root, so rewrite it into one — the same shape the
372
+ // @import root path emits, folding any leading var overrides. Emitting
373
+ // a real `bulma/sass` (+ extras) rather than the hard-configured
374
+ // bestax bundle keeps this convergent with the @import path and avoids
375
+ // reconfiguring `bulma/sass` if a themed root exists elsewhere.
376
+ emitBulmaRoot(indent, prefix, i + 1);
377
+ if (cssMode === 'keep') {
378
+ // The replacement is unavoidable even in keep mode: RBC's own
379
+ // stylesheet is a v3 (Bulma 0.9) asset the migrated v1 components
380
+ // can't use. What changed depends on whether the manifest step ran.
381
+ const depsRan = options.deps !== false;
382
+ const detail = depsRan
383
+ ? 'its package.json entry is removed, so the old import no longer resolves; install bulma@^1'
384
+ : 'it targets Bulma 0.9, not the v1 your components now use; --no-deps kept the dependency, so install bulma@^1 alongside it';
385
+ out.push(`${indent}// ${TODO}: replaced ${packageName}'s own stylesheet with @use 'bulma/sass' — ${detail} — see ${GUIDE}`);
386
+ report(collector, filePath, i + 1, 'sass', depsRan
387
+ ? `replaced ${packageName}'s stylesheet import with bulma/sass; the package is removed from dependencies, so the old import would not resolve`
388
+ : `replaced ${packageName}'s stylesheet import with bulma/sass; it is a Bulma 0.9 asset the migrated v1 components can't use (--no-deps left the package in place)`);
389
+ }
390
+ else {
391
+ // bestax/bulma modes rewrite the file's root without a TODO — still
392
+ // report it so the migration summary reflects the restructured root.
393
+ report(collector, filePath, i + 1, 'sass', `replaced ${packageName}'s stylesheet import with a Bulma v1 @use root`);
394
+ }
395
+ }
396
+ else {
397
+ // A Bulma root already covers this file (a `bulma/…` @import we
398
+ // convert, the file's own `@use` root, or an earlier RBC line we
399
+ // rewrote). In bestax mode keep that root and add only the extras —
400
+ // never a second root, never the configured bundle that would
401
+ // reconfigure an already-loaded module.
402
+ if (cssMode === 'bestax' && !extrasAdded) {
403
+ out.push(prefix
404
+ ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
405
+ : `${indent}${EXTRAS_USE}`);
406
+ extrasAdded = true;
407
+ }
408
+ // Dropping the RBC line is only safe for its root/index stylesheet,
409
+ // which `bulma/sass` supersedes. A deep RBC partial can carry styles
410
+ // `bulma/sass` doesn't, so removing it silently would lose CSS — flag
411
+ // it with a TODO + report entry instead (never a silent skip).
412
+ if (!SOURCE_ROOT_STYLESHEET.test(line)) {
413
+ out.push(`${indent}// ${TODO}: dropped a ${packageName} stylesheet partial that has no Bulma v1 root equivalent; port any styles it carried beyond Bulma's own by hand — see ${GUIDE}`);
414
+ report(collector, filePath, i + 1, 'sass', `${packageName} stylesheet partial dropped; port any styles it carried beyond Bulma’s own by hand`);
415
+ }
416
+ }
417
+ changed = true;
418
+ continue;
419
+ }
420
+ if (OTHER_BULMA_IMPORT.test(line) && !line.includes(TODO)) {
421
+ const extension = line.match(EXTENSION_IMPORT);
422
+ if (extension) {
423
+ out.push(`// ${TODO}: bulma-${extension[1]} is a Bulma 0.9-era extension — check its Bulma v1 compatibility; the bestax extras already style Radio/Checkbox and the advanced form controls — see ${GUIDE}`);
424
+ out.push(line);
425
+ report(collector, filePath, i + 1, 'sass', `third-party Bulma extension \`bulma-${extension[1]}\` left in place; verify it against Bulma v1 or replace it with the bestax extras`);
426
+ changed = true;
427
+ continue;
428
+ }
429
+ out.push(`// ${TODO}: Bulma v1 uses @use instead of @import — see ${GUIDE}`);
430
+ out.push(line);
431
+ report(collector, filePath, i + 1, 'sass', 'unrecognized Bulma @import left in place; convert to @use by hand');
432
+ changed = true;
433
+ continue;
434
+ }
435
+ out.push(line);
436
+ }
437
+ return changed ? out.join('\n') : null;
438
+ };
439
+ return transformStyles;
440
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Prop-level conversion: applies a PropAction to a single attribute, plus the
3
+ * universal modifier-prop pass shared by every mapped component.
4
+ *
5
+ * Source-agnostic — the universal table is passed in by the caller rather than
6
+ * imported, so each source keeps its own `UNIVERSAL_PROPS` in its `mapping.ts`
7
+ * while sharing this interpreter.
8
+ */
9
+ import type { ASTPath } from 'jscodeshift';
10
+ import type { PropAction } from '../../types.js';
11
+ import { type TransformContext } from './jsx-utils.js';
12
+ /** Apply one PropAction to `attr` on `element`. */
13
+ export declare function applyPropAction(ctx: TransformContext, path: ASTPath<any>, element: any, attr: any, action: PropAction): void;
14
+ /**
15
+ * Apply the source library's universal modifier-prop conversions to every
16
+ * attribute that was not already handled by the component's own prop map.
17
+ * `universalProps` is the calling source's `UNIVERSAL_PROPS` table.
18
+ */
19
+ export declare function applyUniversalProps(ctx: TransformContext, path: ASTPath<any>, element: any, handled: Set<string>, universalProps: Record<string, PropAction>): void;
20
+ //# sourceMappingURL=props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/props.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EASL,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AA6BxB,mDAAmD;AACnD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,UAAU,GACjB,IAAI,CA6HN;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACzC,IAAI,CAQN"}
@@ -1,14 +1,17 @@
1
1
  /**
2
2
  * Prop-level conversion: applies a PropAction to a single attribute, plus the
3
3
  * universal modifier-prop pass shared by every mapped component.
4
+ *
5
+ * Source-agnostic — the universal table is passed in by the caller rather than
6
+ * imported, so each source keeps its own `UNIVERSAL_PROPS` in its `mapping.ts`
7
+ * while sharing this interpreter.
4
8
  */
5
- import { UNIVERSAL_PROPS } from './mapping.js';
6
9
  import { addAttr, addTodo, attributesOf, findAttr, literalValueOf, makeAttr, removeAttr, resolveBooleanish, } from './jsx-utils.js';
7
10
  /* eslint-disable @typescript-eslint/no-explicit-any */
8
11
  /**
9
12
  * Add a converted attribute unless the target name is already present — two
10
- * RBC props can map onto one bestax prop (e.g. `textTransform` + `italic`),
11
- * and a silent duplicate would be invalid JSX.
13
+ * source props can map onto one bestax prop (e.g. RBC's `textTransform` +
14
+ * `italic`), and a silent duplicate would be invalid JSX.
12
15
  */
13
16
  function addConverted(ctx, path, element, originalName, name, value) {
14
17
  if (findAttr(element, name)) {
@@ -109,15 +112,16 @@ export function applyPropAction(ctx, path, element, attr, action) {
109
112
  }
110
113
  }
111
114
  /**
112
- * Apply the universal RBC modifier-prop conversions to every attribute that
113
- * was not already handled by the component's own prop map.
115
+ * Apply the source library's universal modifier-prop conversions to every
116
+ * attribute that was not already handled by the component's own prop map.
117
+ * `universalProps` is the calling source's `UNIVERSAL_PROPS` table.
114
118
  */
115
- export function applyUniversalProps(ctx, path, element, handled) {
119
+ export function applyUniversalProps(ctx, path, element, handled, universalProps) {
116
120
  for (const attr of [...attributesOf(element)]) {
117
121
  const name = attr.name.name;
118
122
  if (handled.has(name))
119
123
  continue;
120
- const action = UNIVERSAL_PROPS[name];
124
+ const action = universalProps[name];
121
125
  if (!action)
122
126
  continue;
123
127
  applyPropAction(ctx, path, element, attr, action);
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Minimal semver-range reading for the manifest passes, shared by every source.
3
+ *
4
+ * Not a semver implementation: the workspace deliberately carries no `semver`
5
+ * dependency for this, and the two questions the passes ask are narrow. Both
6
+ * are answered conservatively, so an unrecognised shape is left alone rather
7
+ * than guessed at. The one thing this file must never do is rewrite a value it
8
+ * does not understand, so every accepting path below goes through one grammar.
9
+ */
10
+ /**
11
+ * True only when `range` PROVABLY admits no version >= 1.0.0.
12
+ *
13
+ * The first version of this matched a leading `0` and nothing else, so a range
14
+ * written as comparators (`>=0.7 <1`) was left on 0.x and then reported as
15
+ * already v1. It lived in the rbx source only; the react-bulma-components
16
+ * manifest pass kept the regex until review found the unported half.
17
+ * This walks each `||` alternative and its comparators: a set is pre-v1 when
18
+ * some comparator caps it below 1.0.0 and none of them opens it at 1 or above.
19
+ *
20
+ * Conservative by design: an unrecognised shape returns false and the range is
21
+ * left alone (and reported as such). Bumping a range that might already admit
22
+ * v1 would be the best-guess rewrite this package refuses to make.
23
+ */
24
+ export declare function isPreV1(range: string): boolean;
25
+ /**
26
+ * True when every comparator in every alternative parses to a semver major,
27
+ * so the headline can tell "already v1" apart from "not a version range at
28
+ * all" (`latest`, a git URL) rather than calling both v1.
29
+ */
30
+ export declare function isRecognisedRange(range: string): boolean;
31
+ //# sourceMappingURL=semver-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver-range.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/semver-range.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4BH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9C;AAgGD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBxD"}