@yahoo/uds 2.8.1 → 2.10.0-beta.1
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/cli/bin/uds-darwin-arm64-baseline +0 -0
- package/cli/bin/uds-linux-arm64 +0 -0
- package/cli/bin/uds-linux-x64-baseline +0 -0
- package/cli/cli.ts +1 -1
- package/cli/codemods/addCommentAboveComponents.ts +4 -79
- package/cli/codemods/flattenButtonVariant.ts +143 -0
- package/cli/codemods/propsToClass.ts +7 -12
- package/cli/codemods/remapProps.ts +13 -12
- package/cli/codemods/replaceTwClassNameWithProp.ts +99 -0
- package/cli/codemods/utils/getGlobPattern.ts +11 -0
- package/cli/codemods/utils/getImportsFromPackage.ts +21 -0
- package/cli/codemods/utils/getJSXElements.ts +9 -0
- package/cli/codemods/utils/getProject.ts +17 -0
- package/cli/codemods/utils/hasJSXProperty.ts +14 -0
- package/cli/codemods/utils/index.ts +14 -0
- package/cli/codemods/utils/matchesJSXTagName.ts +14 -0
- package/cli/commands/codemod/flattenButtonVariant.ts +16 -0
- package/cli/commands/codemod/tailwindClassesToProps.ts +24 -0
- package/cli/tailwindcss.d.ts +2 -0
- package/cli/tsconfig.json +30 -8
- package/cli/utils/configWorker.ts +2 -2
- package/cli/utils/purgeCSS.ts +13 -3
- package/cli/utils/setupConfigWorker.ts +2 -1
- package/cli/utils/sortKeys.ts +5 -0
- package/dist/{Text-UCDorZDD.d.cts → Text-DC5H-ljU.d.cts} +29 -68
- package/dist/{Text-UCDorZDD.d.ts → Text-DC5H-ljU.d.ts} +29 -68
- package/dist/VStack-dsjTgotn.d.ts +145 -0
- package/dist/VStack-lZcVQtuR.d.cts +145 -0
- package/dist/analytics/server.js +1 -1
- package/dist/chunk-3R4CMTF2.js +2 -0
- package/dist/chunk-COT7GQ26.js +2 -0
- package/dist/chunk-DIZ6AEFQ.cjs +3 -0
- package/dist/chunk-GWUSJOIP.js +2 -0
- package/dist/chunk-GXWRHF26.cjs +1 -0
- package/dist/chunk-H35WDIEH.cjs +1 -0
- package/dist/chunk-IGRY2O2E.js +2 -0
- package/dist/chunk-IQXT3UML.cjs +2 -0
- package/dist/chunk-PLVCO2Q2.cjs +1 -0
- package/dist/chunk-RCTE4OK2.cjs +1 -0
- package/dist/chunk-ROCVTVD4.js +2 -0
- package/dist/chunk-SWTZ62RF.js +3 -0
- package/dist/chunk-TYCIDVTR.js +2 -0
- package/dist/chunk-UWAIMWW7.cjs +1 -0
- package/dist/client/index.cjs +2 -2
- package/dist/client/index.d.cts +917 -25
- package/dist/client/index.d.ts +917 -25
- package/dist/client/index.js +3 -3
- package/dist/experimental/client/index.cjs +2 -2
- package/dist/experimental/client/index.d.cts +14 -49
- package/dist/experimental/client/index.d.ts +14 -49
- package/dist/experimental/client/index.js +2 -2
- package/dist/experimental/index.cjs +2 -2
- package/dist/experimental/index.d.cts +4 -55
- package/dist/experimental/index.d.ts +4 -55
- package/dist/experimental/index.js +1 -1
- package/dist/fixtures.cjs +1545 -58
- package/dist/fixtures.d.cts +33 -14
- package/dist/fixtures.d.ts +33 -14
- package/dist/fixtures.js +1518 -52
- package/dist/flags.cjs +1 -1
- package/dist/flags.js +1 -1
- package/dist/index-B1ZHRmSN.d.ts +202 -0
- package/dist/index-By9VJ9yq.d.cts +202 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +245 -128
- package/dist/index.d.ts +245 -128
- package/dist/index.js +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/tailwind/plugin.cjs +1 -2
- package/dist/tailwind/plugin.d.cts +14 -5
- package/dist/tailwind/plugin.d.ts +14 -5
- package/dist/tailwind/plugin.js +2 -2
- package/dist/tailwind/purger.cjs +2 -2
- package/dist/tailwind/purger.js +3 -3
- package/dist/tailwind/tsMorph.cjs +1 -1
- package/dist/tailwind/tsMorph.js +1 -1
- package/dist/tailwind/utils.cjs +1 -1
- package/dist/tailwind/utils.d.cts +80 -28
- package/dist/tailwind/utils.d.ts +80 -28
- package/dist/tailwind/utils.js +1 -1
- package/dist/tokens/automation/configs.cjs +1 -0
- package/dist/tokens/automation/configs.d.cts +110 -0
- package/dist/tokens/automation/configs.d.ts +110 -0
- package/dist/tokens/automation/configs.js +1 -0
- package/dist/tokens/automation/properties.cjs +1 -0
- package/dist/tokens/automation/properties.d.cts +7 -0
- package/dist/tokens/automation/properties.d.ts +7 -0
- package/dist/tokens/automation/properties.js +1 -0
- package/dist/tokens/index.cjs +1 -1
- package/dist/tokens/index.d.cts +9355 -15
- package/dist/tokens/index.d.ts +9355 -15
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/parseTokens.cjs +1 -1
- package/dist/tokens/parseTokens.d.cts +7 -23
- package/dist/tokens/parseTokens.d.ts +7 -23
- package/dist/tokens/parseTokens.js +1 -1
- package/dist/types-oxQ-ciqn.d.cts +10237 -0
- package/dist/types-oxQ-ciqn.d.ts +10237 -0
- package/package.json +18 -4
- package/cli/PropsToClass.mock.tsx +0 -14
- package/dist/chunk-3G7IRLGN.js +0 -2
- package/dist/chunk-4ZZV37J4.js +0 -2
- package/dist/chunk-5WBROFT5.cjs +0 -1
- package/dist/chunk-5WTSHH5H.js +0 -1
- package/dist/chunk-6453EQCC.cjs +0 -1
- package/dist/chunk-D5OJRMIW.cjs +0 -2
- package/dist/chunk-EYFQOFYW.cjs +0 -1
- package/dist/chunk-FWF2C6TL.cjs +0 -1
- package/dist/chunk-GIJ2FHY5.cjs +0 -1
- package/dist/chunk-KTPYM4OO.js +0 -2
- package/dist/chunk-PE2P7J44.js +0 -2
- package/dist/chunk-PSLTRJPA.cjs +0 -2
- package/dist/chunk-RNQ6TDFL.cjs +0 -2
- package/dist/chunk-SUASN3GG.js +0 -2
- package/dist/chunk-TAPL6R6T.js +0 -3
- package/dist/chunk-TFSKZAZE.cjs +0 -1
- package/dist/chunk-VPR62GYQ.js +0 -2
- package/dist/chunk-X6F5UEQ5.js +0 -2
- package/dist/types-BjtmdiGC.d.cts +0 -989
- package/dist/types-BjtmdiGC.d.ts +0 -989
- /package/dist/{motionFeatures-PRT45UQH.js → motionFeatures-25DAPVNO.js} +0 -0
- /package/dist/{motionFeatures-HQUM526D.cjs → motionFeatures-II2BNXF5.cjs} +0 -0
Binary file
|
package/cli/bin/uds-linux-arm64
CHANGED
Binary file
|
Binary file
|
package/cli/cli.ts
CHANGED
@@ -1,82 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import { IndentationText, Project } from 'ts-morph';
|
2
2
|
|
3
|
-
import {
|
4
|
-
|
5
|
-
const getProject = () => {
|
6
|
-
const workspaceDir = Bun.env.PWD;
|
7
|
-
const srcDir = path.join(workspaceDir, 'tsconfig.json');
|
8
|
-
return new Project({
|
9
|
-
tsConfigFilePath: srcDir,
|
10
|
-
manipulationSettings: { indentationText: IndentationText.TwoSpaces },
|
11
|
-
});
|
12
|
-
};
|
13
|
-
|
14
|
-
const getGlobPattern = (selectedDirs?: string[]) => {
|
15
|
-
let glob = './';
|
16
|
-
if (selectedDirs && selectedDirs.length === 1) {
|
17
|
-
glob += selectedDirs[0];
|
18
|
-
} else if (selectedDirs) {
|
19
|
-
glob += `{${selectedDirs.join(',')}}`;
|
20
|
-
}
|
21
|
-
glob += '/**/*.+(ts|tsx)';
|
22
|
-
|
23
|
-
return glob;
|
24
|
-
};
|
25
|
-
|
26
|
-
/**
|
27
|
-
* Gets the named imports being imported from `packageName` module.
|
28
|
-
* @param sourceFile Source file to search for imports in.
|
29
|
-
* @param packageName The package name to search in.
|
30
|
-
*/
|
31
|
-
const getImportsFromPackage = (sourceFile: SourceFile, packageName: string) => {
|
32
|
-
const importedComponents = new Set<string>();
|
33
|
-
|
34
|
-
// Collect imports from '@yahoo/uds'
|
35
|
-
sourceFile.getImportDeclarations().forEach((importDeclaration) => {
|
36
|
-
if (importDeclaration.getModuleSpecifierValue() === packageName) {
|
37
|
-
importDeclaration.getNamedImports().forEach((namedImport) => {
|
38
|
-
importedComponents.add(namedImport.getName());
|
39
|
-
});
|
40
|
-
}
|
41
|
-
});
|
42
|
-
|
43
|
-
return importedComponents;
|
44
|
-
};
|
45
|
-
|
46
|
-
/** Returns true if the jsx element has a `propertyName` prop. */
|
47
|
-
function hasJSXProperty(element: Node, propertyName: string) {
|
48
|
-
if (!Node.isJsxElement(element) && !Node.isJsxSelfClosingElement(element)) {
|
49
|
-
return false;
|
50
|
-
}
|
51
|
-
|
52
|
-
const openingElement = Node.isJsxElement(element) ? element.getOpeningElement() : element;
|
53
|
-
|
54
|
-
return openingElement
|
55
|
-
.getAttributes()
|
56
|
-
.some((attr) => Node.isJsxAttribute(attr) && attr.getNameNode().getText() === propertyName);
|
57
|
-
}
|
58
|
-
|
59
|
-
/** Returns true if the name of the jsx element is `tagName`. */
|
60
|
-
function matchesJSXTagName(element: Node, tagName: string) {
|
61
|
-
if (!Node.isJsxElement(element) && !Node.isJsxSelfClosingElement(element)) {
|
62
|
-
return false;
|
63
|
-
}
|
64
|
-
|
65
|
-
const tagNameNode = Node.isJsxElement(element)
|
66
|
-
? element.getOpeningElement().getTagNameNode()
|
67
|
-
: element.getTagNameNode();
|
68
|
-
|
69
|
-
return tagNameNode.getText() === tagName;
|
70
|
-
}
|
71
|
-
|
72
|
-
/** Gets the JSX elements in the source file. */
|
73
|
-
function getJSXElements(sourceFile: SourceFile) {
|
74
|
-
const jsxElements = [
|
75
|
-
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement),
|
76
|
-
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
|
77
|
-
];
|
78
|
-
return jsxElements;
|
79
|
-
}
|
3
|
+
import { getGlobPattern, getImportsFromPackage, getProject } from './utils';
|
4
|
+
import { getJSXElements, hasJSXProperty, matchesJSXTagName } from './utils';
|
80
5
|
|
81
6
|
interface LineInfo {
|
82
7
|
linePos: number;
|
@@ -138,7 +63,7 @@ export async function addCommentAboveComponents({
|
|
138
63
|
});
|
139
64
|
|
140
65
|
const updateSrc = srcLines.join('\n');
|
141
|
-
console.log(updateSrc);
|
66
|
+
// console.log(updateSrc);
|
142
67
|
sourceFile.replaceWithText(updateSrc);
|
143
68
|
}
|
144
69
|
});
|
@@ -0,0 +1,143 @@
|
|
1
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
2
|
+
|
3
|
+
import { ButtonPalette, ButtonVariant, ButtonVariantFlat } from '~/tokens';
|
4
|
+
|
5
|
+
import { getGlobPattern, getImportsFromPackage, getProject } from './utils';
|
6
|
+
|
7
|
+
const buttonFlatValMap: Record<ButtonVariantFlat, ButtonVariantFlat> = {
|
8
|
+
primary: 'primary',
|
9
|
+
secondary: 'secondary',
|
10
|
+
tertiary: 'tertiary',
|
11
|
+
brand: 'brand',
|
12
|
+
alert: 'alert',
|
13
|
+
positive: 'positive',
|
14
|
+
warning: 'warning',
|
15
|
+
info: 'info',
|
16
|
+
'brand-secondary': 'brand-secondary',
|
17
|
+
'brand-tertiary': 'brand-tertiary',
|
18
|
+
'alert-secondary': 'alert-secondary',
|
19
|
+
'alert-tertiary': 'alert-tertiary',
|
20
|
+
'positive-secondary': 'positive-secondary',
|
21
|
+
'positive-tertiary': 'positive-tertiary',
|
22
|
+
'warning-secondary': 'warning-secondary',
|
23
|
+
'warning-tertiary': 'warning-tertiary',
|
24
|
+
'info-secondary': 'info-secondary',
|
25
|
+
'info-tertiary': 'info-tertiary',
|
26
|
+
};
|
27
|
+
|
28
|
+
// This is a hack to typecheck the keys of the object, ensure that all of the values are present
|
29
|
+
// NOTE: Importing fixtures still causes issues
|
30
|
+
const buttonVariantsFlat = Object.values(buttonFlatValMap);
|
31
|
+
|
32
|
+
function parseButtonVariantFlat({
|
33
|
+
palette = 'accent',
|
34
|
+
variant = 'primary',
|
35
|
+
}: {
|
36
|
+
palette?: ButtonPalette;
|
37
|
+
variant?: ButtonVariant;
|
38
|
+
}): ButtonVariantFlat {
|
39
|
+
if (palette === 'accent') {
|
40
|
+
return variant; // primary, secondary, tertiary
|
41
|
+
}
|
42
|
+
|
43
|
+
if (variant === 'primary') {
|
44
|
+
return palette; // brand, alert, positive, warning
|
45
|
+
}
|
46
|
+
|
47
|
+
return `${palette}-${variant}`; // brand-secondary, alert-tertiary, etc.
|
48
|
+
}
|
49
|
+
|
50
|
+
interface Options {
|
51
|
+
selectedDirs?: string[];
|
52
|
+
project?: Project;
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Flatten palette and variant into a single variant prop for UDS Button and IconButton.
|
57
|
+
*/
|
58
|
+
export async function flattenButtonVariant({ selectedDirs, project = getProject() }: Options) {
|
59
|
+
const glob = getGlobPattern(selectedDirs);
|
60
|
+
const sourceFiles = project.getSourceFiles(glob);
|
61
|
+
|
62
|
+
for (const sourceFile of sourceFiles) {
|
63
|
+
// Get UDS imports from '@yahoo/uds'
|
64
|
+
const importUdsComponents = getImportsFromPackage(sourceFile, '@yahoo/uds');
|
65
|
+
|
66
|
+
// Only process files that import Button or IconButton
|
67
|
+
if (!importUdsComponents.has('Button') && !importUdsComponents.has('IconButton')) {
|
68
|
+
continue;
|
69
|
+
}
|
70
|
+
|
71
|
+
// Process both JsxOpeningElement and JsxSelfClosingElement nodes.
|
72
|
+
const jsxElements = [
|
73
|
+
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
|
74
|
+
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
|
75
|
+
].filter((el) => {
|
76
|
+
const tagName = el.getTagNameNode().getText();
|
77
|
+
return (tagName === 'Button' || tagName === 'IconButton') && importUdsComponents.has(tagName);
|
78
|
+
});
|
79
|
+
|
80
|
+
jsxElements.forEach((jsxEl) => {
|
81
|
+
// Get palette and variant attributes
|
82
|
+
const paletteAttr = jsxEl.getAttribute('palette');
|
83
|
+
const variantAttr = jsxEl.getAttribute('variant');
|
84
|
+
|
85
|
+
// Prepare to capture literal values.
|
86
|
+
let paletteLiteral: string | undefined;
|
87
|
+
let variantLiteral: string;
|
88
|
+
|
89
|
+
// Process palette attribute
|
90
|
+
if (paletteAttr) {
|
91
|
+
// @ts-expect-error - getInitializer() is not in the type definition
|
92
|
+
const initializer = paletteAttr.getInitializer();
|
93
|
+
if (initializer && initializer.getKind() === SyntaxKind.StringLiteral) {
|
94
|
+
paletteLiteral = initializer.getText().slice(1, -1);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// Process variant attribute
|
99
|
+
if (variantAttr) {
|
100
|
+
// @ts-expect-error - getInitializer() is not in the type definition
|
101
|
+
const initializer = variantAttr.getInitializer();
|
102
|
+
if (initializer && initializer.getKind() === SyntaxKind.StringLiteral) {
|
103
|
+
variantLiteral = initializer.getText().slice(1, -1);
|
104
|
+
} else {
|
105
|
+
variantLiteral = 'primary';
|
106
|
+
}
|
107
|
+
} else {
|
108
|
+
variantLiteral = 'primary';
|
109
|
+
}
|
110
|
+
|
111
|
+
// Remove the old palette and variant attributes.
|
112
|
+
if (paletteAttr) {
|
113
|
+
paletteAttr.remove();
|
114
|
+
}
|
115
|
+
if (variantAttr) {
|
116
|
+
variantAttr.remove();
|
117
|
+
}
|
118
|
+
|
119
|
+
if (!paletteLiteral && buttonVariantsFlat.includes(variantLiteral as ButtonVariantFlat)) {
|
120
|
+
// If the variant is already a valid flattened value, use it as is.
|
121
|
+
if (variantLiteral !== 'primary') {
|
122
|
+
// If its "primary", just remove all the props
|
123
|
+
jsxEl.addAttribute({
|
124
|
+
name: 'variant',
|
125
|
+
initializer: `"${variantLiteral}"`,
|
126
|
+
});
|
127
|
+
}
|
128
|
+
} else {
|
129
|
+
// Compute the flattened variant using the parsing function.
|
130
|
+
const flattened = parseButtonVariantFlat({
|
131
|
+
palette: paletteLiteral as ButtonPalette,
|
132
|
+
variant: variantLiteral as ButtonVariant,
|
133
|
+
});
|
134
|
+
|
135
|
+
jsxEl.addAttribute({
|
136
|
+
name: 'variant',
|
137
|
+
initializer: `"${flattened}"`,
|
138
|
+
});
|
139
|
+
}
|
140
|
+
});
|
141
|
+
}
|
142
|
+
await project.save();
|
143
|
+
}
|
@@ -11,6 +11,8 @@ import {
|
|
11
11
|
SyntaxKind,
|
12
12
|
} from 'ts-morph';
|
13
13
|
|
14
|
+
import { getGlobPattern } from './utils';
|
15
|
+
|
14
16
|
const throwComplexExpressionWarning = ({
|
15
17
|
attr,
|
16
18
|
element,
|
@@ -70,6 +72,11 @@ const throwComplexExpressionWarning = ({
|
|
70
72
|
|
71
73
|
export const getProject = () => {
|
72
74
|
const workspaceDir = Bun.env.PWD;
|
75
|
+
|
76
|
+
if (!workspaceDir) {
|
77
|
+
throw new Error('Workspace directory not found.');
|
78
|
+
}
|
79
|
+
|
73
80
|
const srcDir = path.join(workspaceDir, 'tsconfig.json');
|
74
81
|
return new Project({
|
75
82
|
tsConfigFilePath: srcDir,
|
@@ -77,18 +84,6 @@ export const getProject = () => {
|
|
77
84
|
});
|
78
85
|
};
|
79
86
|
|
80
|
-
const getGlobPattern = (selectedDirs?: string[]) => {
|
81
|
-
let glob = './';
|
82
|
-
if (selectedDirs && selectedDirs.length === 1) {
|
83
|
-
glob += selectedDirs[0];
|
84
|
-
} else if (selectedDirs) {
|
85
|
-
glob += `{${selectedDirs.join(',')}}`;
|
86
|
-
}
|
87
|
-
glob += '/**/*.+(ts|tsx)';
|
88
|
-
|
89
|
-
return glob;
|
90
|
-
};
|
91
|
-
|
92
87
|
interface PropToClassMap {
|
93
88
|
selectedDirs?: string[];
|
94
89
|
project?: Project;
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import path from 'node:path';
|
2
|
-
|
3
1
|
import { green, print, red } from 'bluebun';
|
4
|
-
import {
|
2
|
+
import { SyntaxKind } from 'ts-morph';
|
3
|
+
|
4
|
+
import { getGlobPattern, getProject } from './utils';
|
5
5
|
|
6
6
|
interface RemapPropsOptions {
|
7
7
|
/** Properties to remap values for */
|
@@ -15,14 +15,8 @@ interface RemapPropsOptions {
|
|
15
15
|
* For example, a UDS component with a prop of `spacing="6"` should be updated to `spacing="4"`.
|
16
16
|
*/
|
17
17
|
export async function remapProps({ properties, oldToNewMap }: RemapPropsOptions) {
|
18
|
-
const
|
19
|
-
const
|
20
|
-
const project = new Project({
|
21
|
-
tsConfigFilePath: srcDir,
|
22
|
-
manipulationSettings: { indentationText: IndentationText.TwoSpaces },
|
23
|
-
});
|
24
|
-
|
25
|
-
const sourceFiles = project.getSourceFiles(`${workspaceDir}/**/*.{ts,tsx}`);
|
18
|
+
const project = getProject();
|
19
|
+
const sourceFiles = project.getSourceFiles(getGlobPattern());
|
26
20
|
|
27
21
|
for (const sourceFile of sourceFiles) {
|
28
22
|
const jsxOpeningElement = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
|
@@ -51,7 +45,14 @@ export async function remapProps({ properties, oldToNewMap }: RemapPropsOptions)
|
|
51
45
|
if (newValue) {
|
52
46
|
const oldLog = red(`${propName}=${oldValue}`);
|
53
47
|
const newLog = green(`${propName}=${newValue}`);
|
54
|
-
|
48
|
+
|
49
|
+
const workspaceDir = Bun.env.PWD;
|
50
|
+
|
51
|
+
if (!workspaceDir) {
|
52
|
+
throw new Error('Workspace directory not found.');
|
53
|
+
}
|
54
|
+
|
55
|
+
const relativePath = sourceFile.getFilePath().replace(workspaceDir, '');
|
55
56
|
print(`${relativePath} ${oldLog} -> ${newLog}`);
|
56
57
|
attribute.set({ name: propName, initializer: `"${newValue}"` });
|
57
58
|
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import { Node, Project, SyntaxKind } from 'ts-morph';
|
2
|
+
|
3
|
+
import { getGlobPattern, getImportsFromPackage, getProject } from './utils';
|
4
|
+
|
5
|
+
const getGlob = (selectedDirs?: string[]) => {
|
6
|
+
const glob = getGlobPattern(selectedDirs);
|
7
|
+
return [glob, '!**/*.d.ts', '!**/*/{vitest,tailwind,uds,site}.config.ts'];
|
8
|
+
};
|
9
|
+
|
10
|
+
interface Options {
|
11
|
+
/** Class names to replace with properties */
|
12
|
+
replacements: Record<string, Record<string, string>>;
|
13
|
+
/** Package to look in. @default '@yahoo/uds' */
|
14
|
+
packageName?: string;
|
15
|
+
selectedDirs?: string[];
|
16
|
+
project?: Project;
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Replace tailwind classNames on UDS components with the equivalent UDS prop.
|
21
|
+
* Example: `w-full` -> `width="full"`.
|
22
|
+
*/
|
23
|
+
export async function replaceTwClassNameWithProp({
|
24
|
+
selectedDirs,
|
25
|
+
packageName = '@yahoo/uds',
|
26
|
+
project = getProject(),
|
27
|
+
replacements,
|
28
|
+
}: Options) {
|
29
|
+
const glob = getGlob(selectedDirs);
|
30
|
+
const sourceFiles = project.getSourceFiles(glob);
|
31
|
+
|
32
|
+
for (const sourceFile of sourceFiles) {
|
33
|
+
const importUdsComponents = getImportsFromPackage(sourceFile, packageName);
|
34
|
+
|
35
|
+
// Find all nodes of JsxAttribute kind and filter for className attributes
|
36
|
+
const jsxAttributes = sourceFile
|
37
|
+
.getDescendantsOfKind(SyntaxKind.JsxAttribute)
|
38
|
+
.filter((node) => node.getNameNode().getText() === 'className');
|
39
|
+
|
40
|
+
// Process each className attribute
|
41
|
+
jsxAttributes.forEach((classNameAttr) => {
|
42
|
+
const initializer = classNameAttr.getInitializer();
|
43
|
+
if (!initializer) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
// Navigate up to find JsxOpeningElement
|
48
|
+
const ancestors = classNameAttr.getAncestors();
|
49
|
+
const jsxOpeningElement = ancestors.find((node) => Node.isJsxOpeningElement(node));
|
50
|
+
|
51
|
+
if (!jsxOpeningElement) {
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
|
55
|
+
// Only modify UDS components.
|
56
|
+
const componentName = jsxOpeningElement.getTagNameNode().getText();
|
57
|
+
const wasImportedFromUDS = importUdsComponents.has(componentName);
|
58
|
+
if (!wasImportedFromUDS) {
|
59
|
+
return;
|
60
|
+
}
|
61
|
+
|
62
|
+
// Get the className value
|
63
|
+
const classNameValue = initializer.getText().replace(/['"]/g, '');
|
64
|
+
const componentClasses = classNameValue.split(' ');
|
65
|
+
|
66
|
+
const replaceList = Object.keys(replacements);
|
67
|
+
const classesToSwitchToProps = new Set();
|
68
|
+
const remainingClasses = componentClasses
|
69
|
+
.filter((c) => {
|
70
|
+
if (replaceList.includes(c)) {
|
71
|
+
classesToSwitchToProps.add(c);
|
72
|
+
}
|
73
|
+
return !classesToSwitchToProps.has(c);
|
74
|
+
})
|
75
|
+
.join(' ');
|
76
|
+
|
77
|
+
// Remove the tw classes from the element which are listed in the replacements.
|
78
|
+
if (remainingClasses) {
|
79
|
+
classNameAttr.setInitializer(`"${remainingClasses}"`);
|
80
|
+
} else {
|
81
|
+
classNameAttr.remove();
|
82
|
+
}
|
83
|
+
|
84
|
+
// For each class that was replaced, add the corresponding prop.
|
85
|
+
for (const item of Object.entries(replacements)) {
|
86
|
+
const [prop, val] = Object.entries(item[1])[0];
|
87
|
+
|
88
|
+
if (classesToSwitchToProps.has(item[0])) {
|
89
|
+
jsxOpeningElement.insertAttribute(0, {
|
90
|
+
name: prop,
|
91
|
+
initializer: `"${val}"`,
|
92
|
+
});
|
93
|
+
}
|
94
|
+
}
|
95
|
+
});
|
96
|
+
}
|
97
|
+
|
98
|
+
await project.save();
|
99
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
export const getGlobPattern = (selectedDirs?: string[]) => {
|
2
|
+
let glob = './';
|
3
|
+
if (selectedDirs && selectedDirs.length === 1) {
|
4
|
+
glob += selectedDirs[0];
|
5
|
+
} else if (selectedDirs) {
|
6
|
+
glob += `{${selectedDirs.join(',')}}`;
|
7
|
+
}
|
8
|
+
glob += '/**/*.+(ts|tsx)';
|
9
|
+
|
10
|
+
return glob;
|
11
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { SourceFile } from 'ts-morph';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Gets the named imports being imported from `packageName` module.
|
5
|
+
* @param sourceFile Source file to search for imports in.
|
6
|
+
* @param packageName The package name to search in.
|
7
|
+
*/
|
8
|
+
export const getImportsFromPackage = (sourceFile: SourceFile, packageName: string) => {
|
9
|
+
const importedComponents = new Set<string>();
|
10
|
+
|
11
|
+
// Collect imports from '@yahoo/uds'
|
12
|
+
sourceFile.getImportDeclarations().forEach((importDeclaration) => {
|
13
|
+
if (importDeclaration.getModuleSpecifierValue() === packageName) {
|
14
|
+
importDeclaration.getNamedImports().forEach((namedImport) => {
|
15
|
+
importedComponents.add(namedImport.getName());
|
16
|
+
});
|
17
|
+
}
|
18
|
+
});
|
19
|
+
|
20
|
+
return importedComponents;
|
21
|
+
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { SourceFile, SyntaxKind } from 'ts-morph';
|
2
|
+
|
3
|
+
/** Gets the JSX elements in the source file. */
|
4
|
+
export function getJSXElements(sourceFile: SourceFile) {
|
5
|
+
return [
|
6
|
+
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement),
|
7
|
+
...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
|
8
|
+
];
|
9
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import path from 'node:path';
|
2
|
+
|
3
|
+
import { IndentationText, Project } from 'ts-morph';
|
4
|
+
|
5
|
+
export const getProject = () => {
|
6
|
+
const workspaceDir = Bun.env.PWD;
|
7
|
+
|
8
|
+
if (!workspaceDir) {
|
9
|
+
throw new Error('Workspace directory not found.');
|
10
|
+
}
|
11
|
+
|
12
|
+
const srcDir = path.join(workspaceDir, 'tsconfig.json');
|
13
|
+
return new Project({
|
14
|
+
tsConfigFilePath: srcDir,
|
15
|
+
manipulationSettings: { indentationText: IndentationText.TwoSpaces },
|
16
|
+
});
|
17
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { Node } from 'ts-morph';
|
2
|
+
|
3
|
+
/** Returns true if the jsx element has a `propertyName` prop. */
|
4
|
+
export function hasJSXProperty(element: Node, propertyName: string) {
|
5
|
+
if (!Node.isJsxElement(element) && !Node.isJsxSelfClosingElement(element)) {
|
6
|
+
return false;
|
7
|
+
}
|
8
|
+
|
9
|
+
const openingElement = Node.isJsxElement(element) ? element.getOpeningElement() : element;
|
10
|
+
|
11
|
+
return openingElement
|
12
|
+
.getAttributes()
|
13
|
+
.some((attr) => Node.isJsxAttribute(attr) && attr.getNameNode().getText() === propertyName);
|
14
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/**
|
2
|
+
* ------------------- GENERATED | DO NOT MODIFY THIS SECTION ------------------
|
3
|
+
* This file uses eslint-plugin-codegen to automatically watch for changes in
|
4
|
+
* the `src/components` directory and updates this barrel file.
|
5
|
+
*/
|
6
|
+
// codegen:start {preset: barrel, exclude: ['*.*.*']}
|
7
|
+
export * from './getGlobPattern';
|
8
|
+
export * from './getImportsFromPackage';
|
9
|
+
export * from './getJSXElements';
|
10
|
+
export * from './getProject';
|
11
|
+
export * from './hasJSXProperty';
|
12
|
+
export * from './matchesJSXTagName';
|
13
|
+
export * from './sizingPropToClassMap';
|
14
|
+
// codegen:end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { Node } from 'ts-morph';
|
2
|
+
|
3
|
+
/** Returns true if the name of the jsx element is `tagName`. */
|
4
|
+
export function matchesJSXTagName(element: Node, tagName: string) {
|
5
|
+
if (!Node.isJsxElement(element) && !Node.isJsxSelfClosingElement(element)) {
|
6
|
+
return false;
|
7
|
+
}
|
8
|
+
|
9
|
+
const tagNameNode = Node.isJsxElement(element)
|
10
|
+
? element.getOpeningElement().getTagNameNode()
|
11
|
+
: element.getTagNameNode();
|
12
|
+
|
13
|
+
return tagNameNode.getText() === tagName;
|
14
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { type Props, spinStart, spinStop } from 'bluebun';
|
2
|
+
|
3
|
+
import { flattenButtonVariant } from '../../codemods/flattenButtonVariant';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
name: 'flattenButtonVariant',
|
7
|
+
description: `Flatten deprecated button props into single variant prop`,
|
8
|
+
|
9
|
+
run: async (props: Props & { selectedDirs?: string[] }) => {
|
10
|
+
spinStart('Running codemod...');
|
11
|
+
|
12
|
+
await flattenButtonVariant({ selectedDirs: props.selectedDirs });
|
13
|
+
|
14
|
+
spinStop('Codemod complete! Peek the diff and commit your changes!');
|
15
|
+
},
|
16
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { type Props, spinStart, spinStop } from 'bluebun';
|
2
|
+
|
3
|
+
import { replaceTwClassNameWithProp } from '../../codemods/replaceTwClassNameWithProp';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
name: 'tailwindClassesToProps',
|
7
|
+
description: `Converts tailwind classes to UDS props (e.g. w-full => width="full").`,
|
8
|
+
|
9
|
+
run: async (props: Props & { selectedDirs?: string[] }) => {
|
10
|
+
spinStart('Running codemod...');
|
11
|
+
await replaceTwClassNameWithProp({
|
12
|
+
selectedDirs: props.selectedDirs,
|
13
|
+
replacements: {
|
14
|
+
'w-fit': { width: 'fit' },
|
15
|
+
'w-full': { width: 'full' },
|
16
|
+
'w-screen': { width: 'screen' },
|
17
|
+
'h-full': { height: 'full' },
|
18
|
+
'h-screen': { height: 'screen' },
|
19
|
+
'h-fit': { height: 'fit' },
|
20
|
+
},
|
21
|
+
});
|
22
|
+
spinStop('Codemod complete! Peek the diff and commit your changes!');
|
23
|
+
},
|
24
|
+
};
|
package/cli/tsconfig.json
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"$schema": "https://json.schemastore.org/tsconfig",
|
3
3
|
"display": "Default",
|
4
|
-
"include": [
|
5
|
-
|
4
|
+
"include": [
|
5
|
+
"."
|
6
|
+
],
|
7
|
+
"exclude": [
|
8
|
+
"node_modules"
|
9
|
+
],
|
6
10
|
"compilerOptions": {
|
7
11
|
"composite": false,
|
8
12
|
"declaration": true,
|
@@ -16,16 +20,34 @@
|
|
16
20
|
"preserveWatchOutput": true,
|
17
21
|
"skipLibCheck": true,
|
18
22
|
"strict": true,
|
19
|
-
"types": [
|
23
|
+
"types": [
|
24
|
+
"bun-types"
|
25
|
+
],
|
20
26
|
"target": "esnext",
|
21
|
-
"lib": [
|
27
|
+
"lib": [
|
28
|
+
"ESNext"
|
29
|
+
],
|
22
30
|
"module": "esnext",
|
23
31
|
"moduleResolution": "bundler",
|
24
32
|
"paths": {
|
25
|
-
"
|
26
|
-
|
27
|
-
|
28
|
-
"
|
33
|
+
"~/*": [
|
34
|
+
"../src/*"
|
35
|
+
],
|
36
|
+
"@yahoo/uds/scripts/*": [
|
37
|
+
"../scripts/*"
|
38
|
+
],
|
39
|
+
// "@yahoo/uds/tokens": [
|
40
|
+
// "../src/tokens/index.ts"
|
41
|
+
// ],
|
42
|
+
"@yahoo/uds/tailwind/purger": [
|
43
|
+
"../src/tailwind/purger/index.ts"
|
44
|
+
],
|
45
|
+
"@yahoo/uds/tailwind/tsMorph": [
|
46
|
+
"../scripts/utils/tsMorph.ts"
|
47
|
+
],
|
48
|
+
"root/*": [
|
49
|
+
"../../*"
|
50
|
+
]
|
29
51
|
}
|
30
52
|
}
|
31
53
|
}
|
@@ -8,7 +8,7 @@ import { eq, gte } from 'semver';
|
|
8
8
|
import { ConfigWorkerThreadMessageEvent } from './types';
|
9
9
|
|
10
10
|
const PRODUCTION_PREFIX = 'https://config.uds.build';
|
11
|
-
const STAGING_PREFIX = 'https://staging.uds.build';
|
11
|
+
const STAGING_PREFIX = 'https://staging.config.uds.build';
|
12
12
|
const LOCAL_PORT = process.env.PORT || 4001;
|
13
13
|
const LOCAL_PREFIX = `http://localhost:${LOCAL_PORT}`;
|
14
14
|
|
@@ -16,7 +16,7 @@ let CONFIG_ENDPOINT = `${PRODUCTION_PREFIX}/api/config`;
|
|
16
16
|
|
17
17
|
// Setup auth bypass for Vercel preview deployments (staging)
|
18
18
|
const PROTECTION_BYPASS = process.env.VERCEL_PROTECTION_BYPASS ?? '';
|
19
|
-
const FETCH_REQUEST_INIT:
|
19
|
+
const FETCH_REQUEST_INIT: BunFetchRequestInit = {
|
20
20
|
headers: {
|
21
21
|
'x-vercel-protection-bypass': PROTECTION_BYPASS,
|
22
22
|
},
|