bestax-migrate 2.1.6 → 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 +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/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
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/deps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAazD,eAAO,MAAM,kBAAkB,EAAE,kBAmGhC,CAAC"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/deps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAazD,eAAO,MAAM,kBAAkB,EAAE,kBAqHhC,CAAC"}
@@ -21,13 +21,29 @@ export const updateDependencies = (filePath, pkg, collector, options) => {
21
21
  collector?.add({ file: filePath, line: null, rule: 'deps', message });
22
22
  };
23
23
  // react-bulma-components goes away entirely.
24
+ let removedSource = false;
24
25
  for (const name of DEP_SECTIONS) {
25
26
  const deps = section(name);
26
27
  if (deps && 'react-bulma-components' in deps) {
27
28
  delete deps['react-bulma-components'];
29
+ removedSource = true;
28
30
  note(`removed react-bulma-components from ${name}`);
29
31
  }
30
32
  }
33
+ // The transform deliberately RETAINS imports for components with no bestax
34
+ // equivalent (Element, Tile) so a partially migrated app still runs.
35
+ // Removing the package from the manifest strands exactly those imports once
36
+ // the user runs the install the report asks for, so say so rather than
37
+ // letting them find out at build time. rbx has carried this warning since
38
+ // it shipped; this source retains imports the same way and did not.
39
+ if (removedSource && options.sourceStillImported) {
40
+ collector?.add({
41
+ file: filePath,
42
+ line: null,
43
+ rule: 'deps',
44
+ message: 'react-bulma-components 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 react-bulma-components until you have',
45
+ });
46
+ }
31
47
  // @allxsmith/bestax-bulma comes in (runtime dependency).
32
48
  const dependencies = (next.dependencies ??= {});
33
49
  if (!dependencies['@allxsmith/bestax-bulma'] &&
@@ -11,7 +11,7 @@
11
11
  * TODO. Non-object or dynamic values get a TODO.
12
12
  */
13
13
  import type { ASTPath } from 'jscodeshift';
14
- import { type TransformContext } from './jsx-utils.js';
14
+ import { type TransformContext } from '../_shared/jsx-utils.js';
15
15
  export type ResponsiveKind = 'generic' | 'columns' | 'column';
16
16
  export declare function flattenResponsiveProps(ctx: TransformContext, path: ASTPath<any>, element: any, kind: ResponsiveKind): void;
17
17
  //# sourceMappingURL=responsive.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"responsive.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/responsive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAkB9D,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,cAAc,GACnB,IAAI,CAkHN"}
1
+ {"version":3,"file":"responsive.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/responsive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAkB9D,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,cAAc,GACnB,IAAI,CAkHN"}
@@ -11,7 +11,7 @@
11
11
  * TODO. Non-object or dynamic values get a TODO.
12
12
  */
13
13
  import { RESPONSIVE_BREAKPOINTS } from './mapping.js';
14
- import { addAttr, addTodo, attributesOf, makeAttr, removeAttr, } from './jsx-utils.js';
14
+ import { addAttr, addTodo, attributesOf, makeAttr, removeAttr, } from '../_shared/jsx-utils.js';
15
15
  const TEXT_ALIGN_MAP = {
16
16
  center: 'centered',
17
17
  justify: 'justified',
@@ -8,15 +8,9 @@
8
8
  * element `replaced` when it substituted the node itself.
9
9
  */
10
10
  import type { ASTPath } from 'jscodeshift';
11
- import { type TransformContext } from './jsx-utils.js';
12
- export interface SpecialResult {
13
- /** Override the mapping's `target` for the rename step. */
14
- target?: string;
15
- /** The node was replaced outright — skip rename and prop passes. */
16
- replaced?: boolean;
17
- /** Prop names the handler consumed; skipped by the later prop passes. */
18
- handledProps?: string[];
19
- }
11
+ import { type TransformContext } from '../_shared/jsx-utils.js';
12
+ import { type SpecialResult } from '../_shared/specials-utils.js';
13
+ export type { SpecialResult };
20
14
  export declare function runSpecial(name: string, ctx: TransformContext, path: ASTPath<any>, element: any): SpecialResult;
21
15
  /** Responsive-flattening kinds keyed by mapping special names. */
22
16
  export declare const RESPONSIVE_KINDS: Record<string, 'columns' | 'column'>;
@@ -1 +1 @@
1
- {"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAo3BD,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,GACX,aAAa,CAIf;AAED,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAGjE,CAAC"}
1
+ {"version":3,"file":"specials.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/specials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,aAAa,EAAE,CAAC;AAsvB9B,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,OAAO,EAAE,GAAG,GACX,aAAa,CAIf;AAED,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAGjE,CAAC"}
@@ -8,97 +8,10 @@
8
8
  * element `replaced` when it substituted the node itself.
9
9
  */
10
10
  import { RESPONSIVE_BREAKPOINTS, UNIVERSAL_PROPS } from './mapping.js';
11
- import { addAttr, addTodo, attributesOf, buildJsxName, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, resolveBooleanish, } from './jsx-utils.js';
12
- /** Pick a target based on a literal align-style prop, removing the prop. */
13
- function alignTarget(ctx, path, element, prop, targets, fallback) {
14
- const attr = findAttr(element, prop);
15
- if (!attr)
16
- return { target: fallback };
17
- const literal = literalValueOf(attr);
18
- if (literal.kind === 'string' && targets[literal.value]) {
19
- removeAttr(element, attr);
20
- ctx.dirty = true;
21
- return { target: targets[literal.value] };
22
- }
23
- if (literal.kind === 'string') {
24
- removeAttr(element, attr);
25
- ctx.dirty = true;
26
- return { target: fallback };
27
- }
28
- addTodo(ctx, path, `prop:${prop}`, `\`${prop}\` has a dynamic value; pick between ${Object.values(targets).join(' / ')} by hand`);
29
- removeAttr(element, attr);
30
- ctx.dirty = true;
31
- return { target: fallback };
32
- }
33
- /**
34
- * Filter Bulma modifier props out of an attribute list bound for a plain HTML
35
- * element (they only exist on bestax components), leaving a TODO when any
36
- * were dropped.
37
- */
38
- function stripModifierProps(ctx, path, attrs, where) {
39
- const kept = [];
40
- const dropped = [];
41
- for (const attr of attrs) {
42
- const name = attr?.name?.name;
43
- if (name && (UNIVERSAL_PROPS[name] || name in RESPONSIVE_BREAKPOINTS)) {
44
- dropped.push(name);
45
- }
46
- else {
47
- kept.push(attr);
48
- }
49
- }
50
- if (dropped.length > 0) {
51
- addTodo(ctx, path, 'plain-element', `${where} became a plain element; the Bulma helper prop(s) ${dropped
52
- .map(d => `\`${d}\``)
53
- .join(', ')} were dropped — restyle with classes`);
54
- }
55
- return kept;
56
- }
57
- /**
58
- * Consume the element's `className` so a plain-element rewrite can merge it
59
- * with its hard-coded Bulma class instead of dropping it. A dynamic value
60
- * can't be merged safely — keep the base class and leave a TODO.
61
- */
62
- function mergeClassName(ctx, path, element, base, where) {
63
- const attr = findAttr(element, 'className');
64
- if (!attr)
65
- return base;
66
- const literal = literalValueOf(attr);
67
- removeAttr(element, attr);
68
- if (literal.kind === 'string') {
69
- return base ? `${base} ${literal.value}` : literal.value;
70
- }
71
- addTodo(ctx, path, 'prop:className', base
72
- ? `dynamic ${where} className; merge it with the \`${base}\` class by hand`
73
- : `dynamic ${where} className; re-apply it to the emitted element by hand`);
74
- return base;
75
- }
76
- /** Parse an icon-font <i className="..."> into bestax Icon name/library/variant. */
77
- function parseIconClasses(className) {
78
- const tokens = className.trim().split(/\s+/);
79
- const faVariant = {
80
- fas: 'solid',
81
- far: 'regular',
82
- fab: 'brands',
83
- fal: 'light',
84
- fad: 'duotone',
85
- };
86
- const faStyle = tokens.find(t => faVariant[t]);
87
- const faName = tokens.find(t => /^fa-/.test(t) && !/^fa-(lg|xs|sm|\dx|fw|spin|pulse)$/.test(t));
88
- if (faStyle && faName) {
89
- return {
90
- name: faName.replace(/^fa-/, ''),
91
- library: 'fa',
92
- variant: faVariant[faStyle],
93
- };
94
- }
95
- if (tokens.includes('mdi')) {
96
- const mdiName = tokens.find(t => /^mdi-/.test(t) && t !== 'mdi');
97
- if (mdiName)
98
- return { name: mdiName.replace(/^mdi-/, ''), library: 'mdi' };
99
- }
100
- return null;
101
- }
11
+ import { addAttr, addTodo, attributesOf, buildJsxName, findAttr, literalValueOf, makeAttr, plainElement, removeAttr, resolveBooleanish, } from '../_shared/jsx-utils.js';
12
+ import { alignTarget, makeStripModifierProps, mergeClassName, parseIconClasses, } from '../_shared/specials-utils.js';
13
+ /* eslint-disable @typescript-eslint/no-explicit-any */
14
+ const stripModifierProps = makeStripModifierProps(UNIVERSAL_PROPS, RESPONSIVE_BREAKPOINTS);
102
15
  const SPECIALS = {
103
16
  /**
104
17
  * RBC defaults Columns `multiline` to TRUE (Bulma and bestax default to
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Stylesheet migration for react-bulma-components apps: Bulma 0.9 Sass
3
- * (`@import` + `$var !default` overrides, `_all` aggregator partials) →
4
- * Bulma v1 modules (`@use "bulma/sass" with (…)`), plus the bestax extras.
2
+ * Stylesheet migration for react-bulma-components apps.
5
3
  *
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).
4
+ * All of the work — Bulma 0.9 `@import` → v1 `@use "bulma/sass" with (…)`,
5
+ * `$var !default` folding, `_all` partial mapping, the bestax extras — lives
6
+ * in the shared factory. Only the package's own specifiers differ per source.
7
+ *
8
+ * RBC's root stylesheets are its Sass entry (`src/index`) and the bundled v3
9
+ * CSS (`dist/react-bulma-components(.min).css`); both stand in for a whole
10
+ * Bulma root, so they are replaced rather than dropped.
10
11
  */
11
- import type { StylesTransform } from '../../types.js';
12
- export declare const transformStyles: StylesTransform;
12
+ export declare const transformStyles: import("../../types.js").StylesTransform;
13
13
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,gBAAgB,CAAC;AA+MrE,eAAO,MAAM,eAAe,EAAE,eA2R7B,CAAC"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,eAAO,MAAM,eAAe,0CAQ1B,CAAC"}
@@ -1,392 +1,20 @@
1
1
  /**
2
- * Stylesheet migration for react-bulma-components apps: Bulma 0.9 Sass
3
- * (`@import` + `$var !default` overrides, `_all` aggregator partials) →
4
- * Bulma v1 modules (`@use "bulma/sass" with (…)`), plus the bestax extras.
2
+ * Stylesheet migration for react-bulma-components apps.
5
3
  *
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
- import path from 'node:path';
12
- const TODO = 'TODO(bestax-migrate)';
13
- const GUIDE = 'https://bestax.io/docs/guides/getting-started/migration/react-bulma-components';
14
- const EXTRAS_USE = "@use '@allxsmith/bestax-bulma/scss/extras';";
15
- /** Bulma v1 sass tree — directories that replaced the 0.9 `_all` partials. */
16
- const V1_DIRS = new Set([
17
- 'utilities',
18
- 'base',
19
- 'elements',
20
- 'form',
21
- 'components',
22
- 'grid',
23
- 'layout',
24
- 'helpers',
25
- 'themes',
26
- ]);
27
- /** Known Bulma v1 leaf partials (path after `bulma/sass/`). */
28
- const V1_LEAVES = new Set([
29
- 'utilities/initial-variables',
30
- 'utilities/derived-variables',
31
- 'utilities/css-variables',
32
- 'utilities/mixins',
33
- 'utilities/functions',
34
- 'utilities/controls',
35
- 'utilities/extends',
36
- 'themes/light',
37
- 'themes/dark',
38
- 'themes/setup',
39
- ]);
40
- /**
41
- * The 0.9 root imports that become `@use "bulma/sass"`. The specifier may be
42
- * bare (`bulma/bulma`), tilde-prefixed (webpack), or a relative path into
43
- * node_modules (`../../node_modules/bulma/bulma`, common under Parcel) —
44
- * relative prefixes are preserved in the emitted `@use`.
45
- */
46
- const ROOT_IMPORT = /^(\s*)@import\s+(['"])((?:\.\.?\/)+node_modules\/|~)?bulma\/(?:bulma(?:\.sass|\.scss)?|css\/bulma(?:\.min)?\.css)\2\s*;?\s*$/;
47
- /** 0.9 partial imports under bulma/sass/… (same prefix forms as the root). */
48
- const PARTIAL_IMPORT = /^(\s*)@import\s+(['"])((?:\.\.?\/)+node_modules\/|~)?bulma\/sass\/([\w/-]+)\2\s*;?\s*$/;
49
- /** A preserved path prefix for the rewritten `@use` (tilde never survives). */
50
- function keptPrefix(prefix) {
51
- return prefix && prefix !== '~' ? prefix : '';
52
- }
53
- /** Any other line that still `@import`s something bulma-ish. */
54
- const OTHER_BULMA_IMPORT = /^\s*@import\s+['"][^'"]*bulma[^'"]*['"]/;
55
- /**
56
- * Third-party Bulma extension packages (`bulma-checkradio`, `bulma-switch`,
57
- * …) — 0.9-era add-ons whose v1 compatibility varies; several are covered
58
- * by the bestax extras. The `bulma-` package name must start at a specifier
59
- * segment boundary (right after the quote/`~`, or after a `/`) so this does
60
- * not also match a name that merely *contains* `bulma-`, like
61
- * `react-bulma-components` (see EXTENSION_IMPORT's own regression test).
62
- */
63
- const EXTENSION_IMPORT = /^\s*@import\s+['"](?:~|[^'"]*\/)?bulma-([\w-]+?)(?:\/|\.|['"])/;
64
- /**
65
- * The source library's own stylesheet. This is not a third-party extension:
66
- * it is the library being migrated away from, and `deps.ts` removes its
67
- * package.json entry in the same run, so leaving the import in place breaks
68
- * the Sass build. Any `react-bulma-components/…` specifier is dead the same
69
- * way — the documented v3 setup paths (the bundled CSS, the "advanced" Sass
70
- * entry point), but also deep partials and extensionless forms — so, like
71
- * `transform.ts`, we match on the package prefix rather than enumerating
72
- * shapes. The prefix may be bare, `~`-prefixed, or a relative node_modules
73
- * path (the Parcel form `ROOT_IMPORT` also supports).
74
- */
75
- const RBC_STYLE_IMPORT = /^(\s*)@import\s+(['"])((?:\.\.?\/)+node_modules\/|~)?react-bulma-components\/[^'"]*\2\s*;?\s*$/;
76
- /**
77
- * The subset of RBC stylesheet imports that are the library's own *root*: the
78
- * bare package specifier, the documented Sass entry point (`src/index`), and
79
- * the bundled v3 CSS (`dist/react-bulma-components(.min).css`). These pull in
80
- * the whole library, which `bulma/sass` supersedes — so when the file already
81
- * has a Bulma root, dropping one of these loses nothing. A deep RBC partial
82
- * (`src/components/navbar.sass`) is NOT a root: `bulma/sass` doesn't carry a
83
- * given partial's styles, so removing one silently would drop CSS. We flag
84
- * those instead (the package's "never a silent skip" rule).
85
- */
86
- const RBC_ROOT_STYLESHEET = /^\s*@import\s+(['"])(?:(?:\.\.?\/)+node_modules\/|~)?react-bulma-components(?:\/src\/index(?:\.s[ac]ss)?|\/dist\/react-bulma-components(?:\.min)?\.css)?\1\s*;?\s*$/;
87
- /**
88
- * A Bulma module root already pulled in via `@use` — the file's own
89
- * `@use 'bulma/sass'` (any prefix, configured or not) or the bestax bundle
90
- * (`scss/bestax`, which itself loads `bulma/sass with (…)`). When one is
91
- * present we must not emit a second root: a duplicate `bulma/sass` namespace,
92
- * or reconfiguring an already-loaded module, is a hard Sass error.
93
- */
94
- const USE_BULMA_ROOT = /^\s*@use\s+(['"])(?:~|(?:\.\.?\/)+node_modules\/)?(?:bulma\/sass|@allxsmith\/bestax-bulma\/(?:src\/)?scss\/bestax)\1/;
95
- /** Any `$name: value;` declaration (with or without `!default`). */
96
- const VAR_DECL = /^\s*\$([\w-]+)\s*:\s*(.+?)\s*(!default)?\s*;\s*$/;
97
- /**
98
- * Strips Sass block comments (slash-star … star-slash spans) from a value
99
- * before it is analyzed for foldability or top-level commas. A comment can
100
- * carry an unbalanced quote (an apostrophe in `user's`) or a stray `(`/`$`
101
- * that would otherwise fool the character scanners into mis-reading the
102
- * value's structure; the comment is inert to Sass, so removing it for
103
- * analysis is safe. The emitted value still carries the comment verbatim.
4
+ * All of the work — Bulma 0.9 `@import` → v1 `@use "bulma/sass" with (…)`,
5
+ * `$var !default` folding, `_all` partial mapping, the bestax extras — lives
6
+ * in the shared factory. Only the package's own specifiers differ per source.
104
7
  *
105
- * The scan tracks quote state first and only recognizes a block comment
106
- * outside a string, in one pass — a bare regex would treat a slash-star …
107
- * star-slash-shaped span that straddles two quoted strings (opening marker in
108
- * one string, closing marker in the next) as a comment and delete the real
109
- * top-level comma between them, so a genuine list would fold unparenthesized.
110
- * An unterminated comment consumes the rest of the value.
111
- */
112
- function stripSassComments(value) {
113
- let result = '';
114
- let quote = null;
115
- let escaped = false;
116
- for (let i = 0; i < value.length; i += 1) {
117
- const char = value[i];
118
- if (quote) {
119
- result += char;
120
- if (escaped) {
121
- escaped = false;
122
- }
123
- else if (char === '\\') {
124
- escaped = true;
125
- }
126
- else if (char === quote) {
127
- quote = null;
128
- }
129
- continue;
130
- }
131
- if (char === '/' && value[i + 1] === '*') {
132
- const end = value.indexOf('*/', i + 2);
133
- if (end === -1)
134
- break; // unterminated comment — drop the rest
135
- i = end + 1; // resume after the closing */
136
- continue;
137
- }
138
- if (char === '"' || char === "'")
139
- quote = char;
140
- result += char;
141
- }
142
- return result;
143
- }
144
- /**
145
- * A value is fold-safe for `with (…)` when it is a plain literal — no
146
- * function calls, interpolation, variable references, or at-rules. Hex
147
- * colors (`#ff6b35`) are fine; `#{…}` interpolation is not. Block comments are
148
- * ignored for this test — their contents don't make the value non-literal.
149
- */
150
- function isFoldableValue(value) {
151
- return !/[()$@]|#\{/.test(stripSassComments(value));
152
- }
153
- /**
154
- * Whether `value` has a comma outside any quoted string — a bare Sass list
155
- * (`'Nunito', sans-serif`) that must be parenthesized before it can sit
156
- * inside `with (…)`, or Dart Sass reads the comma as an argument separator
157
- * instead of a list delimiter. Block comments are stripped first so a quote or
158
- * comma inside one (an apostrophe in `user's`) can't skew the scan.
159
- */
160
- function hasTopLevelComma(value) {
161
- let quote = null;
162
- let escaped = false;
163
- for (const char of stripSassComments(value)) {
164
- if (escaped) {
165
- escaped = false;
166
- continue;
167
- }
168
- if (char === '\\') {
169
- escaped = true;
170
- continue;
171
- }
172
- if (quote) {
173
- if (char === quote)
174
- quote = null;
175
- continue;
176
- }
177
- if (char === '"' || char === "'") {
178
- quote = char;
179
- }
180
- else if (char === ',') {
181
- return true;
182
- }
183
- }
184
- return false;
185
- }
186
- /** Formats a folded value for the `with (…)` clause, parenthesizing a bare list. */
187
- function formatFoldedValue(value) {
188
- return hasTopLevelComma(value) ? `(${value})` : value;
189
- }
190
- function report(collector, file, line, rule, message) {
191
- collector?.add({ file, line, rule, message });
192
- }
193
- export const transformStyles = (filePath, source, collector, options) => {
194
- const cssMode = options.cssMode ?? 'bestax';
195
- if (!/bulma/.test(source))
196
- return null;
197
- // Indented-syntax files: flag only — rewriting without a parser is unsafe.
198
- if (path.extname(filePath) === '.sass') {
199
- if (source.includes(`${TODO}`))
200
- return null;
201
- report(collector, filePath, 1, 'sass', `indented-syntax file references Bulma; convert its @import lines to @use "bulma/sass" by hand — see ${GUIDE}`);
202
- return `// ${TODO}: convert Bulma 0.9 @import lines to @use "bulma/sass" with (…) — see ${GUIDE}\n${source}`;
203
- }
204
- const lines = source.split('\n');
205
- const out = [];
206
- let changed = false;
207
- let extrasAdded = source.includes('@allxsmith/bestax-bulma/scss');
208
- // Pass 1: locate the file's Bulma root and the safe leading variable
209
- // overrides that fold into it. A real `bulma/…` @import root wins; otherwise
210
- // the RBC stylesheet, which we rewrite into a root the same way — so a file
211
- // that starts from RBC and one that starts from a bulma @import converge on
212
- // the identical shape. A pre-existing `@use` root means we create no new one.
213
- const rootImportIndex = lines.findIndex(line => ROOT_IMPORT.test(line));
214
- const rbcRootIndex = lines.findIndex(line => RBC_STYLE_IMPORT.test(line) && !line.includes(TODO));
215
- const hasUseBulmaRoot = lines.some(line => USE_BULMA_ROOT.test(line));
216
- const foldTargetIndex = hasUseBulmaRoot
217
- ? -1
218
- : rootImportIndex !== -1
219
- ? rootImportIndex
220
- : rbcRootIndex;
221
- const foldableVars = [];
222
- const unsafeVarLines = [];
223
- if (foldTargetIndex !== -1) {
224
- for (let i = 0; i < foldTargetIndex; i += 1) {
225
- const line = lines[i];
226
- const match = line.match(VAR_DECL);
227
- if (match && isFoldableValue(match[2])) {
228
- foldableVars.push({ index: i, name: match[1], value: match[2].trim() });
229
- }
230
- else if (/^\s*\$[\w-]+\s*:/.test(line)) {
231
- unsafeVarLines.push(i);
232
- }
233
- }
234
- }
235
- const folded = new Set(foldableVars.map(v => v.index));
236
- /**
237
- * Emit a Bulma module root at `indent` (folding the leading var overrides
238
- * gathered above into `with (…)`, flagging any unsafe ones), then, in
239
- * bestax mode, the extras. `prefix` is a preserved relative-path prefix
240
- * (raw-file toolchains resolve paths, not package specifiers) or ''.
241
- */
242
- const emitBulmaRoot = (indent, prefix, lineNo) => {
243
- const bulmaSass = `${prefix}bulma/sass`;
244
- if (foldableVars.length > 0) {
245
- out.push(`${indent}@use '${bulmaSass}' with (`);
246
- foldableVars.forEach(({ name, value }, index) => {
247
- const comma = index < foldableVars.length - 1 ? ',' : '';
248
- out.push(`${indent} $${name}: ${formatFoldedValue(value)}${comma}`);
249
- });
250
- out.push(`${indent});`);
251
- }
252
- else {
253
- out.push(`${indent}@use '${bulmaSass}';`);
254
- }
255
- if (unsafeVarLines.length > 0) {
256
- out.push(`${indent}// ${TODO}: the variable override(s) above use computed values; move them into the @use "bulma/sass" with (…) configuration by hand`);
257
- report(collector, filePath, lineNo, 'sass', 'variable overrides with computed values could not be folded into `with (…)`');
258
- }
259
- if (cssMode === 'bestax' && !extrasAdded) {
260
- out.push(prefix
261
- ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
262
- : `${indent}${EXTRAS_USE}`);
263
- extrasAdded = true;
264
- }
265
- };
266
- for (let i = 0; i < lines.length; i += 1) {
267
- const line = lines[i];
268
- if (i === rootImportIndex) {
269
- const rootMatch = line.match(ROOT_IMPORT);
270
- // A path-prefixed root import means the toolchain resolves raw file
271
- // paths, not package specifiers — point at the shipped files directly.
272
- emitBulmaRoot(rootMatch?.[1] ?? '', keptPrefix(rootMatch?.[3]), i + 1);
273
- changed = true;
274
- continue;
275
- }
276
- if (folded.has(i)) {
277
- changed = true; // folded into with(...) above
278
- continue;
279
- }
280
- const partial = line.match(PARTIAL_IMPORT);
281
- if (partial) {
282
- const [, indent, , rawPrefix, importPath] = partial;
283
- const prefix = keptPrefix(rawPrefix);
284
- // Partial-only files (modular builds with no root import) still need
285
- // the extras for the bestax components' styling; the extras tree only
286
- // reads Bulma utilities, never configures them, so this is load-safe.
287
- const pushExtras = () => {
288
- if (cssMode !== 'bestax' || extrasAdded || rootImportIndex !== -1) {
289
- return;
290
- }
291
- out.push(prefix
292
- ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
293
- : `${indent}${EXTRAS_USE}`);
294
- extrasAdded = true;
295
- };
296
- const segments = importPath.split('/');
297
- const last = segments[segments.length - 1].replace(/^_/, '');
298
- const dir = segments[0];
299
- if ((last === 'all' || last === 'index') && V1_DIRS.has(dir)) {
300
- out.push(`${indent}@use '${prefix}bulma/sass/${dir}';`);
301
- pushExtras();
302
- changed = true;
303
- continue;
304
- }
305
- const leaf = segments.map(s => s.replace(/^_/, '')).join('/');
306
- if (V1_DIRS.has(leaf) || V1_LEAVES.has(leaf)) {
307
- out.push(`${indent}@use '${prefix}bulma/sass/${leaf}';`);
308
- pushExtras();
309
- changed = true;
310
- continue;
311
- }
312
- out.push(`${indent}// ${TODO}: this Bulma 0.9 partial path no longer exists in Bulma v1; find its replacement under bulma/sass/ — see ${GUIDE}`);
313
- out.push(line);
314
- report(collector, filePath, i + 1, 'sass', `Bulma 0.9 partial path \`bulma/sass/${importPath}\` has no direct v1 equivalent`);
315
- changed = true;
316
- continue;
317
- }
318
- const rbcStyle = line.match(RBC_STYLE_IMPORT);
319
- if (rbcStyle && !line.includes(TODO)) {
320
- const indent = rbcStyle[1];
321
- const prefix = keptPrefix(rbcStyle[3]);
322
- if (i === rbcRootIndex && rootImportIndex === -1 && !hasUseBulmaRoot) {
323
- // The library's own stylesheet never resolves post-migration (deps.ts
324
- // removes react-bulma-components in the same run), and it is this
325
- // file's only Bulma root, so rewrite it into one — the same shape the
326
- // @import root path emits, folding any leading var overrides. Emitting
327
- // a real `bulma/sass` (+ extras) rather than the hard-configured
328
- // bestax bundle keeps this convergent with the @import path and avoids
329
- // reconfiguring `bulma/sass` if a themed root exists elsewhere.
330
- emitBulmaRoot(indent, prefix, i + 1);
331
- if (cssMode === 'keep') {
332
- // The replacement is unavoidable even in keep mode: RBC's own
333
- // stylesheet is a v3 (Bulma 0.9) asset the migrated v1 components
334
- // can't use. What changed depends on whether the manifest step ran.
335
- const depsRan = options.deps !== false;
336
- const detail = depsRan
337
- ? 'its package.json entry is removed, so the old import no longer resolves; install bulma@^1'
338
- : 'it targets Bulma 0.9, not the v1 your components now use; --no-deps kept the dependency, so install bulma@^1 alongside it';
339
- out.push(`${indent}// ${TODO}: replaced react-bulma-components's own stylesheet with @use 'bulma/sass' — ${detail} — see ${GUIDE}`);
340
- report(collector, filePath, i + 1, 'sass', depsRan
341
- ? "replaced react-bulma-components's stylesheet import with bulma/sass; the package is removed from dependencies, so the old import would not resolve"
342
- : "replaced react-bulma-components'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)");
343
- }
344
- else {
345
- // bestax/bulma modes rewrite the file's root without a TODO — still
346
- // report it so the migration summary reflects the restructured root.
347
- report(collector, filePath, i + 1, 'sass', "replaced react-bulma-components's stylesheet import with a Bulma v1 @use root");
348
- }
349
- }
350
- else {
351
- // A Bulma root already covers this file (a `bulma/…` @import we
352
- // convert, the file's own `@use` root, or an earlier RBC line we
353
- // rewrote). In bestax mode keep that root and add only the extras —
354
- // never a second root, never the configured bundle that would
355
- // reconfigure an already-loaded module.
356
- if (cssMode === 'bestax' && !extrasAdded) {
357
- out.push(prefix
358
- ? `${indent}@use '${prefix}@allxsmith/bestax-bulma/src/scss/extras';`
359
- : `${indent}${EXTRAS_USE}`);
360
- extrasAdded = true;
361
- }
362
- // Dropping the RBC line is only safe for its root/index stylesheet,
363
- // which `bulma/sass` supersedes. A deep RBC partial can carry styles
364
- // `bulma/sass` doesn't, so removing it silently would lose CSS — flag
365
- // it with a TODO + report entry instead (never a silent skip).
366
- if (!RBC_ROOT_STYLESHEET.test(line)) {
367
- out.push(`${indent}// ${TODO}: dropped a react-bulma-components stylesheet partial that has no Bulma v1 root equivalent; port any styles it carried beyond Bulma's own by hand — see ${GUIDE}`);
368
- report(collector, filePath, i + 1, 'sass', 'react-bulma-components stylesheet partial dropped; port any styles it carried beyond Bulma’s own by hand');
369
- }
370
- }
371
- changed = true;
372
- continue;
373
- }
374
- if (OTHER_BULMA_IMPORT.test(line) && !line.includes(TODO)) {
375
- const extension = line.match(EXTENSION_IMPORT);
376
- if (extension) {
377
- 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}`);
378
- out.push(line);
379
- 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`);
380
- changed = true;
381
- continue;
382
- }
383
- out.push(`// ${TODO}: Bulma v1 uses @use instead of @import — see ${GUIDE}`);
384
- out.push(line);
385
- report(collector, filePath, i + 1, 'sass', 'unrecognized Bulma @import left in place; convert to @use by hand');
386
- changed = true;
387
- continue;
388
- }
389
- out.push(line);
390
- }
391
- return changed ? out.join('\n') : null;
392
- };
8
+ * RBC's root stylesheets are its Sass entry (`src/index`) and the bundled v3
9
+ * CSS (`dist/react-bulma-components(.min).css`); both stand in for a whole
10
+ * Bulma root, so they are replaced rather than dropped.
11
+ */
12
+ import { makeStylesTransform } from '../_shared/make-styles-transform.js';
13
+ export const transformStyles = makeStylesTransform({
14
+ packageName: 'react-bulma-components',
15
+ guideUrl: 'https://bestax.io/docs/guides/getting-started/migration/react-bulma-components',
16
+ rootStylesheetSuffixes: [
17
+ '/src/index(?:\\.s[ac]ss)?',
18
+ '/dist/react-bulma-components(?:\\.min)?\\.css',
19
+ ],
20
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAoCvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CAofpB"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/sources/react-bulma-components/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA2CvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,GAAG,SAAS,CAyzBpB"}