@staffbase/design 19.1.0 → 20.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codemods/v17-migrate-spacing/README.md +72 -0
- package/codemods/v17-migrate-spacing/transform.js +152 -0
- package/codemods/v20-migrate-avatar/README.md +71 -0
- package/codemods/v20-migrate-avatar/transform.js +191 -0
- package/dist/chunk-CDHmpCQf.cjs +40 -0
- package/dist/components.css +1 -1
- package/dist/illustrations.cjs +541 -0
- package/dist/illustrations.cjs.map +1 -0
- package/dist/illustrations.css +52 -0
- package/dist/illustrations.d.ts +50 -0
- package/dist/illustrations.js +521 -0
- package/dist/illustrations.js.map +1 -0
- package/dist/main.cjs +161 -59
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.ts +87 -20
- package/dist/main.js +154 -34
- package/dist/main.js.map +1 -1
- package/package.json +8 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# v17 — Migrate Tailwind spacing tokens
|
|
2
|
+
|
|
3
|
+
This codemod automatically migrates spacing tokens to the standard Tailwind v4 scale (`1 = 0.25rem`) introduced as a breaking change in `@staffbase/design` v17.
|
|
4
|
+
|
|
5
|
+
The previous custom spacing scale used `1 = 1px`. Dividing all integer spacing tokens by 4 maps each value to the same physical size in the new scale.
|
|
6
|
+
|
|
7
|
+
## What it transforms
|
|
8
|
+
|
|
9
|
+
Affected utilities: **margins**, **padding**, **gap**, **left**, **right**, **bottom**, **top** (and their axis/side variants, negative variants, and responsive/state prefixes).
|
|
10
|
+
|
|
11
|
+
**Not** affected: `space` (`space-x`, `space-y`), `width` (`w-`), `height` (`h-`), `size`.
|
|
12
|
+
|
|
13
|
+
### Common examples
|
|
14
|
+
|
|
15
|
+
| Before | After | Value |
|
|
16
|
+
| ------------ | ----------- | ----- |
|
|
17
|
+
| `p-4` | `p-1` | 4px |
|
|
18
|
+
| `p-8` | `p-2` | 8px |
|
|
19
|
+
| `p-16` | `p-4` | 16px |
|
|
20
|
+
| `p-24` | `p-6` | 24px |
|
|
21
|
+
| `p-2` | `p-0.5` | 2px |
|
|
22
|
+
| `p-6` | `p-1.5` | 6px |
|
|
23
|
+
| `m-32` | `m-8` | 32px |
|
|
24
|
+
| `mx-16` | `mx-4` | 16px |
|
|
25
|
+
| `pt-8` | `pt-2` | 8px |
|
|
26
|
+
| `gap-16` | `gap-4` | 16px |
|
|
27
|
+
| `gap-x-8` | `gap-x-2` | 8px |
|
|
28
|
+
| `left-24` | `left-6` | 24px |
|
|
29
|
+
| `inset-x-16` | `inset-x-4` | 16px |
|
|
30
|
+
| `-mt-8` | `-mt-2` | 8px |
|
|
31
|
+
|
|
32
|
+
### Responsive and state variants are handled
|
|
33
|
+
|
|
34
|
+
| Before | After |
|
|
35
|
+
| ------------- | ------------- |
|
|
36
|
+
| `sm:p-16` | `sm:p-4` |
|
|
37
|
+
| `hover:-mt-4` | `hover:-mt-1` |
|
|
38
|
+
| `lg:gap-8` | `lg:gap-2` |
|
|
39
|
+
|
|
40
|
+
### Works in string literals and template literals
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// className string
|
|
44
|
+
<div className="mt-8 gap-4 p-16" />;
|
|
45
|
+
// → <div className="p-4 mt-2 gap-1" />
|
|
46
|
+
|
|
47
|
+
// clsx / cn call
|
|
48
|
+
clsx('p-16', isActive && 'mt-8')
|
|
49
|
+
// → clsx('p-4', isActive && 'mt-2')
|
|
50
|
+
|
|
51
|
+
// template literal (static segments only)
|
|
52
|
+
`flex p-16 ${dynamic}`;
|
|
53
|
+
// → `flex p-4 ${dynamic}`
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Limitations
|
|
57
|
+
|
|
58
|
+
- **Dynamic values** — template literal expressions (e.g. `` `p-${size}` ``) cannot be statically transformed; these will remain unchanged and will surface as visual regressions to fix manually.
|
|
59
|
+
- **Decimal values** — classes that already use a decimal token (e.g. `p-0.5`) are intentionally left untouched, so the codemod is safe to run incrementally on partially-migrated code.
|
|
60
|
+
- **CSS files** — this codemod only processes JS/TS/JSX/TSX files. Tailwind utility classes in `.css` files must be migrated manually.
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx jscodeshift@latest \
|
|
66
|
+
--transform node_modules/@staffbase/design/codemods/v17-migrate-spacing/transform.js \
|
|
67
|
+
--extensions tsx,ts,jsx,js \
|
|
68
|
+
--parser tsx \
|
|
69
|
+
src/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Add `--dry` to preview changes without writing files, and `--print` to print changed source to stdout.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jscodeshift codemod: Migrate v17 Tailwind spacing tokens.
|
|
3
|
+
*
|
|
4
|
+
* In v17, the design system switched to Tailwind v4's standard spacing scale
|
|
5
|
+
* where 1 = 0.25rem. The previous custom scale used 1 = 1px, so all integer
|
|
6
|
+
* spacing tokens for the affected utilities must be divided by 4.
|
|
7
|
+
*
|
|
8
|
+
* Affected utilities:
|
|
9
|
+
* margins m, mx, my, mt, mb, ml, mr, ms, me (and negative variants)
|
|
10
|
+
* padding p, px, py, pt, pb, pl, pr, ps, pe
|
|
11
|
+
* gap gap, gap-x, gap-y
|
|
12
|
+
* positional left, right, bottom, top, inset, inset-x, inset-y, inset-s, inset-e
|
|
13
|
+
* (and negative variants)
|
|
14
|
+
*
|
|
15
|
+
* NOT affected: space (space-x, space-y), width (w), height (h), size.
|
|
16
|
+
*
|
|
17
|
+
* Examples:
|
|
18
|
+
* p-4 → p-1 (4 / 4 = 1)
|
|
19
|
+
* m-16 → m-4 (16 / 4 = 4)
|
|
20
|
+
* pt-2 → pt-0.5 (2 / 4 = 0.5)
|
|
21
|
+
* gap-8 → gap-2 (8 / 4 = 2)
|
|
22
|
+
* gap-x-24 → gap-x-6 (24 / 4 = 6)
|
|
23
|
+
* -mt-8 → -mt-2 (8 / 4 = 2)
|
|
24
|
+
* left-32 → left-8 (32 / 4 = 8)
|
|
25
|
+
* inset-x-16 → inset-x-4 (16 / 4 = 4)
|
|
26
|
+
* sm:p-16 → sm:p-4 (16 / 4 = 4)
|
|
27
|
+
* hover:-mt-4 → hover:-mt-1 (4 / 4 = 1)
|
|
28
|
+
*
|
|
29
|
+
* Only integer token values are transformed. Decimal values (e.g. p-0.5) are
|
|
30
|
+
* left untouched on the assumption they are already in v4 format, making the
|
|
31
|
+
* codemod safe to run incrementally.
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
* npx jscodeshift@latest \
|
|
35
|
+
* --transform node_modules/@staffbase/design/codemods/v17-migrate-spacing/transform.js \
|
|
36
|
+
* --extensions tsx,ts,jsx,js \
|
|
37
|
+
* --parser tsx \
|
|
38
|
+
* src/
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// ── Spacing prefix list ───────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
// Sorted longest-first so alternation resolves correctly in the regex
|
|
44
|
+
// (e.g. gap-x is tried before gap; px before p; inset-x before inset).
|
|
45
|
+
const SPACING_PREFIXES = [
|
|
46
|
+
// Inset positional (longest first)
|
|
47
|
+
'inset-x',
|
|
48
|
+
'inset-y',
|
|
49
|
+
'inset-s',
|
|
50
|
+
'inset-e',
|
|
51
|
+
'inset',
|
|
52
|
+
// Gap
|
|
53
|
+
'gap-x',
|
|
54
|
+
'gap-y',
|
|
55
|
+
'gap',
|
|
56
|
+
// Margin variants (before bare m)
|
|
57
|
+
'mx',
|
|
58
|
+
'my',
|
|
59
|
+
'mt',
|
|
60
|
+
'mb',
|
|
61
|
+
'ml',
|
|
62
|
+
'mr',
|
|
63
|
+
'ms',
|
|
64
|
+
'me',
|
|
65
|
+
// Padding variants (before bare p)
|
|
66
|
+
'px',
|
|
67
|
+
'py',
|
|
68
|
+
'pt',
|
|
69
|
+
'pb',
|
|
70
|
+
'pl',
|
|
71
|
+
'pr',
|
|
72
|
+
'ps',
|
|
73
|
+
'pe',
|
|
74
|
+
// Bare margin and padding
|
|
75
|
+
'm',
|
|
76
|
+
'p',
|
|
77
|
+
// Positional
|
|
78
|
+
'left',
|
|
79
|
+
'right',
|
|
80
|
+
'bottom',
|
|
81
|
+
'top',
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const prefixAlt = SPACING_PREFIXES.join('|');
|
|
85
|
+
|
|
86
|
+
// Matches spacing utilities with integer values.
|
|
87
|
+
//
|
|
88
|
+
// (?<![a-zA-Z\d_-])
|
|
89
|
+
// Negative lookbehind: prevents matching inside compound names like
|
|
90
|
+
// "custom-p-4" (letter before) or "my-top-4" (dash before "top").
|
|
91
|
+
// Allows matches after a space, colon (variant prefix like sm:), or
|
|
92
|
+
// quote/start-of-string.
|
|
93
|
+
//
|
|
94
|
+
// (-?)
|
|
95
|
+
// Optional leading dash for negative utilities (-mt-4, -left-8, etc.).
|
|
96
|
+
//
|
|
97
|
+
// (prefix)
|
|
98
|
+
// The spacing utility prefix captured for reconstruction.
|
|
99
|
+
//
|
|
100
|
+
// -(\d+)
|
|
101
|
+
// Separator dash followed by an integer token value.
|
|
102
|
+
// Decimal values (e.g. p-0.5) are intentionally excluded so already-migrated
|
|
103
|
+
// code is not double-transformed.
|
|
104
|
+
//
|
|
105
|
+
// (?!\w)
|
|
106
|
+
// Ensures we do not partially match a longer token (e.g. p-4 inside p-40
|
|
107
|
+
// is avoided because \d+ greedily consumes all digits, and (?!\w) then
|
|
108
|
+
// confirms no more word characters follow).
|
|
109
|
+
const SPACING_REGEX = new RegExp(`(?<![a-zA-Z\\d_-])(-?)(${prefixAlt})-(\\d+)(?!\\w)`, 'g');
|
|
110
|
+
|
|
111
|
+
// ── Transformation helper ─────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
function divideByFour(str) {
|
|
114
|
+
return str.replace(SPACING_REGEX, (_match, neg, prefix, num) => {
|
|
115
|
+
const newVal = parseInt(num, 10) / 4;
|
|
116
|
+
// Number.isInteger distinguishes 2.0 (integer) from 0.5 (not).
|
|
117
|
+
// String() gives clean output: 2 → "2", 0.5 → "0.5", 1.5 → "1.5".
|
|
118
|
+
return `${neg}${prefix}-${String(newVal)}`;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
export default function transform(file, api) {
|
|
125
|
+
const j = api.jscodeshift;
|
|
126
|
+
const root = j(file.source);
|
|
127
|
+
let dirty = false;
|
|
128
|
+
|
|
129
|
+
// Replace within all string literals (className props, clsx/cn args, constants, etc.).
|
|
130
|
+
root.find(j.StringLiteral).forEach((path) => {
|
|
131
|
+
const next = divideByFour(path.node.value);
|
|
132
|
+
if (next !== path.node.value) {
|
|
133
|
+
path.node.value = next;
|
|
134
|
+
dirty = true;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Replace within the static parts of template literals.
|
|
139
|
+
root.find(j.TemplateElement).forEach((path) => {
|
|
140
|
+
const {raw, cooked} = path.node.value;
|
|
141
|
+
const nextRaw = divideByFour(raw);
|
|
142
|
+
if (nextRaw !== raw) {
|
|
143
|
+
path.node.value = {
|
|
144
|
+
raw: nextRaw,
|
|
145
|
+
cooked: cooked != null ? divideByFour(cooked) : cooked,
|
|
146
|
+
};
|
|
147
|
+
dirty = true;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return dirty ? root.toSource({quote: 'single'}) : undefined;
|
|
152
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# v20 — Breaking-change migrations
|
|
2
|
+
|
|
3
|
+
This codemod handles all breaking changes introduced in v20 of `@staffbase/design`.
|
|
4
|
+
|
|
5
|
+
## What it transforms
|
|
6
|
+
|
|
7
|
+
### Avatar → AvatarDeprecated
|
|
8
|
+
|
|
9
|
+
`Avatar` is now a compound component. The old single-element Avatar has been renamed to `AvatarDeprecated`.
|
|
10
|
+
|
|
11
|
+
| Before (v19) | After (v20) |
|
|
12
|
+
| ------------------------------------------------- | ----------------------------------------------------------- |
|
|
13
|
+
| `import { Avatar } from '@staffbase/design'` | `import { AvatarDeprecated } from '@staffbase/design'` |
|
|
14
|
+
| `import { AvatarProps } from '@staffbase/design'` | `import { AvatarDeprecatedProps } from '@staffbase/design'` |
|
|
15
|
+
| `<Avatar … />` | `<AvatarDeprecated … />` |
|
|
16
|
+
| `const props: AvatarProps` | `const props: AvatarDeprecatedProps` |
|
|
17
|
+
|
|
18
|
+
### Banner.CloseButton → Banner.Close
|
|
19
|
+
|
|
20
|
+
`Banner.CloseButton` has been renamed to `Banner.Close`.
|
|
21
|
+
|
|
22
|
+
| Before (v19) | After (v20) |
|
|
23
|
+
| -------------------------- | -------------------- |
|
|
24
|
+
| `<Banner.CloseButton … />` | `<Banner.Close … />` |
|
|
25
|
+
|
|
26
|
+
Aliased imports are handled — only the imported binding is renamed, the local alias is preserved:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// Before
|
|
30
|
+
import { Avatar as UserAvatar } from '@staffbase/design';
|
|
31
|
+
<UserAvatar … />
|
|
32
|
+
|
|
33
|
+
// After (local alias unchanged)
|
|
34
|
+
import { AvatarDeprecated as UserAvatar } from '@staffbase/design';
|
|
35
|
+
<UserAvatar … />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Step 1 — Install jscodeshift
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install --save-dev jscodeshift
|
|
44
|
+
# or
|
|
45
|
+
pnpm add -D jscodeshift
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 2 — Run the codemod
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx jscodeshift@latest \
|
|
52
|
+
--transform node_modules/@staffbase/design/codemods/v20-migrate-avatar/transform.js \
|
|
53
|
+
--extensions tsx,ts \
|
|
54
|
+
--parser tsx \
|
|
55
|
+
src/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step 3 — Migrate to the new Avatar (optional but recommended)
|
|
59
|
+
|
|
60
|
+
After running this codemod your code compiles again, but `AvatarDeprecated` will be removed in a future release. Migrate to the new compound component when you're ready:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// Before (AvatarDeprecated)
|
|
64
|
+
<AvatarDeprecated src="…" alt="Jane Doe" size="40" />
|
|
65
|
+
|
|
66
|
+
// After (new Avatar)
|
|
67
|
+
<Avatar.Root size={40}>
|
|
68
|
+
<Avatar.Image src="…" alt="Jane Doe" />
|
|
69
|
+
<Avatar.Fallback name="Jane Doe">JD</Avatar.Fallback>
|
|
70
|
+
</Avatar.Root>
|
|
71
|
+
```
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jscodeshift codemod: v20 breaking-change migrations.
|
|
3
|
+
*
|
|
4
|
+
* 1. Rename old Avatar to AvatarDeprecated:
|
|
5
|
+
* import { Avatar } from '@staffbase/design'
|
|
6
|
+
* → import { AvatarDeprecated } from '@staffbase/design'
|
|
7
|
+
*
|
|
8
|
+
* import { AvatarProps } from '@staffbase/design'
|
|
9
|
+
* → import { AvatarDeprecatedProps } from '@staffbase/design'
|
|
10
|
+
*
|
|
11
|
+
* <Avatar ... /> → <AvatarDeprecated ... />
|
|
12
|
+
*
|
|
13
|
+
* const props: AvatarProps → const props: AvatarDeprecatedProps
|
|
14
|
+
*
|
|
15
|
+
* 2. Rename Banner.CloseButton to Banner.Close:
|
|
16
|
+
* <Banner.CloseButton ... /> → <Banner.Close ... />
|
|
17
|
+
*
|
|
18
|
+
* 3. Remove CloseButtonProps import and replace type usages:
|
|
19
|
+
* import { CloseButtonProps } from '@staffbase/design'
|
|
20
|
+
* → (removed; use ComponentProps<typeof Banner.Close> from 'react' instead)
|
|
21
|
+
*
|
|
22
|
+
* const props: CloseButtonProps → const props: ComponentProps<typeof Banner.Close>
|
|
23
|
+
*
|
|
24
|
+
* If Avatar/AvatarProps was aliased locally the local name is preserved:
|
|
25
|
+
* import { Avatar as MyAvatar } → import { AvatarDeprecated as MyAvatar }
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* npx jscodeshift@latest \
|
|
29
|
+
* --transform node_modules/@staffbase/design/codemods/v20-migrate-avatar/transform.js \
|
|
30
|
+
* --extensions tsx,ts \
|
|
31
|
+
* --parser tsx \
|
|
32
|
+
* src/
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const DESIGN_PKG = '@staffbase/design';
|
|
36
|
+
|
|
37
|
+
export default function transform(file, api) {
|
|
38
|
+
const j = api.jscodeshift;
|
|
39
|
+
const root = j(file.source);
|
|
40
|
+
let dirty = false;
|
|
41
|
+
|
|
42
|
+
// Bail early only if there are no @staffbase/design imports at all.
|
|
43
|
+
const designImports = root.find(j.ImportDeclaration, {source: {value: DESIGN_PKG}});
|
|
44
|
+
if (!designImports.length) return undefined;
|
|
45
|
+
|
|
46
|
+
// 1. Find the `Avatar` and `AvatarProps` import specifiers.
|
|
47
|
+
let localName = null;
|
|
48
|
+
let localPropsName = null;
|
|
49
|
+
|
|
50
|
+
designImports.find(j.ImportSpecifier).forEach((spec) => {
|
|
51
|
+
const importedName = spec.node.imported.name;
|
|
52
|
+
|
|
53
|
+
if (importedName === 'Avatar') {
|
|
54
|
+
// Capture local alias (or fall back to imported name when no alias).
|
|
55
|
+
localName = spec.node.local ? spec.node.local.name : 'Avatar';
|
|
56
|
+
const hasAlias = localName !== 'Avatar';
|
|
57
|
+
|
|
58
|
+
spec.node.imported.name = 'AvatarDeprecated';
|
|
59
|
+
|
|
60
|
+
if (!hasAlias) {
|
|
61
|
+
spec.node.local = j.identifier('AvatarDeprecated');
|
|
62
|
+
localName = 'AvatarDeprecated';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dirty = true;
|
|
66
|
+
} else if (importedName === 'AvatarProps') {
|
|
67
|
+
localPropsName = spec.node.local ? spec.node.local.name : 'AvatarProps';
|
|
68
|
+
const hasAlias = localPropsName !== 'AvatarProps';
|
|
69
|
+
|
|
70
|
+
spec.node.imported.name = 'AvatarDeprecatedProps';
|
|
71
|
+
|
|
72
|
+
if (!hasAlias) {
|
|
73
|
+
spec.node.local = j.identifier('AvatarDeprecatedProps');
|
|
74
|
+
localPropsName = 'AvatarDeprecatedProps';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dirty = true;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Remove the early-return guard here — Banner-only files must also be
|
|
82
|
+
// processed by the steps below.
|
|
83
|
+
|
|
84
|
+
// 2. Rename all JSX usages only when there was no alias (alias keeps its
|
|
85
|
+
// local name unchanged, so no JSX rename is needed).
|
|
86
|
+
if (localName === 'AvatarDeprecated') {
|
|
87
|
+
root
|
|
88
|
+
.find(j.JSXIdentifier, {name: 'Avatar'})
|
|
89
|
+
.filter((path) => {
|
|
90
|
+
// Only rename opening/closing tags that refer to the Avatar import.
|
|
91
|
+
// Guard against unrelated components also named Avatar in other files.
|
|
92
|
+
return path.parent.node.type === 'JSXOpeningElement' || path.parent.node.type === 'JSXClosingElement';
|
|
93
|
+
})
|
|
94
|
+
.forEach((path) => {
|
|
95
|
+
path.node.name = 'AvatarDeprecated';
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 3. Rename AvatarProps type references when there was no alias.
|
|
100
|
+
if (localPropsName === 'AvatarDeprecatedProps') {
|
|
101
|
+
root.find(j.Identifier, {name: 'AvatarProps'}).forEach((path) => {
|
|
102
|
+
const parentType = path.parent.node.type;
|
|
103
|
+
if (
|
|
104
|
+
parentType === 'TSTypeReference' ||
|
|
105
|
+
parentType === 'TSTypeAliasDeclaration' ||
|
|
106
|
+
parentType === 'TSInterfaceHeritage'
|
|
107
|
+
) {
|
|
108
|
+
path.node.name = 'AvatarDeprecatedProps';
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 5. Rename Banner.CloseButton → Banner.Close (JSXMemberExpression).
|
|
114
|
+
root
|
|
115
|
+
.find(j.JSXMemberExpression, {
|
|
116
|
+
object: {name: 'Banner'},
|
|
117
|
+
property: {name: 'CloseButton'},
|
|
118
|
+
})
|
|
119
|
+
.forEach((path) => {
|
|
120
|
+
path.node.property.name = 'Close';
|
|
121
|
+
dirty = true;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// 6. Remove CloseButtonProps from @staffbase/design import and replace all
|
|
125
|
+
// type references with ComponentProps<typeof Banner.Close>.
|
|
126
|
+
let closeButtonPropsLocalName = null;
|
|
127
|
+
|
|
128
|
+
designImports.find(j.ImportSpecifier).forEach((spec) => {
|
|
129
|
+
if (spec.node.imported.name === 'CloseButtonProps') {
|
|
130
|
+
closeButtonPropsLocalName = spec.node.local ? spec.node.local.name : 'CloseButtonProps';
|
|
131
|
+
|
|
132
|
+
// Remove this specifier from the import declaration.
|
|
133
|
+
const importDecl = spec.parent.node;
|
|
134
|
+
importDecl.specifiers = importDecl.specifiers.filter((s) => s !== spec.node);
|
|
135
|
+
|
|
136
|
+
// Drop the entire import declaration if it is now empty.
|
|
137
|
+
if (importDecl.specifiers.length === 0) {
|
|
138
|
+
j(spec.parent).remove();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
dirty = true;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (closeButtonPropsLocalName) {
|
|
146
|
+
// Replace every type reference to the old local name with
|
|
147
|
+
// ComponentProps<typeof Banner.Close>.
|
|
148
|
+
const buildComponentPropsNode = () =>
|
|
149
|
+
j.tsTypeReference(
|
|
150
|
+
j.identifier('ComponentProps'),
|
|
151
|
+
j.tsTypeParameterInstantiation([
|
|
152
|
+
j.tsTypeQuery(j.tsQualifiedName(j.identifier('Banner'), j.identifier('Close'))),
|
|
153
|
+
]),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
root.find(j.Identifier, {name: closeButtonPropsLocalName}).forEach((path) => {
|
|
157
|
+
const parentType = path.parent.node.type;
|
|
158
|
+
if (
|
|
159
|
+
parentType === 'TSTypeReference' ||
|
|
160
|
+
parentType === 'TSTypeAliasDeclaration' ||
|
|
161
|
+
parentType === 'TSInterfaceHeritage'
|
|
162
|
+
) {
|
|
163
|
+
j(path.parent).replaceWith(buildComponentPropsNode());
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Ensure `ComponentProps` is imported from 'react'.
|
|
168
|
+
const reactImports = root.find(j.ImportDeclaration, {source: {value: 'react'}});
|
|
169
|
+
if (reactImports.length) {
|
|
170
|
+
const reactImport = reactImports.get();
|
|
171
|
+
const alreadyImported = reactImport.node.specifiers.some(
|
|
172
|
+
(s) => s.imported && s.imported.name === 'ComponentProps',
|
|
173
|
+
);
|
|
174
|
+
if (!alreadyImported) {
|
|
175
|
+
const newSpec = j.importSpecifier(j.identifier('ComponentProps'));
|
|
176
|
+
newSpec.importKind = 'type';
|
|
177
|
+
reactImport.node.specifiers.push(newSpec);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
// No existing react import — prepend one.
|
|
181
|
+
const newImport = j.importDeclaration(
|
|
182
|
+
[Object.assign(j.importSpecifier(j.identifier('ComponentProps')), {importKind: 'type'})],
|
|
183
|
+
j.literal('react'),
|
|
184
|
+
);
|
|
185
|
+
newImport.importKind = 'type';
|
|
186
|
+
root.find(j.ImportDeclaration).at(0).insertBefore(newImport);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return dirty ? root.toSource({quote: 'single'}) : undefined;
|
|
191
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026, Staffbase SE and contributors.
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
See the License for the specific language governing permissions and
|
|
11
|
+
limitations under the License.
|
|
12
|
+
*/
|
|
13
|
+
//#region \0rolldown/runtime.js
|
|
14
|
+
var __create = Object.create;
|
|
15
|
+
var __defProp = Object.defineProperty;
|
|
16
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
17
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
19
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
22
|
+
key = keys[i];
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
24
|
+
get: ((k) => from[k]).bind(null, key),
|
|
25
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
31
|
+
value: mod,
|
|
32
|
+
enumerable: true
|
|
33
|
+
}) : target, mod));
|
|
34
|
+
//#endregion
|
|
35
|
+
Object.defineProperty(exports, "__toESM", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function() {
|
|
38
|
+
return __toESM;
|
|
39
|
+
}
|
|
40
|
+
});
|