Skip to content

Commit 19f34bf

Browse files
authored
feat(ui): export Appearance type from root entry (#7836)
1 parent 2b46024 commit 19f34bf

File tree

7 files changed

+243
-0
lines changed

7 files changed

+243
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/ui': patch
3+
'@clerk/upgrade': patch
4+
---
5+
6+
Export `Appearance` type from `@clerk/ui` root entry

packages/ui/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Ui } from './internal';
22
import { UI_BRAND } from './internal';
33
import type { Appearance } from './internal/appearance';
44

5+
export type { Appearance } from './internal/appearance';
6+
57
import { ClerkUI } from './ClerkUI';
68

79
declare const PACKAGE_VERSION: string;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
export const fixtures = [
2+
{
3+
name: 'Rewrites basic type import',
4+
source: `
5+
import type { UserResource, ClerkResource } from '@clerk/types';
6+
`,
7+
output: `
8+
import type { UserResource, ClerkResource } from "@clerk/shared/types";
9+
`,
10+
},
11+
{
12+
name: 'Rewrites value import',
13+
source: `
14+
import { OAUTH_PROVIDERS } from '@clerk/types';
15+
`,
16+
output: `
17+
import { OAUTH_PROVIDERS } from "@clerk/shared/types";
18+
`,
19+
},
20+
{
21+
name: 'Redirects Appearance to @clerk/ui',
22+
source: `
23+
import type { Appearance } from '@clerk/types';
24+
`,
25+
output: `
26+
import type { Appearance } from "@clerk/ui";
27+
`,
28+
},
29+
{
30+
name: 'Splits mixed import with Appearance',
31+
source: `
32+
import type { Appearance, UserResource, ClerkResource } from '@clerk/types';
33+
`,
34+
output: `
35+
import type { UserResource, ClerkResource } from "@clerk/shared/types";
36+
import type { Appearance } from "@clerk/ui";
37+
`,
38+
},
39+
{
40+
name: 'Handles require statements',
41+
source: `
42+
const { UserResource } = require('@clerk/types');
43+
`,
44+
output: `
45+
const { UserResource } = require("@clerk/shared/types");
46+
`,
47+
},
48+
{
49+
name: 'Handles require with Appearance only',
50+
source: `
51+
const { Appearance } = require('@clerk/types');
52+
`,
53+
output: `
54+
const { Appearance } = require("@clerk/ui");
55+
`,
56+
},
57+
{
58+
name: 'Splits mixed require with Appearance',
59+
source: `
60+
const { Appearance, UserResource } = require('@clerk/types');
61+
`,
62+
output: `
63+
const {
64+
UserResource
65+
} = require("@clerk/shared/types");
66+
67+
const {
68+
Appearance
69+
} = require("@clerk/ui");
70+
`,
71+
},
72+
{
73+
name: 'Handles namespace import',
74+
source: `
75+
import * as Types from '@clerk/types';
76+
`,
77+
output: `
78+
import * as Types from "@clerk/shared/types";
79+
`,
80+
},
81+
];
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { applyTransform } from 'jscodeshift/dist/testUtils';
2+
import { describe, expect, it } from 'vitest';
3+
4+
import transformer from '../transform-clerk-types-to-shared-types.cjs';
5+
import { fixtures } from './__fixtures__/transform-clerk-types-to-shared-types.fixtures';
6+
7+
describe('transform-clerk-types-to-shared-types', () => {
8+
it.each(fixtures)('$name', ({ source, output }) => {
9+
const result = applyTransform(transformer, {}, { source });
10+
11+
expect(result).toEqual(output.trim());
12+
});
13+
});
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
const SOURCE_PACKAGE = '@clerk/types';
2+
const TARGET_PACKAGE = '@clerk/shared/types';
3+
const UI_PACKAGE = '@clerk/ui';
4+
5+
/**
6+
* Specifiers that should be redirected to `@clerk/ui` instead of `@clerk/shared/types`.
7+
*/
8+
const UI_SPECIFIERS = new Set(['Appearance']);
9+
10+
/**
11+
* Transforms imports of `@clerk/types` to `@clerk/shared/types`, splitting out
12+
* `Appearance` to `@clerk/ui`.
13+
*
14+
* @param {import('jscodeshift').FileInfo} fileInfo
15+
* @param {import('jscodeshift').API} api
16+
* @returns {string|undefined}
17+
*/
18+
module.exports = function transformClerkTypesToSharedTypes({ source }, { jscodeshift: j }) {
19+
const root = j(source);
20+
let dirty = false;
21+
22+
// --- Transform import declarations ---
23+
root.find(j.ImportDeclaration, { source: { value: SOURCE_PACKAGE } }).forEach(path => {
24+
const node = path.node;
25+
const specifiers = node.specifiers || [];
26+
const importKind = node.importKind;
27+
28+
const uiSpecifiers = [];
29+
const sharedSpecifiers = [];
30+
31+
for (const spec of specifiers) {
32+
if (j.ImportSpecifier.check(spec) && UI_SPECIFIERS.has(spec.imported.name)) {
33+
uiSpecifiers.push(spec);
34+
} else {
35+
sharedSpecifiers.push(spec);
36+
}
37+
}
38+
39+
if (uiSpecifiers.length > 0 && sharedSpecifiers.length > 0) {
40+
// Mixed: split into two imports
41+
const sharedImport = j.importDeclaration(sharedSpecifiers, j.stringLiteral(TARGET_PACKAGE));
42+
if (importKind) {
43+
sharedImport.importKind = importKind;
44+
}
45+
sharedImport.comments = node.comments;
46+
47+
const uiImport = j.importDeclaration(uiSpecifiers, j.stringLiteral(UI_PACKAGE));
48+
if (importKind) {
49+
uiImport.importKind = importKind;
50+
}
51+
52+
j(path).replaceWith([sharedImport, uiImport]);
53+
dirty = true;
54+
return;
55+
}
56+
57+
if (uiSpecifiers.length > 0) {
58+
// Only UI specifiers
59+
node.source.value = UI_PACKAGE;
60+
dirty = true;
61+
return;
62+
}
63+
64+
// Only shared specifiers (or namespace/default imports)
65+
node.source.value = TARGET_PACKAGE;
66+
dirty = true;
67+
});
68+
69+
// --- Transform require calls ---
70+
root
71+
.find(j.VariableDeclarator, {
72+
init: {
73+
callee: { name: 'require' },
74+
arguments: [{ value: SOURCE_PACKAGE }],
75+
},
76+
})
77+
.forEach(path => {
78+
const node = path.node;
79+
const id = node.id;
80+
81+
if (id.type === 'ObjectPattern') {
82+
const uiProperties = [];
83+
const sharedProperties = [];
84+
85+
for (const prop of id.properties) {
86+
if (prop.key && UI_SPECIFIERS.has(prop.key.name)) {
87+
uiProperties.push(prop);
88+
} else {
89+
sharedProperties.push(prop);
90+
}
91+
}
92+
93+
if (uiProperties.length > 0 && sharedProperties.length > 0) {
94+
// Mixed: keep shared on main, create new require for UI
95+
node.id.properties = sharedProperties;
96+
node.init.arguments[0] = j.literal(TARGET_PACKAGE);
97+
98+
const variableDeclaration = path.parent.node;
99+
const kind = variableDeclaration.kind || 'const';
100+
101+
const uiDeclarator = j.variableDeclarator(
102+
j.objectPattern(uiProperties),
103+
j.callExpression(j.identifier('require'), [j.literal(UI_PACKAGE)]),
104+
);
105+
const uiDeclaration = j.variableDeclaration(kind, [uiDeclarator]);
106+
107+
j(path.parent).insertAfter(uiDeclaration);
108+
dirty = true;
109+
return;
110+
}
111+
112+
if (uiProperties.length > 0) {
113+
node.init.arguments[0] = j.literal(UI_PACKAGE);
114+
dirty = true;
115+
return;
116+
}
117+
}
118+
119+
// Only shared or not destructured
120+
node.init.arguments[0] = j.literal(TARGET_PACKAGE);
121+
dirty = true;
122+
});
123+
124+
if (!dirty) {
125+
return undefined;
126+
}
127+
128+
let result = root.toSource();
129+
result = result.replace(/^(['"`][^'"`]+['"`]);;/gm, '$1;');
130+
return result;
131+
};
132+
133+
module.exports.parser = 'tsx';

packages/upgrade/src/versions/core-3/changes/clerk-types-deprecation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ Update your imports:
1515
```
1616

1717
The `@clerk/types` package will continue to re-export types from `@clerk/shared/types` for backward compatibility, but new types will only be added to `@clerk/shared/types`.
18+
19+
**Note:** The `Appearance` type should be imported from `@clerk/ui` instead of `@clerk/shared/types`:
20+
21+
```diff
22+
- import type { Appearance } from '@clerk/types';
23+
+ import type { Appearance } from '@clerk/ui';
24+
```

packages/upgrade/src/versions/core-3/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default {
1818
'transform-remove-deprecated-appearance-props',
1919
'transform-appearance-layout-to-options',
2020
'transform-themes-to-ui-themes',
21+
'transform-clerk-types-to-shared-types',
2122
'transform-align-experimental-unstable-prefixes',
2223
// React/JSX version of Protect→Show (handles .tsx, .jsx, .ts, .js files)
2324
{

0 commit comments

Comments
 (0)