Created
September 25, 2024 06:35
-
-
Save aztack/4e1b46f5051e035b15378972f0558b17 to your computer and use it in GitHub Desktop.
extract-possible-values-of-classname-lepusCls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-shadow */ | |
/* eslint-disable @typescript-eslint/ban-types */ | |
/* eslint-disable curly */ | |
/* eslint-disable @typescript-eslint/no-use-before-define */ | |
/* eslint-disable max-lines-per-function */ | |
/* eslint-disable no-param-reassign */ | |
/* eslint-disable prefer-template */ | |
/* eslint-disable array-callback-return */ | |
/* eslint-disable @typescript-eslint/no-non-null-assertion */ | |
import 'colors'; | |
import classnames from 'classnames'; | |
import { highlight } from 'cli-highlight'; | |
import path from 'node:path'; | |
import { fileURLToPath } from 'node:url'; | |
import { Project, SyntaxKind } from "ts-morph"; | |
// @ts-ignore | |
globalThis.classnames = classnames; | |
// @ts-ignore | |
const __filename = fileURLToPath(import.meta.url); | |
const __dirname = path.dirname(__filename); | |
const rootDir = path.resolve(__dirname, '..', '..'); | |
// Initialize a project and source file | |
const project = new Project(); | |
const indexTs = path.join(rootDir, 'rc-button', 'src', 'index.tsx'); | |
const sourceFile = project.addSourceFileAtPath(indexTs); | |
// Helper function to extract union types | |
function extractUnionTypes(arg: any): string[] { | |
const argType = arg.getType(); | |
const unionValues: string[] = []; | |
if (argType.isUnion()) { | |
argType.getUnionTypes().forEach((unionType: { getText: () => string; }) => { | |
unionValues.push(unionType.getText()); | |
}); | |
} | |
return unionValues; | |
} | |
function stripInterpolation(str: string) { | |
return str.replace(/["'`${}]/g, ''); | |
} | |
// Generate all combinations of arrays of strings | |
function generateCombinations(arrays: string[][]): string[][] { | |
if (arrays.length === 0) return [[]]; | |
const [first, ...rest] = arrays; | |
const restCombinations = generateCombinations(rest); | |
// @ts-ignore | |
return first.flatMap(item => restCombinations.map(comb => [item, ...comb])); | |
} | |
// Recursive function to handle array, object, and template strings | |
function handleArguments(arg: any): string[][] { | |
const kind = arg.getKind(); | |
if (kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral) { | |
return [[stripInterpolation(arg.getText())]]; // Return string literals as array of one element | |
} | |
if (kind === SyntaxKind.ArrayLiteralExpression) { | |
// Recursively process array elements and generate combinations | |
const arrayElements = arg.getElements().map((element: any) => handleArguments(element)); | |
return generateCombinations(arrayElements); | |
} | |
if (kind === SyntaxKind.ObjectLiteralExpression) { | |
// Process object literals with conditional classes | |
return arg.getProperties().flatMap((prop: any) => { | |
const key = prop.getNameNode(); | |
const value = prop.getInitializer(); | |
if (key.getKind() === SyntaxKind.ComputedPropertyName) { | |
// Get TemplateExpression from key | |
const templateExpression = key.getFirstChildByKindOrThrow(SyntaxKind.TemplateExpression); | |
const keyCombinations = extractTemplateLiteralCombinations(templateExpression); | |
return keyCombinations.map(key => [key, ...handleArguments(value).flat()]); | |
} else { | |
return [[key.getText()]]; | |
} | |
}); | |
} | |
if (kind === SyntaxKind.TemplateExpression) { | |
return extractTemplateLiteralCombinations(arg).map(val => [val]); | |
} | |
return []; | |
} | |
// Example helper function for extracting combinations of template literals | |
function extractTemplateLiteralCombinations(templateLiteral: any): string[] { | |
const head = templateLiteral.getHead().getText(); | |
const combinedValues: string[][] = []; | |
templateLiteral.getTemplateSpans().forEach((span: any) => { | |
const expr = span.getExpression(); | |
const literalPart = span.getLiteral().getText(); | |
if (expr.getKind() === SyntaxKind.Identifier) { | |
const unionValues = extractUnionTypes(expr); | |
combinedValues.push(unionValues.map(value => value + literalPart)); | |
} else { | |
combinedValues.push([expr.getText() + literalPart]); | |
} | |
}); | |
const possibilities = generateCombinations(combinedValues); | |
const ret = possibilities.map(possibility => head + possibility.join("")); | |
return ret.map(stripInterpolation); | |
} | |
// Generate dynamic classnames function | |
function generateDynamicClassnameFunction(args: any[]): Function | null { | |
const allCombinations = handleMultipleArguments(args); | |
console.log(`${allCombinations.length} combinations`); | |
// if (allCombinations.length > 50) { | |
// console.warn(`Too many combinations, skip`); | |
// return null; | |
// } | |
// Generate multiple calls to classnames, passing each combination as arguments | |
const calls = allCombinations.map(combination => ` | |
classes.push(globalThis.classnames(...${JSON.stringify(combination)})); | |
`.trim()).join("\n"); | |
const code = `const classes = [];\n${calls}\nreturn classes;`; | |
if (process.argv.some(arg => arg === '--debug')) { | |
console.log(highlight(`() => {\n${code}\n}`, { | |
language: 'typescript', | |
ignoreIllegals: true | |
})); | |
} | |
return new Function(code); | |
} | |
// Function to handle multiple arguments (strings, objects, arrays, template literals) | |
function handleMultipleArguments(args: any[]): string[][] { | |
// Flatten all processed arguments into an array of arrays (i.e., combinations of arguments) | |
// @ts-ignore | |
return args.flatMap((arg: any) => { | |
const processedArgs = handleArguments(arg); // Process each argument | |
return processedArgs.map(arg => [arg]); // Wrap each result in an array to preserve grouping | |
}).filter(Boolean); // Remove falsy values | |
} | |
// Main function to find `this.lepusCls` calls and handle multiple arguments | |
function findLepusClsStringPossibilities() { | |
const methodCalls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression); | |
const allClasses = new Set(); | |
methodCalls.forEach((callExpression) => { | |
const expression = callExpression.getExpression(); | |
if ( | |
expression.getKind() === SyntaxKind.PropertyAccessExpression && | |
expression.getText() === "this.lepusCls" | |
) { | |
console.log(`${indexTs}:${callExpression.getStartLineNumber()}`.green); | |
const args = callExpression.getArguments(); | |
console.log(highlight(callExpression.getText(), { | |
language: 'typescript', | |
ignoreIllegals: true | |
})); | |
// Generate the dynamic function using all arguments | |
const dynamicClassnameFunction = generateDynamicClassnameFunction(args); | |
if (!dynamicClassnameFunction) { | |
return; | |
} | |
// Execute the dynamic function and log the results | |
try { | |
const resultClasses = dynamicClassnameFunction(); | |
resultClasses.forEach((classname: any) => { | |
allClasses.add(classname); | |
console.log(classname); | |
}); | |
} catch (e) { | |
const error = e instanceof Error ? e.message : String(e); | |
console.error(`${error}`.red); | |
} | |
} | |
}); | |
console.log(`-`.repeat(80).green); | |
const finalIds = new Set(); | |
Array.from(allClasses).forEach((classname: any) => { | |
classname.split(' ').forEach((name: string) => finalIds.add(name)); | |
}); | |
console.log(Array.from(finalIds).sort()); | |
} | |
// Execute the function | |
findLepusClsStringPossibilities(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment