bestax-migrate 2.1.5 → 2.2.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.
Files changed (61) hide show
  1. package/README.md +14 -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/specials-utils.d.ts +45 -0
  18. package/dist/sources/_shared/specials-utils.d.ts.map +1 -0
  19. package/dist/sources/_shared/specials-utils.js +124 -0
  20. package/dist/sources/rbx/deps.d.ts +18 -0
  21. package/dist/sources/rbx/deps.d.ts.map +1 -0
  22. package/dist/sources/rbx/deps.js +287 -0
  23. package/dist/sources/rbx/index.d.ts +3 -0
  24. package/dist/sources/rbx/index.d.ts.map +1 -0
  25. package/dist/sources/rbx/index.js +10 -0
  26. package/dist/sources/rbx/mapping.d.ts +49 -0
  27. package/dist/sources/rbx/mapping.d.ts.map +1 -0
  28. package/dist/sources/rbx/mapping.js +924 -0
  29. package/dist/sources/rbx/responsive.d.ts +30 -0
  30. package/dist/sources/rbx/responsive.d.ts.map +1 -0
  31. package/dist/sources/rbx/responsive.js +279 -0
  32. package/dist/sources/rbx/specials.d.ts +21 -0
  33. package/dist/sources/rbx/specials.d.ts.map +1 -0
  34. package/dist/sources/rbx/specials.js +706 -0
  35. package/dist/sources/rbx/styles.d.ts +21 -0
  36. package/dist/sources/rbx/styles.d.ts.map +1 -0
  37. package/dist/sources/rbx/styles.js +29 -0
  38. package/dist/sources/rbx/transform.d.ts +25 -0
  39. package/dist/sources/rbx/transform.d.ts.map +1 -0
  40. package/dist/sources/rbx/transform.js +911 -0
  41. package/dist/sources/react-bulma-components/deps.d.ts.map +1 -1
  42. package/dist/sources/react-bulma-components/deps.js +16 -0
  43. package/dist/sources/react-bulma-components/responsive.d.ts +1 -1
  44. package/dist/sources/react-bulma-components/responsive.d.ts.map +1 -1
  45. package/dist/sources/react-bulma-components/responsive.js +1 -1
  46. package/dist/sources/react-bulma-components/specials.d.ts +3 -9
  47. package/dist/sources/react-bulma-components/specials.d.ts.map +1 -1
  48. package/dist/sources/react-bulma-components/specials.js +4 -91
  49. package/dist/sources/react-bulma-components/styles.d.ts +9 -9
  50. package/dist/sources/react-bulma-components/styles.d.ts.map +1 -1
  51. package/dist/sources/react-bulma-components/styles.js +17 -389
  52. package/dist/sources/react-bulma-components/transform.d.ts.map +1 -1
  53. package/dist/sources/react-bulma-components/transform.js +378 -101
  54. package/dist/sources/registry.d.ts.map +1 -1
  55. package/dist/sources/registry.js +2 -0
  56. package/dist/types.d.ts +8 -0
  57. package/dist/types.d.ts.map +1 -1
  58. package/package.json +8 -7
  59. package/dist/sources/react-bulma-components/jsx-utils.d.ts.map +0 -1
  60. package/dist/sources/react-bulma-components/props.d.ts +0 -15
  61. package/dist/sources/react-bulma-components/props.d.ts.map +0 -1
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Socket Badge](https://socket.dev/api/badge/npm/package/bestax-migrate)](https://socket.dev/npm/package/bestax-migrate/overview)
7
7
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/allxsmith/bestax/badge)](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)
8
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14361/badge)](https://www.bestpractices.dev/projects/14361)
8
9
  [![npm provenance](https://img.shields.io/badge/npm-provenance-3fb950.svg)](https://www.npmjs.com/package/bestax-migrate#provenance)
9
10
  [![Security policy](https://img.shields.io/badge/security-policy-blue.svg)](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)
10
11
 
@@ -12,9 +13,12 @@ Codemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https:/
12
13
 
13
14
  Currently supported source libraries:
14
15
 
15
- | Source | Status |
16
- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
17
- | [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |
16
+ | Source | Status |
17
+ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
18
+ | [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |
19
+ | [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |
20
+
21
+ Pass the source library as the first argument: `bestax-migrate <source> <paths…>`.
18
22
 
19
23
  ## Requirements
20
24
 
@@ -29,6 +33,8 @@ the app being migrated — the source is read as text and is never executed.
29
33
  ```bash
30
34
  # Preview what would change (no writes)
31
35
  pnpm dlx bestax-migrate react-bulma-components src/ --dry
36
+ # …or, for an rbx app
37
+ pnpm dlx bestax-migrate rbx src/ --dry
32
38
 
33
39
  # Apply the migration
34
40
  pnpm dlx bestax-migrate react-bulma-components src/
@@ -38,15 +44,15 @@ npm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`.
38
44
 
39
45
  The codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to:
40
46
 
41
- - rewrite `react-bulma-components` imports to `@allxsmith/bestax-bulma` (including `const { Input } = Form` destructuring and namespace imports)
47
+ - rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports)
42
48
  - rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …)
43
49
  - convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign="center"` → `textAlign="centered"`, …)
44
50
  - flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`)
45
51
  - restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → `<Icon name=…>`)
46
52
  - 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'`
47
- - update `package.json`: remove `react-bulma-components`, add `@allxsmith/bestax-bulma`, bump `bulma` to `^1`, and swap the dead `node-sass` for dart `sass` (no install is ever run)
53
+ - 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
48
54
 
49
- Anything 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](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:
55
+ Anything 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)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:
50
56
 
51
57
  ```bash
52
58
  npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate
@@ -68,7 +74,7 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate
68
74
  2. Search for `TODO(bestax-migrate)` and resolve each comment
69
75
  3. Typecheck/build and review the rendered app
70
76
 
71
- Full walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components).
77
+ Full 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).
72
78
 
73
79
  ## Hardened by default
74
80
 
@@ -99,4 +105,4 @@ Full disclosure of every field and control: [Telemetry guide](https://bestax.io/
99
105
 
100
106
  ## License
101
107
 
102
- MIT © Alex Smith
108
+ Source code licensed MIT
package/dist/cli.d.ts CHANGED
@@ -22,6 +22,59 @@ export interface CliIo {
22
22
  log: (message: string) => void;
23
23
  error: (message: string) => void;
24
24
  }
25
+ /**
26
+ * Matches an import of `name` at a specifier boundary, in every form a
27
+ * retained reference can take: a root or DEEP JavaScript import
28
+ * (`from 'rbx'`, `from 'rbx/base/theme'` — deep ones are kept with a TODO), a
29
+ * bindingless side-effect `import 'rbx'` (which the transform leaves untouched
30
+ * because there is nothing to rewrite), the CommonJS / dynamic forms
31
+ * `require('rbx')` and `import('rbx')` — neither of which the jscodeshift
32
+ * transform rewrites, so both survive the run as live references — and the
33
+ * Sass forms the stylesheet transform preserves in indented `.sass` files
34
+ * (`@import '~rbx/rbx'`).
35
+ *
36
+ * Matching only `from '<pkg>'` meant a file whose ONLY remaining reference was
37
+ * a deep or Sass import let the manifest pass drop the package with no
38
+ * warning. Matching a bare substring is the opposite error, and worse for a
39
+ * name as short as `rbx`: prose and a sibling package like `rbx-utils` both
40
+ * counted as imports.
41
+ *
42
+ * Comments are allowed wherever whitespace is, because a webpack magic comment
43
+ * sits exactly there: `import(/* webpackChunkName: "x" *\/ 'rbx')`.
44
+ */
45
+ /**
46
+ * Blank out comments while preserving strings, offsets, and line breaks.
47
+ *
48
+ * The import matcher needs to know whether a keyword is real code, and no
49
+ * lookbehind can tell it: `// import { Box } from 'rbx'` is not an import,
50
+ * while the `//` inside `const u = 'http://x'; require('rbx')` is not a
51
+ * comment. Comment bytes become spaces rather than being deleted so the
52
+ * caller's line lookup still lines up with the original text.
53
+ *
54
+ * Deliberately small: this is not a JavaScript lexer. It tracks quotes and the
55
+ * two comment forms, which is what the matcher needs, and it is applied to
56
+ * `.sass` and `.vue` files too, where `//` is also a comment.
57
+ */
58
+ export declare function blankComments(text: string): string;
59
+ /**
60
+ * Matches an import of `name` at a specifier boundary, in every form a
61
+ * retained reference can take: a root or DEEP JavaScript import
62
+ * (`from 'rbx'`, `from 'rbx/base/theme'`), a bindingless side-effect
63
+ * `import 'rbx'`, a re-export, the CommonJS and dynamic forms `require('rbx')`
64
+ * and `import('rbx')` — none of which the jscodeshift transform rewrites, so
65
+ * all survive the run as live references — and the Sass form the stylesheet
66
+ * transform preserves in indented `.sass` files (`@import '~rbx/rbx'`).
67
+ *
68
+ * Run it over `blankComments(text)`, not the raw text: the keyword alternatives
69
+ * carry no comment awareness of their own by design, because that job needs a
70
+ * scanner rather than a lookbehind.
71
+ *
72
+ * Matching only `from '<pkg>'` meant a file whose ONLY remaining reference was
73
+ * a deep or Sass import let the manifest pass drop the package with no warning.
74
+ * Matching a bare substring is the opposite error, and worse for a name as
75
+ * short as `rbx`: prose and a sibling package like `rbx-utils` both counted.
76
+ */
77
+ export declare function makeSourceImportRe(name: string): RegExp;
25
78
  /**
26
79
  * One-question consent prompt (readline — this package carries no interactive
27
80
  * prompt dependency). Returns null when the question is cancelled (Ctrl-C /
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqBxE,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EAAE,EACjB,UAAU,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CAkBV;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAkB5D;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AA4ID;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,KAAK,EACT,KAAK,GAAE,QAAwB,EAC/B,MAAM,GAAE,QAAyB,EAGjC,aAAa,GAAE,OAAO,eAAiC,GACtD,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAyBzB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,EAAE,EAAE,KAAK,EACT,aAAa,GAAE,CAAC,EAAE,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAA0B,GAC7E,OAAO,CAAC,IAAI,CAAC,CAqBf;AAYD,wBAAgB,SAAS,CACvB,EAAE,GAAE,KAAkD,GACrD,OAAO,CAkHT"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqBxE,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EAAE,EACjB,UAAU,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CAkBV;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAkB5D;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AASD;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgDlD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsBvD;AA8JD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,KAAK,EACT,KAAK,GAAE,QAAwB,EAC/B,MAAM,GAAE,QAAyB,EAGjC,aAAa,GAAE,OAAO,eAAiC,GACtD,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAyBzB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,EAAE,EAAE,KAAK,EACT,aAAa,GAAE,CAAC,EAAE,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAA0B,GAC7E,OAAO,CAAC,IAAI,CAAC,CAqBf;AAYD,wBAAgB,SAAS,CACvB,EAAE,GAAE,KAAkD,GACrD,OAAO,CAkHT"}
package/dist/cli.js CHANGED
@@ -79,8 +79,132 @@ export function findPackageJsons(targets) {
79
79
  }
80
80
  return [...found].sort();
81
81
  }
82
+ /**
83
+ * Matches an import of `name` at a specifier boundary, in every form a
84
+ * retained reference can take: a root or DEEP JavaScript import
85
+ * (`from 'rbx'`, `from 'rbx/base/theme'` — deep ones are kept with a TODO), a
86
+ * bindingless side-effect `import 'rbx'` (which the transform leaves untouched
87
+ * because there is nothing to rewrite), the CommonJS / dynamic forms
88
+ * `require('rbx')` and `import('rbx')` — neither of which the jscodeshift
89
+ * transform rewrites, so both survive the run as live references — and the
90
+ * Sass forms the stylesheet transform preserves in indented `.sass` files
91
+ * (`@import '~rbx/rbx'`).
92
+ *
93
+ * Matching only `from '<pkg>'` meant a file whose ONLY remaining reference was
94
+ * a deep or Sass import let the manifest pass drop the package with no
95
+ * warning. Matching a bare substring is the opposite error, and worse for a
96
+ * name as short as `rbx`: prose and a sibling package like `rbx-utils` both
97
+ * counted as imports.
98
+ *
99
+ * Comments are allowed wherever whitespace is, because a webpack magic comment
100
+ * sits exactly there: `import(/* webpackChunkName: "x" *\/ 'rbx')`.
101
+ */
102
+ /**
103
+ * Blank out comments while preserving strings, offsets, and line breaks.
104
+ *
105
+ * The import matcher needs to know whether a keyword is real code, and no
106
+ * lookbehind can tell it: `// import { Box } from 'rbx'` is not an import,
107
+ * while the `//` inside `const u = 'http://x'; require('rbx')` is not a
108
+ * comment. Comment bytes become spaces rather than being deleted so the
109
+ * caller's line lookup still lines up with the original text.
110
+ *
111
+ * Deliberately small: this is not a JavaScript lexer. It tracks quotes and the
112
+ * two comment forms, which is what the matcher needs, and it is applied to
113
+ * `.sass` and `.vue` files too, where `//` is also a comment.
114
+ */
115
+ export function blankComments(text) {
116
+ let out = '';
117
+ let quote = null;
118
+ let i = 0;
119
+ while (i < text.length) {
120
+ const c = text[i];
121
+ const next = text[i + 1];
122
+ if (quote) {
123
+ if (c === '\\' && i + 1 < text.length) {
124
+ out += c + next;
125
+ i += 2;
126
+ continue;
127
+ }
128
+ if (c === quote)
129
+ quote = null;
130
+ out += c;
131
+ i += 1;
132
+ continue;
133
+ }
134
+ if (c === '"' || c === "'" || c === '`') {
135
+ quote = c;
136
+ out += c;
137
+ i += 1;
138
+ continue;
139
+ }
140
+ if (c === '/' && next === '/') {
141
+ while (i < text.length && text[i] !== '\n') {
142
+ out += ' ';
143
+ i += 1;
144
+ }
145
+ continue;
146
+ }
147
+ if (c === '/' && next === '*') {
148
+ out += ' ';
149
+ i += 2;
150
+ while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) {
151
+ out += text[i] === '\n' ? '\n' : ' ';
152
+ i += 1;
153
+ }
154
+ if (i < text.length) {
155
+ out += ' ';
156
+ i += 2;
157
+ }
158
+ continue;
159
+ }
160
+ out += c;
161
+ i += 1;
162
+ }
163
+ return out;
164
+ }
165
+ /**
166
+ * Matches an import of `name` at a specifier boundary, in every form a
167
+ * retained reference can take: a root or DEEP JavaScript import
168
+ * (`from 'rbx'`, `from 'rbx/base/theme'`), a bindingless side-effect
169
+ * `import 'rbx'`, a re-export, the CommonJS and dynamic forms `require('rbx')`
170
+ * and `import('rbx')` — none of which the jscodeshift transform rewrites, so
171
+ * all survive the run as live references — and the Sass form the stylesheet
172
+ * transform preserves in indented `.sass` files (`@import '~rbx/rbx'`).
173
+ *
174
+ * Run it over `blankComments(text)`, not the raw text: the keyword alternatives
175
+ * carry no comment awareness of their own by design, because that job needs a
176
+ * scanner rather than a lookbehind.
177
+ *
178
+ * Matching only `from '<pkg>'` meant a file whose ONLY remaining reference was
179
+ * a deep or Sass import let the manifest pass drop the package with no warning.
180
+ * Matching a bare substring is the opposite error, and worse for a name as
181
+ * short as `rbx`: prose and a sibling package like `rbx-utils` both counted.
182
+ */
183
+ export function makeSourceImportRe(name) {
184
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
185
+ const spec = `['"](?:~|(?:\\.\\.?/)+node_modules/)?${escaped}(?:/[^'"]*)?['"]`;
186
+ // Not the tail of a longer identifier: `myrequire('rbx')` is not a require,
187
+ // and `foo.import(...)` is not an import.
188
+ const lead = String.raw `(?<![\w$.])`;
189
+ return new RegExp([
190
+ // A static import or re-export. The keyword is what separates a real
191
+ // specifier from a bare `from` in prose ("Migrated from 'rbx' in 2019").
192
+ // `[^;]` spans the newlines of a multi-line specifier list without
193
+ // running past the statement.
194
+ `${lead}(?:import|export)\\b[^;]*?\\bfrom\\s*${spec}`,
195
+ // A bindingless side-effect import, and the Sass form.
196
+ `${lead}import\\s*${spec}`,
197
+ `@import\\s*${spec}`,
198
+ // The dynamic and CommonJS forms, which appear mid-expression.
199
+ // `require.resolve('rbx')` is as live a reference as `require('rbx')`
200
+ // and is just as unrewritten by the transform.
201
+ `${lead}(?:import|require(?:\\.resolve)?)\\s*\\(\\s*${spec}`,
202
+ ].join('|'));
203
+ }
82
204
  function migrateFiles(source, files, reporter, io, options) {
83
205
  let bulmaReferenced = false;
206
+ let sourceStillImported = false;
207
+ const sourceImportRe = makeSourceImportRe(source.name);
84
208
  for (const file of files) {
85
209
  const sourceText = fs.readFileSync(file, 'utf8');
86
210
  const collector = reporter.startFile();
@@ -103,12 +227,23 @@ function migrateFiles(source, files, reporter, io, options) {
103
227
  }
104
228
  catch (error) {
105
229
  io.error(chalk.red(`✖ ${file}: ${error.message}`));
230
+ // Read the signals off the ORIGINAL text before bailing. A parse
231
+ // failure in the only file importing the source library would
232
+ // otherwise let the manifest pass remove the package with no warning —
233
+ // the file still imports it, we just couldn't rewrite it.
234
+ if (/['"](?:~?bulma\/)/.test(sourceText))
235
+ bulmaReferenced = true;
236
+ if (sourceImportRe.test(blankComments(sourceText)))
237
+ sourceStillImported = true;
106
238
  reporter.finishFile(file, false, collector.entries);
107
239
  continue;
108
240
  }
109
241
  if (/['"](?:~?bulma\/)/.test(output ?? sourceText)) {
110
242
  bulmaReferenced = true;
111
243
  }
244
+ if (sourceImportRe.test(blankComments(output ?? sourceText))) {
245
+ sourceStillImported = true;
246
+ }
112
247
  if (output !== null) {
113
248
  if (options.print)
114
249
  io.log(output);
@@ -117,7 +252,7 @@ function migrateFiles(source, files, reporter, io, options) {
117
252
  }
118
253
  reporter.finishFile(file, output !== null, collector.entries);
119
254
  }
120
- return bulmaReferenced;
255
+ return { bulmaReferenced, sourceStillImported };
121
256
  }
122
257
  /**
123
258
  * Surface files in unparseable formats (.astro, .vue, …) that import the
@@ -126,13 +261,24 @@ function migrateFiles(source, files, reporter, io, options) {
126
261
  function reportUnsupportedFiles(source, targets, reporter, extensions) {
127
262
  const unsupported = UNSUPPORTED_EXTENSIONS.filter(ext => !extensions.includes(ext));
128
263
  if (unsupported.length === 0)
129
- return;
264
+ return false;
265
+ // Whether any unsupported file still references the source package. These
266
+ // are reported but never rewritten, so the reference survives the run —
267
+ // and the manifest pass must not remove the package out from under it.
268
+ let stillImported = false;
269
+ const importRe = makeSourceImportRe(source.name);
130
270
  for (const file of collectFiles(targets, unsupported)) {
131
271
  const text = fs.readFileSync(file, 'utf8');
132
- if (!text.includes(source.name))
272
+ // A substring match is not evidence of an import. `rbx` is three
273
+ // characters: prose in an .mdx file and a sibling package like
274
+ // `rbx-utils` both matched, which reported a file that imports nothing
275
+ // and pinned the dependency the manifest pass would otherwise remove.
276
+ const scanned = blankComments(text);
277
+ if (!importRe.test(scanned))
133
278
  continue;
279
+ stillImported = true;
134
280
  const collector = reporter.startFile();
135
- const line = text.split('\n').findIndex(l => l.includes(source.name)) + 1 || null;
281
+ const line = scanned.split('\n').findIndex(l => importRe.test(l)) + 1 || null;
136
282
  collector.add({
137
283
  file,
138
284
  line,
@@ -141,8 +287,9 @@ function reportUnsupportedFiles(source, targets, reporter, extensions) {
141
287
  });
142
288
  reporter.finishFile(file, false, collector.entries);
143
289
  }
290
+ return stillImported;
144
291
  }
145
- function migrateDependencies(source, targets, reporter, io, options, bulmaReferenced) {
292
+ function migrateDependencies(source, targets, reporter, io, options, deps) {
146
293
  if (!options.deps || !source.updateDependencies)
147
294
  return;
148
295
  for (const pkgPath of findPackageJsons(targets)) {
@@ -153,7 +300,8 @@ function migrateDependencies(source, targets, reporter, io, options, bulmaRefere
153
300
  const pkg = JSON.parse(raw);
154
301
  const next = source.updateDependencies(pkgPath, pkg, collector, {
155
302
  cssMode: options.cssMode,
156
- bulmaReferenced,
303
+ bulmaReferenced: deps.bulmaReferenced,
304
+ sourceStillImported: deps.sourceStillImported,
157
305
  });
158
306
  if (next !== null) {
159
307
  changed = true;
@@ -307,9 +455,11 @@ export function createCLI(io = { log: console.log, error: console.error }) {
307
455
  cssMode: options.css,
308
456
  };
309
457
  const reporter = new Reporter();
310
- const bulmaReferenced = migrateFiles(source, files, reporter, io, runOptions);
311
- reportUnsupportedFiles(source, targets, reporter, extensions);
312
- migrateDependencies(source, targets, reporter, io, runOptions, bulmaReferenced);
458
+ const depSignals = migrateFiles(source, files, reporter, io, runOptions);
459
+ const unsupportedImports = reportUnsupportedFiles(source, targets, reporter, extensions);
460
+ if (unsupportedImports)
461
+ depSignals.sourceStillImported = true;
462
+ migrateDependencies(source, targets, reporter, io, runOptions, depSignals);
313
463
  io.log(reporter.render(source.label + (runOptions.dry ? ' (dry run)' : '')));
314
464
  displayStarNudge(io);
315
465
  // Success path only: rule names and counts, never file/line/message.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Import-rewriting mechanics shared by every source transform: collecting the
3
+ * names a file already binds, and handing out non-colliding local names for
4
+ * the bestax imports a transform wants to add.
5
+ *
6
+ * Source-agnostic: nothing here knows which library is being migrated.
7
+ */
8
+ import type { ASTPath, Collection, JSCodeshift } from 'jscodeshift';
9
+ import type { TransformContext } from './jsx-utils.js';
10
+ export declare const nameOf: (node: any) => string;
11
+ /**
12
+ * Every identifier the file already binds — variables, function and class
13
+ * declarations, parameters (including destructured ones), and imports other
14
+ * than the source library's own (those are pruned by the caller).
15
+ *
16
+ * Importing a bestax component under a name the file already uses would
17
+ * redeclare it, so these names drive the aliasing in `makeReserve`.
18
+ */
19
+ export declare function collectBoundNames(j: JSCodeshift, root: Collection<any>, skipImportSource: string): Set<string>;
20
+ /**
21
+ * Build the `ctx.reserve` implementation: register a bestax import by its
22
+ * exported name and get back the local name to write in JSX — the plain name
23
+ * when it is free, otherwise `Bulma<Name>` (then `Bulma<Name>2`, …).
24
+ */
25
+ export declare function makeReserve(ctx: TransformContext, bound: Set<string>): (importedName: string) => string;
26
+ /**
27
+ * Tab-indented sources keep tabs when reprinted, so recast's output does not
28
+ * drift from the untouched lines around it.
29
+ */
30
+ export declare function prefersTabs(source: string): boolean;
31
+ /**
32
+ * True when the identifier `name` at `path` really resolves to the binding
33
+ * the import/alias passes recorded, rather than a local that shadows it.
34
+ *
35
+ * Both passes key on identifier TEXT, so a nested `function F({ Card })`
36
+ * parameter looked exactly like the source library's `Card`: its JSX was
37
+ * rewritten to bestax's `Card.FooterItem` and the destructured parameter was
38
+ * renamed alongside it, silently repointing the code at a different object.
39
+ *
40
+ * `expected` is the scope the binding was collected in — the program scope for
41
+ * an import (they are always top-level), or the declaring scope of a
42
+ * `const { Item } = Card` alias, which the alias pass records because it walks
43
+ * declarators at any depth.
44
+ */
45
+ export declare function resolvesToBinding(path: ASTPath<any>, name: string, expected: unknown): boolean;
46
+ //# sourceMappingURL=imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/imports.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIvD,eAAO,MAAM,MAAM,GAAI,MAAM,GAAG,KAAG,MACyB,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,WAAW,EACd,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,EACrB,gBAAgB,EAAE,MAAM,GACvB,GAAG,CAAC,MAAM,CAAC,CAmEb;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GACjB,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAiBlC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,GAChB,OAAO,CAST"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Import-rewriting mechanics shared by every source transform: collecting the
3
+ * names a file already binds, and handing out non-colliding local names for
4
+ * the bestax imports a transform wants to add.
5
+ *
6
+ * Source-agnostic: nothing here knows which library is being migrated.
7
+ */
8
+ /* eslint-disable @typescript-eslint/no-explicit-any */
9
+ export const nameOf = (node) => typeof node === 'string' ? node : String(node?.name ?? '');
10
+ /**
11
+ * Every identifier the file already binds — variables, function and class
12
+ * declarations, parameters (including destructured ones), and imports other
13
+ * than the source library's own (those are pruned by the caller).
14
+ *
15
+ * Importing a bestax component under a name the file already uses would
16
+ * redeclare it, so these names drive the aliasing in `makeReserve`.
17
+ */
18
+ export function collectBoundNames(j, root, skipImportSource) {
19
+ const bound = new Set();
20
+ const collectPattern = (node) => {
21
+ if (!node)
22
+ return;
23
+ switch (node.type) {
24
+ case 'Identifier':
25
+ bound.add(node.name);
26
+ break;
27
+ case 'ObjectPattern':
28
+ for (const prop of node.properties ?? []) {
29
+ collectPattern(prop.value ?? prop.argument);
30
+ }
31
+ break;
32
+ case 'ArrayPattern':
33
+ for (const el of node.elements ?? [])
34
+ collectPattern(el);
35
+ break;
36
+ case 'RestElement':
37
+ collectPattern(node.argument);
38
+ break;
39
+ case 'AssignmentPattern':
40
+ collectPattern(node.left);
41
+ break;
42
+ }
43
+ };
44
+ root.find(j.VariableDeclarator).forEach(path => collectPattern(path.node.id));
45
+ root.find(j.FunctionDeclaration).forEach(path => {
46
+ if (path.node.id)
47
+ bound.add(nameOf(path.node.id));
48
+ for (const param of path.node.params ?? [])
49
+ collectPattern(param);
50
+ });
51
+ root
52
+ .find(j.ClassDeclaration)
53
+ .forEach(path => path.node.id && bound.add(nameOf(path.node.id)));
54
+ // TypeScript declaration forms that occupy a name an import would collide
55
+ // with. An `enum Button` next to a generated `import { Button }` is a
56
+ // duplicate-identifier build break, and the alias pass could not see it.
57
+ // Interfaces and type aliases share the declaration space with an imported
58
+ // binding of the same name too, so they reserve as well — an unnecessary
59
+ // alias costs nothing, a missed one does not compile.
60
+ for (const kind of [
61
+ 'TSEnumDeclaration',
62
+ 'TSModuleDeclaration',
63
+ 'TSInterfaceDeclaration',
64
+ 'TSTypeAliasDeclaration',
65
+ ]) {
66
+ root.find(j[kind]).forEach((path) => {
67
+ const id = path.node.id;
68
+ if (id && (id.type === 'Identifier' || id.type === 'StringLiteral')) {
69
+ bound.add(nameOf(id));
70
+ }
71
+ });
72
+ }
73
+ root.find(j.FunctionExpression).forEach(path => {
74
+ for (const param of path.node.params ?? [])
75
+ collectPattern(param);
76
+ });
77
+ root.find(j.ArrowFunctionExpression).forEach(path => {
78
+ for (const param of path.node.params ?? [])
79
+ collectPattern(param);
80
+ });
81
+ root.find(j.ImportDeclaration).forEach(path => {
82
+ if (String(path.node.source.value) === skipImportSource)
83
+ return;
84
+ for (const spec of path.node.specifiers ?? []) {
85
+ if (spec.local)
86
+ bound.add(nameOf(spec.local));
87
+ }
88
+ });
89
+ return bound;
90
+ }
91
+ /**
92
+ * Build the `ctx.reserve` implementation: register a bestax import by its
93
+ * exported name and get back the local name to write in JSX — the plain name
94
+ * when it is free, otherwise `Bulma<Name>` (then `Bulma<Name>2`, …).
95
+ */
96
+ export function makeReserve(ctx, bound) {
97
+ return (importedName) => {
98
+ const existing = ctx.needed.get(importedName);
99
+ if (existing)
100
+ return existing;
101
+ let local = importedName;
102
+ if (bound.has(local)) {
103
+ local = `Bulma${importedName}`;
104
+ let suffix = 2;
105
+ const locals = new Set(ctx.needed.values());
106
+ while (bound.has(local) || locals.has(local)) {
107
+ local = `Bulma${importedName}${suffix}`;
108
+ suffix += 1;
109
+ }
110
+ }
111
+ ctx.needed.set(importedName, local);
112
+ return local;
113
+ };
114
+ }
115
+ /**
116
+ * Tab-indented sources keep tabs when reprinted, so recast's output does not
117
+ * drift from the untouched lines around it.
118
+ */
119
+ export function prefersTabs(source) {
120
+ let tabs = 0;
121
+ let spaces = 0;
122
+ for (const line of source.split('\n')) {
123
+ if (line.startsWith('\t'))
124
+ tabs += 1;
125
+ else if (line.startsWith(' '))
126
+ spaces += 1;
127
+ }
128
+ return tabs > spaces;
129
+ }
130
+ /**
131
+ * True when the identifier `name` at `path` really resolves to the binding
132
+ * the import/alias passes recorded, rather than a local that shadows it.
133
+ *
134
+ * Both passes key on identifier TEXT, so a nested `function F({ Card })`
135
+ * parameter looked exactly like the source library's `Card`: its JSX was
136
+ * rewritten to bestax's `Card.FooterItem` and the destructured parameter was
137
+ * renamed alongside it, silently repointing the code at a different object.
138
+ *
139
+ * `expected` is the scope the binding was collected in — the program scope for
140
+ * an import (they are always top-level), or the declaring scope of a
141
+ * `const { Item } = Card` alias, which the alias pass records because it walks
142
+ * declarators at any depth.
143
+ */
144
+ export function resolvesToBinding(path, name, expected) {
145
+ if (!expected)
146
+ return true;
147
+ const declaring = path.scope?.lookup(name);
148
+ // An unresolvable name keeps the old behaviour rather than silently
149
+ // skipping work: a miss here means no rewrite at all, which is worse than
150
+ // the shadowing case it guards against.
151
+ return !declaring || declaring === expected;
152
+ }
@@ -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
  import type { JSCodeshift } from 'jscodeshift';
7
11
  import type { ASTPath } from 'jscodeshift';
@@ -10,7 +14,7 @@ export interface TransformContext {
10
14
  j: JSCodeshift;
11
15
  file: string;
12
16
  collector: TodoCollector | undefined;
13
- /** RBC top-level import names that must survive the import rewrite. */
17
+ /** Source-library top-level import names that must survive the rewrite. */
14
18
  retained: Set<string>;
15
19
  /** bestax-bulma imports: imported name → local name (aliased on conflict). */
16
20
  needed: Map<string, string>;
@@ -26,7 +30,10 @@ export interface TransformContext {
26
30
  * Navbar.Item becomes Navbar.DropdownMenu).
27
31
  */
28
32
  overrides: WeakMap<object, string>;
29
- /** Resolves a JSX name node to its canonical RBC path (set by transform). */
33
+ /**
34
+ * Resolves a JSX name node to its canonical source-library path (set by the
35
+ * source's transform).
36
+ */
30
37
  resolve?: (name: any) => string[] | null;
31
38
  dirty: boolean;
32
39
  }
@@ -57,13 +64,14 @@ export type LiteralValue = {
57
64
  export declare function literalValueOf(attr: any): LiteralValue;
58
65
  export type Booleanish = 'truthy' | 'falsy' | 'expression';
59
66
  /**
60
- * Classify a JSX attribute by the truthiness RBC evaluates it with at
61
- * runtime (`!!value`): every statically-known literal (boolean, string, or
62
- * number) resolves to `'truthy'`/`'falsy'`; only a genuine expression is
63
- * `'expression'`. This is the single place the six call sites that branch on
64
- * a "booleanish" prop (Button `remove`, Heading `heading`/`subtitle`,
65
- * `booleanToProp`, the `active` ladders, Field `multiline`) agree on what
66
- * counts as resolvable, so they can't drift out of sync with each other.
67
+ * Classify a JSX attribute by the truthiness the source library evaluates it
68
+ * with at runtime (`!!value`): every statically-known literal (boolean,
69
+ * string, or number) resolves to `'truthy'`/`'falsy'`; only a genuine
70
+ * expression is `'expression'`. This is the single place every call site that
71
+ * branches on a "booleanish" prop (RBC's Button `remove`, Heading
72
+ * `heading`/`subtitle`, `booleanToProp`, the `active` ladders, Field
73
+ * `multiline`; rbx's bare-boolean modifiers) agrees on what counts as
74
+ * resolvable, so they can't drift out of sync with each other.
67
75
  */
68
76
  export declare function resolveBooleanish(attr: any): Booleanish;
69
77
  /** Create `name` (bare boolean) or `name="value"` attribute. */
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-utils.d.ts","sourceRoot":"","sources":["../../../src/sources/_shared/jsx-utils.ts"],"names":[],"mappings":"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"}