@yahoo/uds 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/cli/README.md +15 -2
  2. package/cli/codemods/remapProps.ts +65 -0
  3. package/cli/commands/codemod/spacingProps.ts +52 -0
  4. package/cli/preload.ts +12 -8
  5. package/cli/utils/purgeCSS.test.ts +55 -18
  6. package/cli/utils/purgeCSS.ts +144 -21
  7. package/dist/{Image.native-CfDCpWe_.d.cts → Image.native-Dy2tsvOP.d.ts} +13 -12
  8. package/dist/{Image.native-nwk5tr_f.d.ts → Image.native-DyqCXXic.d.cts} +13 -12
  9. package/dist/{VStack-D-py89Ge.d.cts → VStack-BjW120vC.d.cts} +25 -2
  10. package/dist/{VStack-Bj6PfbK5.d.ts → VStack-CPOOI31y.d.ts} +25 -2
  11. package/dist/experimental/index.cjs +2 -1
  12. package/dist/experimental/index.d.cts +30 -32
  13. package/dist/experimental/index.d.ts +30 -32
  14. package/dist/experimental/index.js +2 -1
  15. package/dist/experimental/index.native.cjs +1 -1
  16. package/dist/experimental/index.native.d.cts +13 -16
  17. package/dist/experimental/index.native.d.ts +13 -16
  18. package/dist/experimental/index.native.js +1 -1
  19. package/dist/fixtures.cjs +1 -664
  20. package/dist/fixtures.d.ts +1 -2
  21. package/dist/fixtures.js +1 -663
  22. package/dist/index.cjs +2 -1
  23. package/dist/index.d.cts +14 -30
  24. package/dist/index.d.ts +14 -30
  25. package/dist/index.js +3 -1
  26. package/dist/{index.native-BLJdXZHY.d.ts → index.native-D-eItswB.d.ts} +1 -1
  27. package/dist/{index.native-A2gCijBH.d.cts → index.native-VZWytAxw.d.cts} +1 -1
  28. package/dist/index.native.cjs +1 -1
  29. package/dist/index.native.d.cts +1122 -10
  30. package/dist/index.native.d.ts +1122 -10
  31. package/dist/index.native.js +1 -1
  32. package/dist/metafile-cjs.json +1 -0
  33. package/dist/metafile-esm.json +1 -0
  34. package/dist/tailwind/plugin.cjs +1 -1
  35. package/dist/tailwind/plugin.d.cts +1 -1
  36. package/dist/tailwind/plugin.d.ts +1 -1
  37. package/dist/tailwind/plugin.js +3 -1
  38. package/dist/tailwind/purger.cjs +3 -1
  39. package/dist/tailwind/purger.d.cts +3 -6
  40. package/dist/tailwind/purger.d.ts +3 -6
  41. package/dist/tailwind/purger.js +3 -1
  42. package/dist/tailwind/tsMorph.cjs +1 -1
  43. package/dist/tailwind/tsMorph.d.cts +11 -2
  44. package/dist/tailwind/tsMorph.d.ts +11 -2
  45. package/dist/tailwind/tsMorph.js +3 -1
  46. package/dist/tokens/index.cjs +2 -1
  47. package/dist/tokens/index.d.cts +1097 -3
  48. package/dist/tokens/index.d.ts +1097 -3
  49. package/dist/tokens/index.js +2 -1
  50. package/dist/tokens/index.native.d.cts +2 -2
  51. package/dist/tokens/index.native.d.ts +2 -2
  52. package/dist/tokens/parseTokens.cjs +2 -1
  53. package/dist/tokens/parseTokens.d.cts +1 -1
  54. package/dist/tokens/parseTokens.d.ts +1 -1
  55. package/dist/tokens/parseTokens.js +2 -1
  56. package/dist/{types-Diou6f1Q.d.cts → types-Cy3fKMaw.d.cts} +21 -20
  57. package/dist/{types-Diou6f1Q.d.ts → types-Cy3fKMaw.d.ts} +21 -20
  58. package/dist/{types-Dk8fLx7L.d.cts → types-at8CTkly.d.cts} +21 -20
  59. package/dist/{types-Dk8fLx7L.d.ts → types-at8CTkly.d.ts} +21 -20
  60. package/package.json +20 -19
package/cli/README.md CHANGED
@@ -46,7 +46,7 @@ uds sync --id [id] --outFile [path]
46
46
  uds sync --id [id]
47
47
  ```
48
48
 
49
- #### Enviornment variables
49
+ #### Environment variables
50
50
 
51
51
  Alternatively, you can use environment variables instead of flags.
52
52
 
@@ -86,7 +86,20 @@ For more information about safelisting classes in Tailwind, visit the [official
86
86
  uds purge
87
87
  ```
88
88
 
89
- #### Enviornment variables
89
+ #### Flags
90
+
91
+ | Flag | Description | Default | Required |
92
+ | -------- | --------------------------- | ------------------ | -------- |
93
+ | `output` | Output path of the safelist | ./dist/safelist.ts | |
94
+ | `config` | UDS config | ./uds.config.ts | |
95
+
96
+ **Example:**
97
+
98
+ ```shell
99
+ uds purge --output output/dir
100
+ ```
101
+
102
+ #### Environment variables
90
103
 
91
104
  | Variable | Description | Default | Required |
92
105
  | ------------------------------- | ------------------------------------------- | ------- | -------- |
@@ -0,0 +1,65 @@
1
+ import path from 'node:path';
2
+
3
+ import { green, print, red } from 'bluebun';
4
+ import { IndentationText, Project, SyntaxKind } from 'ts-morph';
5
+
6
+ interface RemapPropsOptions {
7
+ /** Properties to remap values for */
8
+ properties: string[];
9
+ /** Mapping of old value to new value */
10
+ oldToNewMap: Record<string, string>;
11
+ }
12
+
13
+ /**
14
+ * Remap props from old to new values.
15
+ * For example, a UDS component with a prop of `spacing="6"` should be updated to `spacing="4"`.
16
+ */
17
+ export async function remapProps({ properties, oldToNewMap }: RemapPropsOptions) {
18
+ const workspaceDir = Bun.env.PWD;
19
+ const srcDir = path.join(workspaceDir, 'tsconfig.json');
20
+ const project = new Project({
21
+ tsConfigFilePath: srcDir,
22
+ manipulationSettings: { indentationText: IndentationText.TwoSpaces },
23
+ });
24
+
25
+ const sourceFiles = project.getSourceFiles(`${workspaceDir}/**/*.{ts,tsx}`);
26
+
27
+ for (const sourceFile of sourceFiles) {
28
+ const jsxOpeningElement = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
29
+ const jsxSelfClosingElement = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
30
+ const jsxElements = [...jsxOpeningElement, ...jsxSelfClosingElement];
31
+ for (const jsxElement of jsxElements) {
32
+ const identifier = jsxElement.getFirstChildByKind(SyntaxKind.Identifier);
33
+ const symbol = identifier?.getSymbol();
34
+ const isUdsComponent = symbol?.getDeclarations().some((declaration) => {
35
+ const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
36
+ return importDeclaration?.getModuleSpecifierValue().startsWith('@yahoo/uds');
37
+ });
38
+
39
+ if (isUdsComponent) {
40
+ const attributes = jsxElement.getAttributes();
41
+ for (const attribute of attributes) {
42
+ const propIdentifier = attribute.getFirstChildByKind(SyntaxKind.Identifier);
43
+ const propName = propIdentifier?.getText();
44
+
45
+ if (propName && properties.includes(propName)) {
46
+ const oldValue = attribute
47
+ .getFirstChildByKind(SyntaxKind.StringLiteral)
48
+ ?.getLiteralValue();
49
+ const newValue = oldValue ? oldToNewMap[oldValue] : undefined;
50
+
51
+ if (newValue) {
52
+ const oldLog = red(`${propName}=${oldValue}`);
53
+ const newLog = green(`${propName}=${newValue}`);
54
+ const relativePath = sourceFile.getFilePath().replace(Bun.env.PWD, '');
55
+ print(`${relativePath} ${oldLog} -> ${newLog}`);
56
+ attribute.set({ name: propName, initializer: `"${newValue}"` });
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ await project.save();
65
+ }
@@ -0,0 +1,52 @@
1
+ import { spinStart, spinStop } from 'bluebun';
2
+
3
+ import { remapProps } from '../../codemods/remapProps';
4
+
5
+ export default {
6
+ name: 'spacingProps',
7
+ description: `Migrate spacing props from old to new values.`,
8
+ run: async () => {
9
+ spinStart('Running codemod...');
10
+
11
+ await remapProps({
12
+ properties: [
13
+ 'spacing',
14
+ 'spacingHorizontal',
15
+ 'spacingVertical',
16
+ 'spacingBottom',
17
+ 'spacingEnd',
18
+ 'spacingStart',
19
+ 'spacingTop',
20
+ 'offset',
21
+ 'offsetHorizontal',
22
+ 'offsetVertical',
23
+ 'offsetBottom',
24
+ 'offsetEnd',
25
+ 'offsetStart',
26
+ 'offsetTop',
27
+ 'gap',
28
+ 'columnGap',
29
+ 'rowGap',
30
+ ],
31
+ oldToNewMap: {
32
+ '1': 'px',
33
+ '2': '0.5',
34
+ '3': '1',
35
+ '4': '2',
36
+ '5': '3',
37
+ '6': '4',
38
+ '7': '6',
39
+ '8': '8',
40
+ '9': '10',
41
+ '10': '12',
42
+ '11': '14',
43
+ '12': '16',
44
+ /** So typescript will fail for this replacement */
45
+ '13': 'THIS VALUE DOES NOT EXIST. PLEASE REMOVE.',
46
+ '14': '20',
47
+ },
48
+ });
49
+
50
+ spinStop('Codemod complete! Peek the diff and commit your changes!');
51
+ },
52
+ };
package/cli/preload.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { jest, mock } from 'bun:test';
1
+ import { mock, spyOn } from 'bun:test';
2
2
 
3
- const mockFastGlob = jest.fn().mockResolvedValue(['/pages/PageA.tsx', '/pages/PageB.tsx']);
4
-
5
- mock.module('fast-glob', () => ({ __esModule: true, default: mockFastGlob }));
3
+ spyOn(global.Bun.Glob.prototype, 'scan').mockImplementation(() => {
4
+ return ['/pages/PageA.tsx', '/pages/PageB.tsx'] as unknown as AsyncIterableIterator<string>;
5
+ });
6
6
 
7
7
  mock.module('@yahoo/uds/tailwind/purger', () => ({
8
8
  componentsDependencies: {
@@ -11,15 +11,19 @@ mock.module('@yahoo/uds/tailwind/purger', () => ({
11
11
  HStack: ['Box'],
12
12
  },
13
13
  componentToVariants: {
14
- Button: ['color'],
15
- HStack: ['alignItems', 'justifyContent'],
16
- Icon: ['color'],
14
+ Button: [['color', ['accent']]],
15
+ HStack: [
16
+ ['alignItems', ['flex-start']],
17
+ ['justifyContent', ['flex-start']],
18
+ ],
19
+ Icon: [['color', ['accent']]],
17
20
  Text: ['fontSize', 'fontFamily'],
18
21
  Pressable: ['display'],
19
22
  },
20
23
  variantsList: ['color', 'alignItems', 'justifyContent', 'display', 'fontSize', 'fontFamily'],
21
24
  variantToTailwindClass: {
22
- color: 'text-accent text-alert text-black text-brand text-positive text-warning text-white',
25
+ color:
26
+ 'container text-accent text-alert text-black text-brand text-positive text-warning text-white',
23
27
  alignItems: 'items-start items-end items-center items-stretch items-baseline',
24
28
  justifyContent:
25
29
  'justify-start justify-end justify-center justify-between justify-around justify-evenly',
@@ -9,10 +9,11 @@ import {
9
9
  getUsedProps,
10
10
  isUDSComponent,
11
11
  parseFiles,
12
+ scanGetStylesReferences,
12
13
  } from './purgeCSS';
13
14
 
14
15
  const PAGE_A_CODE = `
15
- import { HStack, Button } from '@yahoo/uds';
16
+ import { HStack, Button, getStyles } from '@yahoo/uds';
16
17
 
17
18
  const functionWithProp = () => {
18
19
  const vars = {
@@ -27,10 +28,20 @@ const functionWithProp = () => {
27
28
 
28
29
  const AnotherComponent = () => {
29
30
  const propsList = functionWithProp();
30
- return <HStack test="test" {...propsList}>meow</HStack>
31
+
32
+ const classNames = getStyles({
33
+ borderStartColor: 'primary'
34
+ });
35
+
36
+ return <HStack testProp="testValue" classNames={classNames} {...propsList}>meow</HStack>
31
37
  }
32
38
 
33
39
  const PageA = () => {
40
+ const justifyContent = 'justify-start';
41
+ const classNames = getStyles({
42
+ justifyContent
43
+ });
44
+
34
45
  return (
35
46
  <div>
36
47
  <HStack />
@@ -56,7 +67,7 @@ const PageB = () => {
56
67
  `;
57
68
 
58
69
  const FILES = ['/pages/PageA.tsx', '/pages/PageB.tsx'];
59
- const IMPORTED_UDS_COMPONENTS = ['HStack', 'Button', 'Spinner'];
70
+ const IMPORTED_UDS_COMPONENTS = ['HStack', 'Button', 'getStyles', 'Spinner'];
60
71
 
61
72
  describe('purgeCSS', () => {
62
73
  const project = new Project();
@@ -85,24 +96,23 @@ describe('purgeCSS', () => {
85
96
  });
86
97
 
87
98
  describe('getTailwindSafelist', () => {
88
- it('returns the tailwind classes corresponding to the props on a component', () => {
99
+ it('returns the tailwind classes required for all the components', () => {
100
+ project.createSourceFile(FILES[0], PAGE_A_CODE, {
101
+ overwrite: true,
102
+ });
103
+ project.createSourceFile(FILES[1], PAGE_B_CODE, {
104
+ overwrite: true,
105
+ });
106
+
89
107
  const res = getTailwindSafelist(project, IMPORTED_UDS_COMPONENTS);
90
108
 
91
109
  expect(res).toEqual([
92
110
  'container',
93
111
  'fill',
94
- '',
95
112
  'items-start',
96
- 'items-end',
97
113
  'items-center',
98
- 'items-stretch',
99
- 'items-baseline',
100
114
  'justify-start',
101
- 'justify-end',
102
115
  'justify-center',
103
- 'justify-between',
104
- 'justify-around',
105
- 'justify-evenly',
106
116
  'text-accent',
107
117
  'text-alert',
108
118
  'text-black',
@@ -110,6 +120,13 @@ describe('purgeCSS', () => {
110
120
  'text-positive',
111
121
  'text-warning',
112
122
  'text-white',
123
+ 'text-transparent',
124
+ 'text-muted',
125
+ 'text-on-color',
126
+ 'text-primary',
127
+ 'text-secondary',
128
+ 'text-tertiary',
129
+ 'border-s-primary',
113
130
  ]);
114
131
  });
115
132
  });
@@ -148,12 +165,13 @@ describe('purgeCSS', () => {
148
165
  const usedProps = getUsedProps(project, 'HStack');
149
166
 
150
167
  expect(usedProps).toEqual([
151
- 'test',
152
- 'color',
153
- 'isActive',
154
- 'flexGrow',
155
- 'alignItems',
156
- 'justifyContent',
168
+ ['testProp', ['testValue']],
169
+ ['classNames', []],
170
+ ['color', []],
171
+ ['isActive', []],
172
+ ['flexGrow', ['1']],
173
+ ['alignItems', ['center']],
174
+ ['justifyContent', ['center']],
157
175
  ]);
158
176
  });
159
177
  });
@@ -169,4 +187,23 @@ describe('purgeCSS', () => {
169
187
  ]);
170
188
  });
171
189
  });
190
+
191
+ describe('scanGetStylesReferences', () => {
192
+ it('should scan the project for getStyles references and return the used variants', () => {
193
+ project.createSourceFile(FILES[0], PAGE_A_CODE, {
194
+ overwrite: true,
195
+ });
196
+ project.createSourceFile(FILES[1], PAGE_B_CODE, {
197
+ overwrite: true,
198
+ });
199
+
200
+ const res = scanGetStylesReferences(project);
201
+
202
+ const expectedResult = new Map();
203
+ expectedResult.set('borderStartColor', new Set(['primary']));
204
+ expectedResult.set('justifyContent', new Set([]));
205
+
206
+ expect(res).toEqual(expectedResult);
207
+ });
208
+ });
172
209
  });
@@ -5,9 +5,11 @@ import {
5
5
  componentToTwClasses,
6
6
  componentToVariants,
7
7
  variantsList,
8
- variantToTailwindClass,
9
8
  } from '@yahoo/uds/tailwind/purger';
10
- import { findReferencesAsJsxElements, getUsedPropsInReference } from '@yahoo/uds/tailwind/tsMorph';
9
+ import {
10
+ findReferencesAsJsxElements,
11
+ getUsedPropsInReference,
12
+ } from '@yahoo/uds/tailwind/purger/utils';
11
13
  import {
12
14
  ColorModeConfig,
13
15
  DARK_COLOR_MODE_CLASSNAME,
@@ -17,6 +19,7 @@ import {
17
19
  ScaleModeConfig,
18
20
  SMALL_SCALE_MODE_CLASSNAME,
19
21
  UniversalTokensConfig,
22
+ variants,
20
23
  XLARGE_SCALE_MODE_CLASSNAME,
21
24
  XSMALL_SCALE_MODE_CLASSNAME,
22
25
  XXLARGE_SCALE_MODE_CLASSNAME,
@@ -24,8 +27,18 @@ import {
24
27
  } from '@yahoo/uds/tokens';
25
28
  import { print, spinStart, spinStop } from 'bluebun';
26
29
  import { BunFile } from 'bun';
27
- import FastGlob from 'fast-glob';
28
- import { JsxOpeningElement, JsxSelfClosingElement, Project, ts } from 'ts-morph';
30
+ import { uniq } from 'lodash-es';
31
+ import {
32
+ CallExpression,
33
+ JsxOpeningElement,
34
+ JsxSelfClosingElement,
35
+ Node,
36
+ Project,
37
+ SyntaxKind,
38
+ ts,
39
+ } from 'ts-morph';
40
+
41
+ import packageJson from '../../package.json';
29
42
 
30
43
  const scaleModeToClass: { [key in keyof ScaleModeConfig]: string } = {
31
44
  large: LARGE_SCALE_MODE_CLASSNAME,
@@ -49,9 +62,8 @@ type Files = string[];
49
62
  export const getFiles = async (): Promise<Files> => {
50
63
  const workspaceDir = Bun.env.PWD;
51
64
  const srcDir = path.join(workspaceDir, '/src/');
52
- const files = await FastGlob(`${srcDir}/**/*.{jsx,tsx}`);
53
-
54
- return files;
65
+ const glob = new Bun.Glob('**/*.{jsx,tsx}');
66
+ return Array.fromAsync(glob.scan({ cwd: srcDir, absolute: true }));
55
67
  };
56
68
 
57
69
  /**
@@ -125,23 +137,34 @@ export const parseFiles = (project: Project, files: Files): ImportsList => {
125
137
  export const getTailwindSafelist = (project: Project, componentList: string[]): SafeList => {
126
138
  const safeList: SafeList = [];
127
139
  const validVariants = new Set<string>(variantsList);
128
- const usedProps = new Set<string>();
140
+ // we track the prop name and which values have been used
141
+ const usedProps = new Map<string, Set<string>>();
129
142
  componentList.forEach((component: string) => {
130
143
  if (isUDSComponent(component)) {
131
144
  // get the TW classes relevant for each prop
132
145
  // these classes are used internally in UDS,
133
146
  // they either have been initialized or used by other UDS components
134
- componentToVariants[component].forEach((prop: string) => {
135
- if (validVariants.has(prop) && !usedProps.has(prop)) {
136
- usedProps.add(prop);
147
+ componentToVariants[component].forEach(([propName, usedValues]) => {
148
+ if (validVariants.has(propName)) {
149
+ const options: Set<string> = usedProps.get(propName) ?? new Set();
150
+ usedValues.forEach((val) => options.add(val));
151
+ usedProps.set(propName, options);
137
152
  }
138
153
  });
139
154
 
140
155
  // scan the project for used props and
141
156
  // get the corresponding css for those used props
142
- getUsedProps(project, component).forEach((prop: string) => {
143
- if (validVariants.has(prop) && !usedProps.has(prop)) {
144
- usedProps.add(prop);
157
+ getUsedProps(project, component).forEach(([propName, usedValues]) => {
158
+ if (validVariants.has(propName)) {
159
+ // This means that an expression has been used (Ex: `<Button size={getSize()} />`)
160
+ // so we need to add all possible options for that prop
161
+ if (usedValues.length === 0) {
162
+ usedValues = Object.keys(variants[propName as keyof typeof variants]);
163
+ }
164
+
165
+ const options: Set<string> = usedProps.get(propName) ?? new Set();
166
+ usedValues.forEach((val) => options.add(val));
167
+ usedProps.set(propName, options);
145
168
  }
146
169
  });
147
170
 
@@ -150,11 +173,20 @@ export const getTailwindSafelist = (project: Project, componentList: string[]):
150
173
  }
151
174
  });
152
175
 
153
- for (const prop of usedProps) {
154
- safeList.push(...variantToTailwindClass[prop].replaceAll('\\', '').split(' '));
176
+ for (const [propName, usedValues] of usedProps) {
177
+ usedValues.forEach((option) => {
178
+ safeList.push((variants[propName as never][option] as string)?.replaceAll('\\', ''));
179
+ });
180
+ }
181
+
182
+ for (const [variant, variantOptions] of scanGetStylesReferences(project)) {
183
+ variantOptions.forEach((option) => {
184
+ safeList.push((variants[variant as never][option] as string)?.replaceAll('\\', ''));
185
+ });
155
186
  }
156
187
 
157
- return safeList;
188
+ // Return a deduped list and strip out any empty strings
189
+ return uniq(safeList.filter(Boolean));
158
190
  };
159
191
 
160
192
  /**
@@ -163,12 +195,13 @@ export const getTailwindSafelist = (project: Project, componentList: string[]):
163
195
  * @example
164
196
  * const usedProps = getUsedProps(project, 'HStack');
165
197
  */
166
- export const getUsedProps = (project: Project, component: string) => {
198
+ export const getUsedProps = (project: Project, component: string): Array<[string, string[]]> => {
167
199
  const references: (JsxOpeningElement | JsxSelfClosingElement)[] = [];
168
- references.push(...findNamedImportReferences(project, '@yahoo/uds', component));
169
- references.push(...findNamedImportReferences(project, '@yahoo/uds/experimental', component));
200
+ references.push(...findNamedImportReferences(project, packageJson.name, component));
201
+ references.push(
202
+ ...findNamedImportReferences(project, `${packageJson.name}/experimental`, component),
203
+ );
170
204
 
171
- // for each reference find the used/references props
172
205
  const usedProps = references.map((reference) => getUsedPropsInReference(reference)).flat();
173
206
 
174
207
  return usedProps;
@@ -234,6 +267,96 @@ export const getClassesForEnabledThemesAndScales = (): string[] => {
234
267
  return classes;
235
268
  };
236
269
 
270
+ /**
271
+ * Scan the source code for all `getStyles` references
272
+ *
273
+ * Note: This currently only works if we are passing a literal object to getStyles.
274
+ *
275
+ * Explanation: They we are able to enfer what css to include is by looking at the properties of the object passed.
276
+ * If something other than an object is passed, we can fallback on the Type, but that would require handling
277
+ * a lot of edge cases (function return type, spread operator, ternary, ...) and each one of these cases will
278
+ * most likely have a sub case. Falling back to the Type will complicate the code a lot is error prone as there
279
+ * is only so much info we can get out of the types as the Users are free to use `any` on their project which will
280
+ * provide no value for us. Hence why having a literal object passed is the best and probably the only sane way
281
+ * to go about this.
282
+ */
283
+ export const scanGetStylesReferences = (project: Project): Map<string, Set<string>> => {
284
+ // Find all the references for `getStyles`
285
+ const references: CallExpression[] = [];
286
+ for (const sourceFile of project.getSourceFiles()) {
287
+ for (const importDeclaration of sourceFile.getImportDeclarations()) {
288
+ if (importDeclaration.getModuleSpecifierValue() === packageJson.name) {
289
+ for (const namedImport of importDeclaration.getNamedImports()) {
290
+ if (namedImport.getName() === 'getStyles') {
291
+ const identifier = namedImport.getFirstDescendantByKindOrThrow(
292
+ ts.SyntaxKind.Identifier,
293
+ );
294
+
295
+ for (const reference of identifier.findReferencesAsNodes()) {
296
+ const node = reference.getFirstAncestor((node) => {
297
+ return Node.isCallExpression(node);
298
+ });
299
+
300
+ if (node) {
301
+ references.push(node as CallExpression);
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ const usedProps = new Map<string, Set<string>>();
311
+ for (const reference of references) {
312
+ // TODO: handle the throw, tell users to not use something other than an object when calling `getStyles`
313
+ const objectLiteralExpression = reference.getFirstChildByKindOrThrow(
314
+ SyntaxKind.ObjectLiteralExpression,
315
+ );
316
+ const propertyAssignments = objectLiteralExpression.getDescendantsOfKind(
317
+ SyntaxKind.PropertyAssignment,
318
+ ); // PropertyAssignment is { property: 'test' }
319
+ const shorthandPropertyAssignments = objectLiteralExpression.getDescendantsOfKind(
320
+ SyntaxKind.ShorthandPropertyAssignment,
321
+ ); // ShorthandPropertyAssignment is { property }
322
+ // TODO: handle spread assignment
323
+ propertyAssignments.forEach((propertyAssigment) => {
324
+ const identifier = propertyAssigment.getFirstChildByKind(SyntaxKind.Identifier)?.getText();
325
+ // check if we are assigning a string
326
+ const stringLiteral = propertyAssigment
327
+ .getFirstChildByKind(SyntaxKind.StringLiteral)
328
+ ?.getLiteralText();
329
+ if (identifier && !stringLiteral) {
330
+ // this means an expression has been used
331
+ // so we add all the possibilites
332
+ const isvalidVariant = !!variants[identifier as keyof typeof variants];
333
+ const fallback = isvalidVariant
334
+ ? Object.keys(variants[identifier as keyof typeof variants])
335
+ : [];
336
+ usedProps.set(identifier, new Set(fallback));
337
+ }
338
+ if (identifier && stringLiteral) {
339
+ let options: Set<string>;
340
+ if (usedProps.has(identifier)) {
341
+ options = usedProps.get(identifier)!;
342
+ } else {
343
+ options = new Set();
344
+ }
345
+ options.add(stringLiteral);
346
+ usedProps.set(identifier, options);
347
+ }
348
+ });
349
+ shorthandPropertyAssignments.forEach((propertyAssigment) => {
350
+ const identifier = propertyAssigment.getFirstChildByKind(SyntaxKind.Identifier)?.getText();
351
+ if (identifier) {
352
+ usedProps.set(identifier, new Set());
353
+ }
354
+ });
355
+ }
356
+
357
+ return usedProps;
358
+ };
359
+
237
360
  type PurgeOptions = {
238
361
  config?: string;
239
362
  output?: string;
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { b as UniversalPressableProps, c as UniversalIconButtonProps, d as UniversalImageProps } from './types-Dk8fLx7L.cjs';
2
+ import { b as UniversalPressableProps, c as UniversalIconButtonProps, d as UniversalImageProps } from './types-at8CTkly.js';
3
3
  import { View, PressableProps as PressableProps$1, StyleProp, ViewStyle } from 'react-native';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ImageProps as ImageProps$1 } from 'expo-image';
@@ -14,17 +14,18 @@ declare const Pressable: react.ForwardRefExoticComponent<PressableProps & react.
14
14
  interface IconButtonProps extends PressableProps, UniversalIconButtonProps {
15
15
  }
16
16
  /**
17
- * An icon button element that can be used to trigger an action.
18
- * @example
19
- ```tsx
20
- import { IconButton } from "@yahoo/uds";
21
-
22
- export function IconButtonDemo() {
23
- return (
24
- <IconButton variant="accent-outline" color="primary" name="close" onPress={console.log} />
25
- )
26
- }
27
- ```
17
+ * An icon button is essentially an interactive icon. They should be used to
18
+ * display an icon which, when clicked, allows the user to trigger an action.
19
+ * Icon buttons provide additional features such hover states, focus states, and
20
+ * pressable functionality.
21
+ *
22
+ * **Basic usage**
23
+ * ```tsx
24
+ * import { IconButton } from "@yahoo/uds";
25
+ * import { AddFolder } from "@yahoo/uds-icons";
26
+ *
27
+ * <IconButton name={AddFolder} color="primary" variant="accent" iconVariant="outline" />
28
+ * ```
28
29
  */
29
30
  declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<View>>;
30
31
 
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { b as UniversalPressableProps, c as UniversalIconButtonProps, d as UniversalImageProps } from './types-Dk8fLx7L.js';
2
+ import { b as UniversalPressableProps, c as UniversalIconButtonProps, d as UniversalImageProps } from './types-at8CTkly.cjs';
3
3
  import { View, PressableProps as PressableProps$1, StyleProp, ViewStyle } from 'react-native';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ImageProps as ImageProps$1 } from 'expo-image';
@@ -14,17 +14,18 @@ declare const Pressable: react.ForwardRefExoticComponent<PressableProps & react.
14
14
  interface IconButtonProps extends PressableProps, UniversalIconButtonProps {
15
15
  }
16
16
  /**
17
- * An icon button element that can be used to trigger an action.
18
- * @example
19
- ```tsx
20
- import { IconButton } from "@yahoo/uds";
21
-
22
- export function IconButtonDemo() {
23
- return (
24
- <IconButton variant="accent-outline" color="primary" name="close" onPress={console.log} />
25
- )
26
- }
27
- ```
17
+ * An icon button is essentially an interactive icon. They should be used to
18
+ * display an icon which, when clicked, allows the user to trigger an action.
19
+ * Icon buttons provide additional features such hover states, focus states, and
20
+ * pressable functionality.
21
+ *
22
+ * **Basic usage**
23
+ * ```tsx
24
+ * import { IconButton } from "@yahoo/uds";
25
+ * import { AddFolder } from "@yahoo/uds-icons";
26
+ *
27
+ * <IconButton name={AddFolder} color="primary" variant="accent" iconVariant="outline" />
28
+ * ```
28
29
  */
29
30
  declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<View>>;
30
31
 
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { Ref } from 'react';
3
- import { g as UniversalBoxProps, h as UniversalPressableProps, i as UniversalTextProps, j as UniversalStackProps } from './types-Diou6f1Q.cjs';
3
+ import { aH as UniversalBoxProps, aK as UniversalIconProps, aL as UniversalPressableProps, aN as UniversalTextProps, g as UniversalStackProps } from './types-Cy3fKMaw.cjs';
4
4
 
5
5
  type DivProps = React.HTMLAttributes<HTMLDivElement>;
6
6
  interface BoxProps extends UniversalBoxProps, DivProps {
@@ -23,6 +23,29 @@ interface BoxProps extends UniversalBoxProps, DivProps {
23
23
  */
24
24
  declare const Box: react.ForwardRefExoticComponent<BoxProps & react.RefAttributes<HTMLDivElement>>;
25
25
 
26
+ type SVGElementProps = Omit<React.HTMLAttributes<SVGSVGElement>, 'color'>;
27
+ interface IconProps extends UniversalIconProps, SVGElementProps {
28
+ }
29
+ /**
30
+ * Icons are small symbols for actions or other items. They are available in
31
+ * three different sizes: `sm`, `md`, and `lg`. Each size also supports an
32
+ * outline and fill variant. Icons can be colored using the `color` prop.
33
+ *
34
+ * The component is available in the `@yahoo/uds` package but is meant to be used
35
+ * with the assets in `@yahoo/uds-icons`. You will need both packages to use icons.
36
+ * A separate package provides modularity from the core library, better
37
+ * versioning strategies, and tree shakeability.
38
+ *
39
+ * **Basic usage**
40
+ * ```tsx
41
+ * import { Icon } from '@yahoo/uds';
42
+ * import { AddFolder } from '@yahoo/uds-icons';
43
+ *
44
+ * <Icon name={AddFolder} variant="fill" size="sm" />
45
+ * ```
46
+ */
47
+ declare const Icon: react.ForwardRefExoticComponent<IconProps & react.RefAttributes<SVGSVGElement>>;
48
+
26
49
  type HtmlButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color' | 'name'>;
27
50
  interface PressableProps extends UniversalPressableProps, HtmlButtonProps {
28
51
  }
@@ -100,4 +123,4 @@ type VStackProps = UniversalStackProps & DivProps;
100
123
  **/
101
124
  declare const VStack: react.ForwardRefExoticComponent<UniversalStackProps & DivProps & react.RefAttributes<HTMLDivElement>>;
102
125
 
103
- export { Box as B, type DivProps as D, Pressable as P, Text as T, VStack as V, type BoxProps as a, type PressableProps as b, type TextProps as c, type VStackProps as d };
126
+ export { Box as B, type DivProps as D, Icon as I, Pressable as P, Text as T, VStack as V, type BoxProps as a, type IconProps as b, type PressableProps as c, type TextProps as d, type VStackProps as e };