@reapit/elements 5.0.0-beta.71 → 5.0.0-beta.72
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/bin/elements.cjs +3 -5
- package/dist/codemods/at-a-glance-article-card/transform.d.ts +4 -0
- package/dist/codemods/at-a-glance-article-card/transform.d.ts.map +1 -0
- package/dist/codemods/at-a-glance-article-card/transform.js +223 -0
- package/dist/codemods/at-a-glance-article-card/transform.js.map +1 -0
- package/dist/codemods/bin.d.ts +2 -0
- package/dist/codemods/bin.d.ts.map +1 -0
- package/dist/codemods/bin.js +140 -0
- package/dist/codemods/bin.js.map +1 -0
- package/dist/codemods/codemods.d.ts +29 -0
- package/dist/codemods/codemods.d.ts.map +1 -0
- package/dist/codemods/codemods.js +32 -0
- package/dist/codemods/codemods.js.map +1 -0
- package/{codemods → dist/codemods}/manifest.json +1 -1
- package/dist/codemods/runner.d.ts +21 -0
- package/dist/codemods/runner.d.ts.map +1 -0
- package/dist/codemods/runner.js +165 -0
- package/dist/codemods/runner.js.map +1 -0
- package/package.json +6 -5
- package/codemods/__tests__/codemods.test.ts +0 -178
- package/codemods/__tests__/generate-manifest.test.ts +0 -240
- package/codemods/__tests__/readme-parser.test.ts +0 -218
- package/codemods/__tests__/runner.test.ts +0 -530
- package/codemods/at-a-glance-article-card/README.md +0 -122
- package/codemods/at-a-glance-article-card/__tests__/transform.test.ts +0 -390
- package/codemods/at-a-glance-article-card/transform.ts +0 -291
- package/codemods/bin.ts +0 -205
- package/codemods/codemods.ts +0 -75
- package/codemods/generate-manifest.ts +0 -120
- package/codemods/manifest.schema.json +0 -39
- package/codemods/readme-parser.ts +0 -37
- package/codemods/runner.ts +0 -196
package/bin/elements.cjs
CHANGED
|
@@ -35,11 +35,9 @@ For more information, visit: https://github.com/reapit/elements
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function handleCodemod(codemodArgs) {
|
|
38
|
-
const child = spawn(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{ stdio: 'inherit' },
|
|
42
|
-
)
|
|
38
|
+
const child = spawn(process.execPath, [join(__dirname, '..', 'dist', 'codemods', 'bin.js'), ...codemodArgs], {
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
})
|
|
43
41
|
|
|
44
42
|
child.on('exit', (code) => {
|
|
45
43
|
process.exit(code ?? 0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../codemods/at-a-glance-article-card/transform.ts"],"names":[],"mappings":"AA6QA,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAmB,EAC7B,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC,MAAM,CAiBR"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a module specifier matches a package name.
|
|
4
|
+
* Handles both exact matches and subpath imports.
|
|
5
|
+
* @example
|
|
6
|
+
* matchesPackage('@company/ui', '@company/ui') // true
|
|
7
|
+
* matchesPackage('@company/ui/elements', '@company/ui') // true
|
|
8
|
+
* matchesPackage('@company/ui-v2', '@company/ui') // false
|
|
9
|
+
*/
|
|
10
|
+
function matchesPackage(moduleSpecifier, packageName) {
|
|
11
|
+
return moduleSpecifier === packageName || moduleSpecifier.startsWith(packageName + '/');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Checks if a module specifier is an import from @reapit/elements or a facade package.
|
|
15
|
+
*/
|
|
16
|
+
function isElementsImport(moduleSpecifier, facadePackage) {
|
|
17
|
+
return (matchesPackage(moduleSpecifier, '@reapit/elements') ||
|
|
18
|
+
(facadePackage !== undefined && matchesPackage(moduleSpecifier, facadePackage)));
|
|
19
|
+
}
|
|
20
|
+
function getTagName(element) {
|
|
21
|
+
return element.getTagNameNode().getText();
|
|
22
|
+
}
|
|
23
|
+
function isNamespacedComponent(element, componentName) {
|
|
24
|
+
return getTagName(element) === `AtAGlance.${componentName}`;
|
|
25
|
+
}
|
|
26
|
+
function hasProp(element, propName) {
|
|
27
|
+
const attributes = element.getAttributes();
|
|
28
|
+
return attributes.some((attr) => {
|
|
29
|
+
if (attr.getKind() !== SyntaxKind.JsxAttribute) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const jsxAttr = attr.asKindOrThrow(SyntaxKind.JsxAttribute);
|
|
33
|
+
const nameNode = jsxAttr.getNameNode();
|
|
34
|
+
return nameNode.getText() === propName;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function hasChildren(element) {
|
|
38
|
+
// Self-closing elements have no children
|
|
39
|
+
if (element.getKind() === SyntaxKind.JsxSelfClosingElement) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// For opening elements, check the parent JsxElement's children
|
|
43
|
+
const parent = element.getParent();
|
|
44
|
+
if (parent?.getKind() === SyntaxKind.JsxElement) {
|
|
45
|
+
const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement);
|
|
46
|
+
const children = jsxElement.getJsxChildren();
|
|
47
|
+
// Filter out whitespace-only text
|
|
48
|
+
return children.some((child) => {
|
|
49
|
+
if (child.getKind() === SyntaxKind.JsxText) {
|
|
50
|
+
return child.getText().trim().length > 0;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
function isUsingOldApi(element) {
|
|
58
|
+
// New API uses subcomponents as children or grid prop
|
|
59
|
+
if (hasChildren(element) || hasProp(element, 'grid')) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// Old API has displayValue or label props, or is completely empty (no props, no children)
|
|
63
|
+
// Empty cards should be migrated to ArticleCard (will cause TS errors, but that's expected)
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function renameTagTo(element, newTagName) {
|
|
67
|
+
const tagNameNode = element.getTagNameNode();
|
|
68
|
+
tagNameNode.replaceWithText(newTagName);
|
|
69
|
+
}
|
|
70
|
+
function transformJsxElements(sourceFile, atAGlanceCardAliases) {
|
|
71
|
+
// Two-pass transformation approach:
|
|
72
|
+
// Pass 1: Transform namespaced components (AtAGlance.Card -> AtAGlance.ArticleCard)
|
|
73
|
+
// Pass 2: Transform direct imports (AtAGlanceCard -> AtAGlance.ArticleCard)
|
|
74
|
+
//
|
|
75
|
+
// We cannot do this in a single pass because mutating the AST while iterating
|
|
76
|
+
// over it can cause nodes to be invalidated or missed. By collecting all elements
|
|
77
|
+
// first, then mutating, we avoid iterator invalidation issues.
|
|
78
|
+
// Pass 1: Process namespaced AtAGlance.Card components
|
|
79
|
+
const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
|
|
80
|
+
const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
|
|
81
|
+
// Transform namespaced AtAGlance.Card to AtAGlance.ArticleCard when using old API
|
|
82
|
+
for (const element of [...selfClosingElements, ...openingElements]) {
|
|
83
|
+
if (isNamespacedComponent(element, 'Card') && isUsingOldApi(element)) {
|
|
84
|
+
renameTagTo(element, 'AtAGlance.ArticleCard');
|
|
85
|
+
// Also rename closing tag for non-self-closing elements
|
|
86
|
+
if (element.getKind() === SyntaxKind.JsxOpeningElement) {
|
|
87
|
+
const parent = element.getParent();
|
|
88
|
+
if (parent?.getKind() === SyntaxKind.JsxElement) {
|
|
89
|
+
const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement);
|
|
90
|
+
const closingElement = jsxElement.getClosingElement();
|
|
91
|
+
if (closingElement) {
|
|
92
|
+
closingElement.getTagNameNode().replaceWithText('AtAGlance.ArticleCard');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Pass 2: Re-fetch elements after Pass 1 mutations, then transform direct imports
|
|
99
|
+
const selfClosingElements2 = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
|
|
100
|
+
const openingElements2 = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
|
|
101
|
+
// Transform direct AtAGlanceCard (or its aliases) to AtAGlance.ArticleCard when using old API
|
|
102
|
+
for (const element of [...selfClosingElements2, ...openingElements2]) {
|
|
103
|
+
const tagName = getTagName(element);
|
|
104
|
+
if (atAGlanceCardAliases.has(tagName) && isUsingOldApi(element)) {
|
|
105
|
+
renameTagTo(element, 'AtAGlance.ArticleCard');
|
|
106
|
+
// Also rename closing tag for non-self-closing elements
|
|
107
|
+
if (element.getKind() === SyntaxKind.JsxOpeningElement) {
|
|
108
|
+
const parent = element.getParent();
|
|
109
|
+
if (parent?.getKind() === SyntaxKind.JsxElement) {
|
|
110
|
+
const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement);
|
|
111
|
+
const closingElement = jsxElement.getClosingElement();
|
|
112
|
+
if (closingElement) {
|
|
113
|
+
closingElement.getTagNameNode().replaceWithText('AtAGlance.ArticleCard');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function getAtAGlanceCardAliases(sourceFile, facadePackage) {
|
|
121
|
+
const aliases = new Set();
|
|
122
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
123
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
124
|
+
if (!isElementsImport(moduleSpecifier, facadePackage))
|
|
125
|
+
continue;
|
|
126
|
+
for (const namedImport of importDecl.getNamedImports()) {
|
|
127
|
+
if (namedImport.getName() === 'AtAGlanceCard') {
|
|
128
|
+
// Get the alias if it exists, otherwise use the original name
|
|
129
|
+
// Only add the alias (or original name) that's actually used in the file
|
|
130
|
+
const alias = namedImport.getAliasNode()?.getText();
|
|
131
|
+
aliases.add(alias ?? 'AtAGlanceCard');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Add default only if file has NO imports at all (handles test snippets without imports)
|
|
136
|
+
if (aliases.size === 0 && sourceFile.getImportDeclarations().length === 0) {
|
|
137
|
+
aliases.add('AtAGlanceCard');
|
|
138
|
+
}
|
|
139
|
+
return aliases;
|
|
140
|
+
}
|
|
141
|
+
function isAtAGlanceCardStillUsed(sourceFile, aliases) {
|
|
142
|
+
const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
|
|
143
|
+
const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
|
|
144
|
+
return [...selfClosingElements, ...openingElements].some((element) => {
|
|
145
|
+
const tagName = getTagName(element);
|
|
146
|
+
return aliases.has(tagName);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function hasAtAGlanceImport(sourceFile, facadePackage) {
|
|
150
|
+
return sourceFile.getImportDeclarations().some((importDecl) => {
|
|
151
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
152
|
+
if (!isElementsImport(moduleSpecifier, facadePackage))
|
|
153
|
+
return false;
|
|
154
|
+
return importDecl.getNamedImports().some((namedImport) => namedImport.getName() === 'AtAGlance');
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function usesAtAGlanceNamespace(sourceFile) {
|
|
158
|
+
const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
|
|
159
|
+
const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);
|
|
160
|
+
return [...selfClosingElements, ...openingElements].some((element) => getTagName(element).startsWith('AtAGlance.'));
|
|
161
|
+
}
|
|
162
|
+
function updateImports(sourceFile, atAGlanceCardAliases, facadePackage) {
|
|
163
|
+
const importDeclarations = sourceFile.getImportDeclarations();
|
|
164
|
+
const atAGlanceCardStillUsed = isAtAGlanceCardStillUsed(sourceFile, atAGlanceCardAliases);
|
|
165
|
+
const needsAtAGlanceImport = usesAtAGlanceNamespace(sourceFile) && !hasAtAGlanceImport(sourceFile, facadePackage);
|
|
166
|
+
let importDeclWhereAtAGlanceCardWasRemoved = null;
|
|
167
|
+
// First pass: Remove AtAGlanceCard imports and track where it was removed
|
|
168
|
+
for (const importDecl of importDeclarations) {
|
|
169
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
170
|
+
if (!isElementsImport(moduleSpecifier, facadePackage))
|
|
171
|
+
continue;
|
|
172
|
+
if (!atAGlanceCardStillUsed) {
|
|
173
|
+
const namedImports = importDecl.getNamedImports();
|
|
174
|
+
for (const namedImport of namedImports) {
|
|
175
|
+
if (namedImport.getName() === 'AtAGlanceCard') {
|
|
176
|
+
namedImport.remove();
|
|
177
|
+
importDeclWhereAtAGlanceCardWasRemoved = importDecl;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Second pass: Add AtAGlance import to the same declaration where AtAGlanceCard was removed
|
|
183
|
+
// Do this BEFORE removing empty imports to avoid accessing removed nodes
|
|
184
|
+
if (needsAtAGlanceImport && importDeclWhereAtAGlanceCardWasRemoved) {
|
|
185
|
+
importDeclWhereAtAGlanceCardWasRemoved.addNamedImport('AtAGlance');
|
|
186
|
+
}
|
|
187
|
+
else if (needsAtAGlanceImport) {
|
|
188
|
+
// If we didn't find where AtAGlanceCard was removed, add to first elements import
|
|
189
|
+
for (const importDecl of importDeclarations) {
|
|
190
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
191
|
+
if (isElementsImport(moduleSpecifier, facadePackage)) {
|
|
192
|
+
importDecl.addNamedImport('AtAGlance');
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Third pass: Remove empty import declarations
|
|
198
|
+
for (const importDecl of importDeclarations) {
|
|
199
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
200
|
+
if (!isElementsImport(moduleSpecifier, facadePackage))
|
|
201
|
+
continue;
|
|
202
|
+
if (importDecl.getNamedImports().length === 0 &&
|
|
203
|
+
!importDecl.getDefaultImport() &&
|
|
204
|
+
!importDecl.getNamespaceImport()) {
|
|
205
|
+
importDecl.remove();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
export default function transform(source, filePath = 'file.tsx', options) {
|
|
210
|
+
const project = new Project({
|
|
211
|
+
useInMemoryFileSystem: true,
|
|
212
|
+
compilerOptions: {
|
|
213
|
+
jsx: 2, // JsxEmit.React
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
const sourceFile = project.createSourceFile(filePath, source);
|
|
217
|
+
// Get aliases before transforming (e.g., import { AtAGlanceCard as Card })
|
|
218
|
+
const atAGlanceCardAliases = getAtAGlanceCardAliases(sourceFile, options?.facadePackage);
|
|
219
|
+
transformJsxElements(sourceFile, atAGlanceCardAliases);
|
|
220
|
+
updateImports(sourceFile, atAGlanceCardAliases, options?.facadePackage);
|
|
221
|
+
return sourceFile.getFullText();
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../codemods/at-a-glance-article-card/transform.ts"],"names":[],"mappings":"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","sourcesContent":["import { Project, SyntaxKind, JsxOpeningElement, JsxSelfClosingElement, SourceFile } from 'ts-morph'\n\n/**\n * Codemod to migrate AtAGlance.Card to the new AtAGlance.ArticleCard.\n *\n * The old AtAGlance.Card component accepted props like `displayValue`, `label`,\n * `description`, and `icon` directly. The new API separates concerns:\n *\n * - `AtAGlance.Card` is now a primitive for custom layouts using `grid` prop and\n * subcomponents (Icon, Label, Description, Value)\n * - `AtAGlance.ArticleCard` is the new high-level component for static article cards\n *\n * Transformations:\n * - AtAGlance.Card (with displayValue/label props) -> AtAGlance.ArticleCard\n * - AtAGlanceCard (with displayValue/label props) -> AtAGlance.ArticleCard\n * - AtAGlance.Card (with children/grid) -> No change (already using new API)\n * - AtAGlanceCard (with children/grid) -> No change (already using new API)\n * - AtAGlance.AnchorCard / AtAGlanceAnchorCard -> No change (API unchanged)\n * - AtAGlance.ButtonCard / AtAGlanceButtonCard -> No change (API unchanged)\n */\n\ntype JsxElementWithTag = JsxOpeningElement | JsxSelfClosingElement\n\n/**\n * Checks if a module specifier matches a package name.\n * Handles both exact matches and subpath imports.\n * @example\n * matchesPackage('@company/ui', '@company/ui') // true\n * matchesPackage('@company/ui/elements', '@company/ui') // true\n * matchesPackage('@company/ui-v2', '@company/ui') // false\n */\nfunction matchesPackage(moduleSpecifier: string, packageName: string): boolean {\n return moduleSpecifier === packageName || moduleSpecifier.startsWith(packageName + '/')\n}\n\n/**\n * Checks if a module specifier is an import from @reapit/elements or a facade package.\n */\nfunction isElementsImport(moduleSpecifier: string, facadePackage?: string): boolean {\n return (\n matchesPackage(moduleSpecifier, '@reapit/elements') ||\n (facadePackage !== undefined && matchesPackage(moduleSpecifier, facadePackage))\n )\n}\n\nfunction getTagName(element: JsxElementWithTag): string {\n return element.getTagNameNode().getText()\n}\n\nfunction isNamespacedComponent(element: JsxElementWithTag, componentName: string): boolean {\n return getTagName(element) === `AtAGlance.${componentName}`\n}\n\nfunction hasProp(element: JsxElementWithTag, propName: string): boolean {\n const attributes = element.getAttributes()\n return attributes.some((attr) => {\n if (attr.getKind() !== SyntaxKind.JsxAttribute) {\n return false\n }\n const jsxAttr = attr.asKindOrThrow(SyntaxKind.JsxAttribute)\n const nameNode = jsxAttr.getNameNode()\n return nameNode.getText() === propName\n })\n}\n\nfunction hasChildren(element: JsxElementWithTag): boolean {\n // Self-closing elements have no children\n if (element.getKind() === SyntaxKind.JsxSelfClosingElement) {\n return false\n }\n\n // For opening elements, check the parent JsxElement's children\n const parent = element.getParent()\n if (parent?.getKind() === SyntaxKind.JsxElement) {\n const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement)\n const children = jsxElement.getJsxChildren()\n // Filter out whitespace-only text\n return children.some((child) => {\n if (child.getKind() === SyntaxKind.JsxText) {\n return child.getText().trim().length > 0\n }\n return true\n })\n }\n\n return false\n}\n\nfunction isUsingOldApi(element: JsxElementWithTag): boolean {\n // New API uses subcomponents as children or grid prop\n if (hasChildren(element) || hasProp(element, 'grid')) {\n return false\n }\n // Old API has displayValue or label props, or is completely empty (no props, no children)\n // Empty cards should be migrated to ArticleCard (will cause TS errors, but that's expected)\n return true\n}\n\nfunction renameTagTo(element: JsxElementWithTag, newTagName: string): void {\n const tagNameNode = element.getTagNameNode()\n tagNameNode.replaceWithText(newTagName)\n}\n\nfunction transformJsxElements(sourceFile: SourceFile, atAGlanceCardAliases: Set<string>): void {\n // Two-pass transformation approach:\n // Pass 1: Transform namespaced components (AtAGlance.Card -> AtAGlance.ArticleCard)\n // Pass 2: Transform direct imports (AtAGlanceCard -> AtAGlance.ArticleCard)\n //\n // We cannot do this in a single pass because mutating the AST while iterating\n // over it can cause nodes to be invalidated or missed. By collecting all elements\n // first, then mutating, we avoid iterator invalidation issues.\n\n // Pass 1: Process namespaced AtAGlance.Card components\n const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)\n const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)\n\n // Transform namespaced AtAGlance.Card to AtAGlance.ArticleCard when using old API\n for (const element of [...selfClosingElements, ...openingElements]) {\n if (isNamespacedComponent(element, 'Card') && isUsingOldApi(element)) {\n renameTagTo(element, 'AtAGlance.ArticleCard')\n\n // Also rename closing tag for non-self-closing elements\n if (element.getKind() === SyntaxKind.JsxOpeningElement) {\n const parent = element.getParent()\n if (parent?.getKind() === SyntaxKind.JsxElement) {\n const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement)\n const closingElement = jsxElement.getClosingElement()\n if (closingElement) {\n closingElement.getTagNameNode().replaceWithText('AtAGlance.ArticleCard')\n }\n }\n }\n }\n }\n\n // Pass 2: Re-fetch elements after Pass 1 mutations, then transform direct imports\n const selfClosingElements2 = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)\n const openingElements2 = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)\n\n // Transform direct AtAGlanceCard (or its aliases) to AtAGlance.ArticleCard when using old API\n for (const element of [...selfClosingElements2, ...openingElements2]) {\n const tagName = getTagName(element)\n if (atAGlanceCardAliases.has(tagName) && isUsingOldApi(element)) {\n renameTagTo(element, 'AtAGlance.ArticleCard')\n\n // Also rename closing tag for non-self-closing elements\n if (element.getKind() === SyntaxKind.JsxOpeningElement) {\n const parent = element.getParent()\n if (parent?.getKind() === SyntaxKind.JsxElement) {\n const jsxElement = parent.asKindOrThrow(SyntaxKind.JsxElement)\n const closingElement = jsxElement.getClosingElement()\n if (closingElement) {\n closingElement.getTagNameNode().replaceWithText('AtAGlance.ArticleCard')\n }\n }\n }\n }\n }\n}\n\nfunction getAtAGlanceCardAliases(sourceFile: SourceFile, facadePackage?: string): Set<string> {\n const aliases = new Set<string>()\n\n for (const importDecl of sourceFile.getImportDeclarations()) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue()\n\n if (!isElementsImport(moduleSpecifier, facadePackage)) continue\n\n for (const namedImport of importDecl.getNamedImports()) {\n if (namedImport.getName() === 'AtAGlanceCard') {\n // Get the alias if it exists, otherwise use the original name\n // Only add the alias (or original name) that's actually used in the file\n const alias = namedImport.getAliasNode()?.getText()\n aliases.add(alias ?? 'AtAGlanceCard')\n }\n }\n }\n\n // Add default only if file has NO imports at all (handles test snippets without imports)\n if (aliases.size === 0 && sourceFile.getImportDeclarations().length === 0) {\n aliases.add('AtAGlanceCard')\n }\n\n return aliases\n}\n\nfunction isAtAGlanceCardStillUsed(sourceFile: SourceFile, aliases: Set<string>): boolean {\n const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)\n const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)\n\n return [...selfClosingElements, ...openingElements].some((element) => {\n const tagName = getTagName(element)\n return aliases.has(tagName)\n })\n}\n\nfunction hasAtAGlanceImport(sourceFile: SourceFile, facadePackage?: string): boolean {\n return sourceFile.getImportDeclarations().some((importDecl) => {\n const moduleSpecifier = importDecl.getModuleSpecifierValue()\n\n if (!isElementsImport(moduleSpecifier, facadePackage)) return false\n\n return importDecl.getNamedImports().some((namedImport) => namedImport.getName() === 'AtAGlance')\n })\n}\n\nfunction usesAtAGlanceNamespace(sourceFile: SourceFile): boolean {\n const selfClosingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)\n const openingElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)\n\n return [...selfClosingElements, ...openingElements].some((element) => getTagName(element).startsWith('AtAGlance.'))\n}\n\nfunction updateImports(sourceFile: SourceFile, atAGlanceCardAliases: Set<string>, facadePackage?: string): void {\n const importDeclarations = sourceFile.getImportDeclarations()\n const atAGlanceCardStillUsed = isAtAGlanceCardStillUsed(sourceFile, atAGlanceCardAliases)\n const needsAtAGlanceImport = usesAtAGlanceNamespace(sourceFile) && !hasAtAGlanceImport(sourceFile, facadePackage)\n let importDeclWhereAtAGlanceCardWasRemoved: (typeof importDeclarations)[0] | null = null\n\n // First pass: Remove AtAGlanceCard imports and track where it was removed\n for (const importDecl of importDeclarations) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue()\n\n if (!isElementsImport(moduleSpecifier, facadePackage)) continue\n\n if (!atAGlanceCardStillUsed) {\n const namedImports = importDecl.getNamedImports()\n\n for (const namedImport of namedImports) {\n if (namedImport.getName() === 'AtAGlanceCard') {\n namedImport.remove()\n importDeclWhereAtAGlanceCardWasRemoved = importDecl\n }\n }\n }\n }\n\n // Second pass: Add AtAGlance import to the same declaration where AtAGlanceCard was removed\n // Do this BEFORE removing empty imports to avoid accessing removed nodes\n if (needsAtAGlanceImport && importDeclWhereAtAGlanceCardWasRemoved) {\n importDeclWhereAtAGlanceCardWasRemoved.addNamedImport('AtAGlance')\n } else if (needsAtAGlanceImport) {\n // If we didn't find where AtAGlanceCard was removed, add to first elements import\n for (const importDecl of importDeclarations) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue()\n\n if (isElementsImport(moduleSpecifier, facadePackage)) {\n importDecl.addNamedImport('AtAGlance')\n break\n }\n }\n }\n\n // Third pass: Remove empty import declarations\n for (const importDecl of importDeclarations) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue()\n\n if (!isElementsImport(moduleSpecifier, facadePackage)) continue\n\n if (\n importDecl.getNamedImports().length === 0 &&\n !importDecl.getDefaultImport() &&\n !importDecl.getNamespaceImport()\n ) {\n importDecl.remove()\n }\n }\n}\n\nexport default function transform(\n source: string,\n filePath: string = 'file.tsx',\n options?: { facadePackage?: string },\n): string {\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n jsx: 2, // JsxEmit.React\n },\n })\n\n const sourceFile = project.createSourceFile(filePath, source)\n\n // Get aliases before transforming (e.g., import { AtAGlanceCard as Card })\n const atAGlanceCardAliases = getAtAGlanceCardAliases(sourceFile, options?.facadePackage)\n\n transformJsxElements(sourceFile, atAGlanceCardAliases)\n updateImports(sourceFile, atAGlanceCardAliases, options?.facadePackage)\n\n return sourceFile.getFullText()\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../codemods/bin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { run } from './runner.js';
|
|
2
|
+
import { listCodemods, getCodemodDescription, validateCodemodName } from './codemods.js';
|
|
3
|
+
function printHelp() {
|
|
4
|
+
console.log(`
|
|
5
|
+
Usage: yarn dlx @reapit/elements@beta codemod <command> [options]
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
list List available codemods
|
|
9
|
+
apply <name> <dir> Apply a codemod to a directory
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--help, -h Show this help message
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
yarn dlx @reapit/elements@beta codemod list
|
|
16
|
+
yarn dlx @reapit/elements@beta codemod apply at-a-glance-article-card src/
|
|
17
|
+
yarn dlx @reapit/elements@beta codemod apply at-a-glance-article-card src/ --dry-run
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
function printApplyHelp(codemodName) {
|
|
21
|
+
console.log(`
|
|
22
|
+
Usage: yarn dlx @reapit/elements@beta codemod apply ${codemodName} <directory> [options]
|
|
23
|
+
|
|
24
|
+
Arguments:
|
|
25
|
+
<directory> Directory to search for files to transform
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
--ext <extensions> File extensions to process (default: .tsx,.ts,.jsx,.js)
|
|
29
|
+
--facade-package <pkg> Package name that re-exports @reapit/elements
|
|
30
|
+
--dry-run, -d Preview changes without writing files
|
|
31
|
+
--help, -h Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/
|
|
35
|
+
yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --dry-run
|
|
36
|
+
yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --ext .tsx,.jsx
|
|
37
|
+
yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --facade-package @company/ui-components
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
function printList() {
|
|
41
|
+
const codemods = listCodemods();
|
|
42
|
+
if (codemods.length === 0) {
|
|
43
|
+
console.log('No codemods available.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log('\nAvailable codemods:\n');
|
|
47
|
+
// Calculate padding for alignment
|
|
48
|
+
const maxNameLength = Math.max(...codemods.map((name) => name.length));
|
|
49
|
+
for (const name of codemods) {
|
|
50
|
+
const description = getCodemodDescription(name);
|
|
51
|
+
const padding = ' '.repeat(maxNameLength - name.length + 4);
|
|
52
|
+
if (description) {
|
|
53
|
+
console.log(` ${name}${padding}${description}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(` ${name}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
function printAvailableCodemods() {
|
|
62
|
+
const codemods = listCodemods();
|
|
63
|
+
if (codemods.length === 0) {
|
|
64
|
+
console.log('No codemods available.');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log('\nAvailable codemods:');
|
|
68
|
+
const maxNameLength = Math.max(...codemods.map((name) => name.length));
|
|
69
|
+
for (const name of codemods) {
|
|
70
|
+
const description = getCodemodDescription(name);
|
|
71
|
+
const padding = ' '.repeat(maxNameLength - name.length + 4);
|
|
72
|
+
if (description) {
|
|
73
|
+
console.log(` ${name}${padding}${description}`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(` ${name}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function handleApply(args) {
|
|
81
|
+
const codemodName = args[0];
|
|
82
|
+
if (!codemodName || codemodName.startsWith('-')) {
|
|
83
|
+
console.error('Error: No codemod name provided');
|
|
84
|
+
console.log('\nUsage: yarn dlx @reapit/elements@beta codemod apply <name> <directory> [options]');
|
|
85
|
+
console.log("\nRun 'yarn dlx @reapit/elements@beta codemod list' to see available codemods.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
// Security: Sanitize codemod name by validating it against the allowlist
|
|
89
|
+
// This prevents path traversal attacks in the dynamic import below
|
|
90
|
+
const sanitizedCodemodName = validateCodemodName(codemodName);
|
|
91
|
+
if (!sanitizedCodemodName) {
|
|
92
|
+
console.error(`Error: Unknown codemod '${codemodName}'`);
|
|
93
|
+
printAvailableCodemods();
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
const remainingArgs = args.slice(1);
|
|
97
|
+
// Handle help for specific codemod
|
|
98
|
+
if (remainingArgs.includes('--help') || remainingArgs.includes('-h')) {
|
|
99
|
+
printApplyHelp(sanitizedCodemodName);
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
// Load and run the codemod
|
|
103
|
+
const codemodModule = await import(`./${sanitizedCodemodName}/transform.js`);
|
|
104
|
+
const transform = codemodModule.default;
|
|
105
|
+
if (typeof transform !== 'function') {
|
|
106
|
+
console.error(`Error: Codemod '${codemodName}' does not export a default transform function`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
await run({
|
|
110
|
+
transform,
|
|
111
|
+
codemodName: sanitizedCodemodName,
|
|
112
|
+
args: remainingArgs,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async function main() {
|
|
116
|
+
const args = process.argv.slice(2);
|
|
117
|
+
const command = args[0];
|
|
118
|
+
// Handle no command or help
|
|
119
|
+
if (!command || command === '--help' || command === '-h') {
|
|
120
|
+
printHelp();
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
switch (command) {
|
|
124
|
+
case 'list':
|
|
125
|
+
printList();
|
|
126
|
+
break;
|
|
127
|
+
case 'apply':
|
|
128
|
+
await handleApply(args.slice(1));
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
console.error(`Error: Unknown command '${command}'`);
|
|
132
|
+
printHelp();
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
main().catch((error) => {
|
|
137
|
+
console.error('Error:', error.message);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../codemods/bin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAExF,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;CAcb,CAAC,CAAA;AACF,CAAC;AAED,SAAS,cAAc,CAAC,WAAmB;IACzC,OAAO,CAAC,GAAG,CAAC;sDACwC,WAAW;;;;;;;;;;;;iDAYhB,WAAW;iDACX,WAAW;iDACX,WAAW;iDACX,WAAW;CAC3D,CAAC,CAAA;AACF,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;QACrC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;IAEtC,kCAAkC;IAClC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAEtE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAC3D,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,GAAG,WAAW,EAAE,CAAC,CAAA;QAClD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAA;AACf,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;QACrC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;IAEpC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAEtE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAC3D,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,GAAG,WAAW,EAAE,CAAC,CAAA;QAClD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAc;IACvC,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAE3B,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAA;QAChD,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAA;QACjG,OAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAA;QAC7F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,yEAAyE;IACzE,mEAAmE;IACnE,MAAM,oBAAoB,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAA;IAE7D,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,2BAA2B,WAAW,GAAG,CAAC,CAAA;QACxD,sBAAsB,EAAE,CAAA;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAEnC,mCAAmC;IACnC,IAAI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,cAAc,CAAC,oBAAoB,CAAC,CAAA;QACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,2BAA2B;IAC3B,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,oBAAoB,eAAe,CAAC,CAAA;IAC5E,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAA;IAEvC,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,mBAAmB,WAAW,gDAAgD,CAAC,CAAA;QAC7F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,GAAG,CAAC;QACR,SAAS;QACT,WAAW,EAAE,oBAAoB;QACjC,IAAI,EAAE,aAAa;KACpB,CAAC,CAAA;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAEvB,4BAA4B;IAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,SAAS,EAAE,CAAA;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,SAAS,EAAE,CAAA;YACX,MAAK;QAEP,KAAK,OAAO;YACV,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAChC,MAAK;QAEP;YACE,OAAO,CAAC,KAAK,CAAC,2BAA2B,OAAO,GAAG,CAAC,CAAA;YACpD,SAAS,EAAE,CAAA;YACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA","sourcesContent":["import { run } from './runner.js'\nimport { listCodemods, getCodemodDescription, validateCodemodName } from './codemods.js'\n\nfunction printHelp(): void {\n console.log(`\nUsage: yarn dlx @reapit/elements@beta codemod <command> [options]\n\nCommands:\n list List available codemods\n apply <name> <dir> Apply a codemod to a directory\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n yarn dlx @reapit/elements@beta codemod list\n yarn dlx @reapit/elements@beta codemod apply at-a-glance-article-card src/\n yarn dlx @reapit/elements@beta codemod apply at-a-glance-article-card src/ --dry-run\n`)\n}\n\nfunction printApplyHelp(codemodName: string): void {\n console.log(`\nUsage: yarn dlx @reapit/elements@beta codemod apply ${codemodName} <directory> [options]\n\nArguments:\n <directory> Directory to search for files to transform\n\nOptions:\n --ext <extensions> File extensions to process (default: .tsx,.ts,.jsx,.js)\n --facade-package <pkg> Package name that re-exports @reapit/elements\n --dry-run, -d Preview changes without writing files\n --help, -h Show this help message\n\nExamples:\n yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/\n yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --dry-run\n yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --ext .tsx,.jsx\n yarn dlx @reapit/elements@beta codemod apply ${codemodName} src/ --facade-package @company/ui-components\n`)\n}\n\nfunction printList(): void {\n const codemods = listCodemods()\n\n if (codemods.length === 0) {\n console.log('No codemods available.')\n return\n }\n\n console.log('\\nAvailable codemods:\\n')\n\n // Calculate padding for alignment\n const maxNameLength = Math.max(...codemods.map((name) => name.length))\n\n for (const name of codemods) {\n const description = getCodemodDescription(name)\n const padding = ' '.repeat(maxNameLength - name.length + 4)\n if (description) {\n console.log(` ${name}${padding}${description}`)\n } else {\n console.log(` ${name}`)\n }\n }\n\n console.log()\n}\n\nfunction printAvailableCodemods(): void {\n const codemods = listCodemods()\n\n if (codemods.length === 0) {\n console.log('No codemods available.')\n return\n }\n\n console.log('\\nAvailable codemods:')\n\n const maxNameLength = Math.max(...codemods.map((name) => name.length))\n\n for (const name of codemods) {\n const description = getCodemodDescription(name)\n const padding = ' '.repeat(maxNameLength - name.length + 4)\n if (description) {\n console.log(` ${name}${padding}${description}`)\n } else {\n console.log(` ${name}`)\n }\n }\n}\n\nasync function handleApply(args: string[]): Promise<void> {\n const codemodName = args[0]\n\n if (!codemodName || codemodName.startsWith('-')) {\n console.error('Error: No codemod name provided')\n console.log('\\nUsage: yarn dlx @reapit/elements@beta codemod apply <name> <directory> [options]')\n console.log(\"\\nRun 'yarn dlx @reapit/elements@beta codemod list' to see available codemods.\")\n process.exit(1)\n }\n\n // Security: Sanitize codemod name by validating it against the allowlist\n // This prevents path traversal attacks in the dynamic import below\n const sanitizedCodemodName = validateCodemodName(codemodName)\n\n if (!sanitizedCodemodName) {\n console.error(`Error: Unknown codemod '${codemodName}'`)\n printAvailableCodemods()\n process.exit(1)\n }\n\n const remainingArgs = args.slice(1)\n\n // Handle help for specific codemod\n if (remainingArgs.includes('--help') || remainingArgs.includes('-h')) {\n printApplyHelp(sanitizedCodemodName)\n process.exit(0)\n }\n\n // Load and run the codemod\n const codemodModule = await import(`./${sanitizedCodemodName}/transform.js`)\n const transform = codemodModule.default\n\n if (typeof transform !== 'function') {\n console.error(`Error: Codemod '${codemodName}' does not export a default transform function`)\n process.exit(1)\n }\n\n await run({\n transform,\n codemodName: sanitizedCodemodName,\n args: remainingArgs,\n })\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n const command = args[0]\n\n // Handle no command or help\n if (!command || command === '--help' || command === '-h') {\n printHelp()\n process.exit(0)\n }\n\n switch (command) {\n case 'list':\n printList()\n break\n\n case 'apply':\n await handleApply(args.slice(1))\n break\n\n default:\n console.error(`Error: Unknown command '${command}'`)\n printHelp()\n process.exit(1)\n }\n}\n\nmain().catch((error: Error) => {\n console.error('Error:', error.message)\n process.exit(1)\n})\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface CodemodMetadata {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
readonly description: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare const AVAILABLE_CODEMODS: readonly CodemodMetadata[];
|
|
6
|
+
export declare const CODEMOD_NAMES: readonly string[];
|
|
7
|
+
export type CodemodName = (typeof CODEMOD_NAMES)[number];
|
|
8
|
+
/**
|
|
9
|
+
* Returns available codemods from the generated manifest.
|
|
10
|
+
*
|
|
11
|
+
* @returns Array of codemod names
|
|
12
|
+
*/
|
|
13
|
+
export declare function listCodemods(): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Retrieves the description from the manifest for a specific codemod.
|
|
16
|
+
*
|
|
17
|
+
* @param name - The codemod name
|
|
18
|
+
* @returns The description, or null if not found
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCodemodDescription(name: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Validates that a codemod exists in the manifest.
|
|
23
|
+
* Returns the sanitized name to prevent path traversal attacks.
|
|
24
|
+
*
|
|
25
|
+
* @param name - The codemod name to validate
|
|
26
|
+
* @returns The sanitized codemod name, or null if invalid
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateCodemodName(name: string): CodemodName | null;
|
|
29
|
+
//# sourceMappingURL=codemods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codemods.d.ts","sourceRoot":"","sources":["../../codemods/codemods.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAMD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAAoC,CAAA;AAC7F,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAuD,CAAA;AAClG,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAEpE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import manifest from './manifest.json' with { type: 'json' };
|
|
2
|
+
export const AVAILABLE_CODEMODS = manifest.codemods;
|
|
3
|
+
export const CODEMOD_NAMES = manifest.codemods.map((c) => c.name);
|
|
4
|
+
/**
|
|
5
|
+
* Returns available codemods from the generated manifest.
|
|
6
|
+
*
|
|
7
|
+
* @returns Array of codemod names
|
|
8
|
+
*/
|
|
9
|
+
export function listCodemods() {
|
|
10
|
+
return CODEMOD_NAMES.slice(); // Return copy to prevent mutation
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves the description from the manifest for a specific codemod.
|
|
14
|
+
*
|
|
15
|
+
* @param name - The codemod name
|
|
16
|
+
* @returns The description, or null if not found
|
|
17
|
+
*/
|
|
18
|
+
export function getCodemodDescription(name) {
|
|
19
|
+
const metadata = AVAILABLE_CODEMODS.find((c) => c.name === name);
|
|
20
|
+
return metadata?.description ?? null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validates that a codemod exists in the manifest.
|
|
24
|
+
* Returns the sanitized name to prevent path traversal attacks.
|
|
25
|
+
*
|
|
26
|
+
* @param name - The codemod name to validate
|
|
27
|
+
* @returns The sanitized codemod name, or null if invalid
|
|
28
|
+
*/
|
|
29
|
+
export function validateCodemodName(name) {
|
|
30
|
+
return CODEMOD_NAMES.includes(name) ? name : null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=codemods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codemods.js","sourceRoot":"","sources":["../../codemods/codemods.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAY5D,MAAM,CAAC,MAAM,kBAAkB,GAAgC,QAAqB,CAAC,QAAQ,CAAA;AAC7F,MAAM,CAAC,MAAM,aAAa,GAAuB,QAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AAGlG;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,aAAa,CAAC,KAAK,EAAE,CAAA,CAAC,kCAAkC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAChE,OAAO,QAAQ,EAAE,WAAW,IAAI,IAAI,CAAA;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,aAAa,CAAC,QAAQ,CAAC,IAAmB,CAAC,CAAC,CAAC,CAAE,IAAoB,CAAC,CAAC,CAAC,IAAI,CAAA;AACnF,CAAC","sourcesContent":["import manifest from './manifest.json' with { type: 'json' }\n\n// Type definitions for the manifest\nexport interface CodemodMetadata {\n readonly name: string\n readonly description: string | null\n}\n\ninterface Manifest {\n codemods: CodemodMetadata[]\n}\n\nexport const AVAILABLE_CODEMODS: readonly CodemodMetadata[] = (manifest as Manifest).codemods\nexport const CODEMOD_NAMES: readonly string[] = (manifest as Manifest).codemods.map((c) => c.name)\nexport type CodemodName = (typeof CODEMOD_NAMES)[number]\n\n/**\n * Returns available codemods from the generated manifest.\n *\n * @returns Array of codemod names\n */\nexport function listCodemods(): string[] {\n return CODEMOD_NAMES.slice() // Return copy to prevent mutation\n}\n\n/**\n * Retrieves the description from the manifest for a specific codemod.\n *\n * @param name - The codemod name\n * @returns The description, or null if not found\n */\nexport function getCodemodDescription(name: string): string | null {\n const metadata = AVAILABLE_CODEMODS.find((c) => c.name === name)\n return metadata?.description ?? null\n}\n\n/**\n * Validates that a codemod exists in the manifest.\n * Returns the sanitized name to prevent path traversal attacks.\n *\n * @param name - The codemod name to validate\n * @returns The sanitized codemod name, or null if invalid\n */\nexport function validateCodemodName(name: string): CodemodName | null {\n return CODEMOD_NAMES.includes(name as CodemodName) ? (name as CodemodName) : null\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type Transform = (source: string, filePath: string, options?: {
|
|
2
|
+
facadePackage?: string;
|
|
3
|
+
}) => string;
|
|
4
|
+
export interface RunOptions {
|
|
5
|
+
transform: Transform;
|
|
6
|
+
codemodName: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Recursively finds files matching the given patterns within a directory.
|
|
11
|
+
* Includes path traversal protection via isPathSafe validation.
|
|
12
|
+
*
|
|
13
|
+
* @param dir - The directory to search
|
|
14
|
+
* @param patterns - File patterns to match (e.g., ["*.ts", "*.tsx"])
|
|
15
|
+
* @param results - Accumulator for matching file paths
|
|
16
|
+
* @returns Array of absolute paths to matching files
|
|
17
|
+
*/
|
|
18
|
+
export declare function findFiles(dir: string, patterns: string[], results?: string[]): string[];
|
|
19
|
+
export declare function matchesPatterns(filename: string, patterns: string[]): boolean;
|
|
20
|
+
export declare function run({ transform, codemodName, args }: RunOptions): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../codemods/runner.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAA;AAE1G,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AA2BD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAyB3F;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAyB7E;AAuBD,wBAAsB,GAAG,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA2ErF"}
|