-
-
Notifications
You must be signed in to change notification settings - Fork 25
feat(chalk-to-util-styletext): add workflow #256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
richiemccoll
wants to merge
22
commits into
nodejs:main
Choose a base branch
from
richiemccoll:feat/chalk-styletext-migration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
4fac653
feat(chalk-to-util-styletext): add workflow with basic test
richiemccoll 21781d6
feat(chalk-to-util-styletext): sync lockfile
richiemccoll 6944457
feat(chalk-to-util-styletext): add commonjs require case
richiemccoll e628155
Merge branch 'main' into feat/chalk-styletext-migration
richiemccoll eaf5469
feat(chalk-to-util-styletext): add bg color case
richiemccoll 8b29e26
feat(chalk-to-util-styletext): add chained case
richiemccoll 0c2b44a
feat(chalk-to-util-styletext): add advanced modifiers case
richiemccoll 54b3bb9
feat(chalk-to-util-styletext): add more modifiers to test case
richiemccoll 90d0648
feat(chalk-to-util-styletext): add modifiers test case
richiemccoll 1bf1736
feat(chalk-to-util-styletext): add mixed imports test case
richiemccoll 6a14179
feat(chalk-to-util-styletext): add dynamic imports test case
richiemccoll cefb46c
Merge branch 'main' into feat/chalk-styletext-migration
richiemccoll 324fb50
feat(chalk-to-util-styletext): refactoring
richiemccoll fa51707
feat(chalk-to-util-styletext): code review comments
richiemccoll 27a0ad2
Merge branch 'main' into feat/chalk-styletext-migration
richiemccoll 0482005
feat(chalk-to-util-styletext): code review comments
richiemccoll 8c762aa
Merge branch 'feat/chalk-styletext-migration' of github.com:richiemcc…
richiemccoll f2a5a8b
feat(chalk-to-util-styletext): handle different import cases
richiemccoll 09e8eaf
feat(chalk-to-util-styletext): add unsupported features test
richiemccoll 412f1ee
Merge branch 'main' into feat/chalk-styletext-migration
richiemccoll 34c97cd
feat(chalk-to-util-styletext): warn on unsupported method
richiemccoll 6d5433d
Merge branch 'feat/chalk-styletext-migration' of github.com:richiemcc…
richiemccoll File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # chalk-to-util-styletext | ||
|
|
||
| TODO |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| schema_version: "1.0" | ||
| name: "@nodejs/chalk-to-util-styletext" | ||
| version: 1.0.0 | ||
| description: Migrate from the chalk package to Node.js's built-in util.styleText API | ||
| author: Richie McColl | ||
| license: MIT | ||
| workflow: workflow.yaml | ||
| category: migration | ||
|
|
||
| targets: | ||
| languages: | ||
| - javascript | ||
| - typescript | ||
|
|
||
| keywords: | ||
| - transformation | ||
| - migration | ||
|
|
||
| registry: | ||
| access: public | ||
| visibility: public |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "name": "@nodejs/chalk-to-util-styletext", | ||
| "version": "1.0.0", | ||
| "description": "Migrate from the chalk package to Node.js's built-in util.styleText API", | ||
| "type": "module", | ||
| "scripts": { | ||
| "test": "npx codemod jssg test -l typescript ./src/workflow.ts ./" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/nodejs/userland-migrations.git", | ||
| "directory": "recipes/chalk-to-util-styletext", | ||
| "bugs": "https://github.com/nodejs/userland-migrations/issues" | ||
| }, | ||
| "author": "Richie McColl", | ||
| "license": "MIT", | ||
| "homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/chalk-to-util-styletext/README.md", | ||
| "devDependencies": { | ||
| "@codemod.com/jssg-types": "^1.0.9" | ||
| }, | ||
| "dependencies": { | ||
| "@nodejs/codemod-utils": "*" | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| import { getNodeRequireCalls } from "@nodejs/codemod-utils/ast-grep/require-call"; | ||
| import { | ||
| getNodeImportCalls, | ||
| getNodeImportStatements, | ||
| } from "@nodejs/codemod-utils/ast-grep/import-statement"; | ||
| import { resolveBindingPath } from "@nodejs/codemod-utils/ast-grep/resolve-binding-path"; | ||
| import type { Edit, Range, SgNode, SgRoot } from "@codemod.com/jssg-types/main"; | ||
| import type Js from "@codemod.com/jssg-types/langs/javascript"; | ||
|
|
||
| /** | ||
| * Transform function that converts chalk method calls to Node.js util.styleText calls. | ||
| * | ||
| * Examples: | ||
| * - chalk.red("text") → styleText("red", "text") | ||
| * - chalk.red.bold("text") → styleText(["red", "bold"], "text") | ||
| */ | ||
| export default function transform(root: SgRoot<Js>): string | null { | ||
| const rootNode = root.root(); | ||
| const edits: Edit[] = []; | ||
| const chalkBinding = "chalk"; | ||
|
|
||
| // This actually catches `node:chalk` import but we don't care as it shouldn't append | ||
| const statements = [ | ||
| ...getNodeImportStatements(root, chalkBinding), | ||
| ...getNodeRequireCalls(root, chalkBinding), | ||
| ...getNodeImportCalls(root, chalkBinding), | ||
| ]; | ||
|
|
||
| // If there aren't any imports then we don't process the file | ||
| if (!statements.length) return null; | ||
AugustinMauroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| for (const statement of statements) { | ||
| const binding = resolveBindingPath(statement, "$"); | ||
|
|
||
| if (!binding) continue; | ||
|
|
||
| const chalkCalls = rootNode.findAll({ | ||
| rule: { | ||
| any: [ | ||
| // Pattern 1: single method calls: chalk.method(text) | ||
| { pattern: `${binding}.$METHOD($TEXT)` }, | ||
| // Pattern 2: chained method calls: chalk.method1.method2(text)); | ||
| { | ||
| kind: "call_expression", | ||
| has: { | ||
| field: "function", | ||
| kind: "member_expression", | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| }); | ||
|
|
||
| for (const call of chalkCalls) { | ||
| const methodMatch = call.getMatch("METHOD"); | ||
| const textMatch = call.getMatch("TEXT"); | ||
|
|
||
| if (methodMatch && textMatch) { | ||
| // Pattern 1: chalk.method(text) → styleText("method", text) | ||
| const method = methodMatch.text(); | ||
| const text = textMatch.text(); | ||
| const styleMethod = COMPAT_MAP[method] || method; | ||
AugustinMauroy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const replacement = `styleText("${styleMethod}", ${text})`; | ||
|
|
||
| edits.push(call.replace(replacement)); | ||
| } else { | ||
| // Pattern 2: chalk.method1.method2(text) → styleText(["method1", "method2"], text) | ||
| const functionExpr = call.field("function"); | ||
|
|
||
| if (!functionExpr) continue; | ||
|
|
||
| const styles = extractChalkStyles(functionExpr, binding); | ||
|
|
||
| if (styles.length === 0) continue; | ||
|
|
||
| const args = call.field("arguments"); | ||
|
|
||
| if (!args) continue; | ||
|
|
||
| const argsList = args.children().filter((c) => { | ||
| const excluded = [",", "(", ")"]; | ||
| return !excluded.includes(c.kind()); | ||
| }); | ||
|
|
||
| if (argsList.length === 0) continue; | ||
|
|
||
| const textArg = argsList[0].text(); | ||
|
|
||
| if (styles.length === 1) { | ||
| const replacement = `styleText("${styles[0]}", ${textArg})`; | ||
|
|
||
| edits.push(call.replace(replacement)); | ||
| } else { | ||
| const stylesArray = `[${styles.map((s) => `"${s}"`).join(", ")}]`; | ||
| const replacement = `styleText(${stylesArray}, ${textArg})`; | ||
|
|
||
| edits.push(call.replace(replacement)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (edits.length > 0) { | ||
| // Update the import or require statements if any calls were transformed | ||
| if (statement.kind() === "import_statement") { | ||
| // Replace entire import statement | ||
| edits.push(statement.replace(`import { styleText } from "node:util";`)); | ||
| } else if (statement.kind() === "variable_declarator") { | ||
| // Handle dynamic ESM import | ||
| if (statement.field("value")?.kind() === "await_expression") { | ||
| edits.push(statement.replace(`{ styleText } = await import("node:util")`)); | ||
| } else { | ||
| // Handle CommonJS require | ||
| edits.push(statement.replace(`{ styleText } = require("node:util")`)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!edits.length) return null; | ||
|
|
||
| const sourceCode = rootNode.commitEdits(edits); | ||
|
|
||
| return sourceCode; | ||
AugustinMauroy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // Compatibility mapping for chalk properties that differ in util.styleText | ||
| const COMPAT_MAP: Record<string, string> = { | ||
| overline: "overlined", | ||
| }; | ||
|
|
||
| /** | ||
| * Traverses a member expression node to extract chained chalk styles. | ||
| * and returns a list of styles in the order they were called. | ||
| */ | ||
| function extractChalkStyles(node: SgNode<Js>, chalkBinding: string): string[] { | ||
AugustinMauroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const styles: string[] = []; | ||
|
|
||
| function traverse(node: SgNode<Js>): boolean { | ||
| const obj = node.field("object"); | ||
| const prop = node.field("property"); | ||
|
|
||
| if (obj && prop && prop.kind() === "property_identifier") { | ||
| const propName = prop.text(); | ||
|
|
||
| if (obj.kind() === "identifier" && obj.text() === chalkBinding) { | ||
| // Base case: chalk.method | ||
| styles.push(COMPAT_MAP[propName] || propName); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| if (obj.kind() === "member_expression" && traverse(obj)) { | ||
| // Recursive case: chain.method | ||
| styles.push(COMPAT_MAP[propName] || propName); | ||
|
|
||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| traverse(node); | ||
|
|
||
| return styles; | ||
AugustinMauroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
15 changes: 15 additions & 0 deletions
15
recipes/chalk-to-util-styletext/tests/expected/advanced-modifiers.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { styleText } from "node:util"; | ||
|
|
||
| // Some of these have limited terminal support - may not work in all environments | ||
| console.log(styleText("bold", "Bold text")); | ||
| console.log(styleText("blink", "Blinking text")); | ||
| console.log(styleText("dim", "Dimmed text")); | ||
| console.log(styleText("doubleunderline", "Double underlined")); | ||
| console.log(styleText("framed", "Framed text")); | ||
| console.log(styleText("italic", "Italic text")); | ||
| console.log(styleText("inverse", "Inverted colors")); | ||
| console.log(styleText("hidden", "Hidden text")); | ||
| console.log(styleText("overlined", "Overlined text")); | ||
| console.log(styleText("reset", "Reset text")); | ||
| console.log(styleText("strikethrough", "Strikethrough text")); | ||
| console.log(styleText("underline", "Underlined text")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { styleText } from "node:util"; | ||
|
|
||
| console.log(styleText(["bgRed", "white"], "Error on red background")); | ||
| console.log(styleText(["bgGreen", "black"], "Success on green background")); | ||
| console.log(styleText(["bgBlue", "whiteBright"], "Info on blue background")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { styleText } from "node:util"; | ||
|
|
||
| console.log(styleText("red", "Error message")); | ||
| console.log(styleText("green", "Success message")); | ||
| console.log(styleText("blue", "Info message")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { styleText } from "node:util"; | ||
|
|
||
| console.log(styleText(["red", "bold"], "Error: Operation failed")); | ||
| console.log(styleText(["green", "underline"], "Success: All tests passed")); | ||
| console.log(styleText(["yellow", "bgBlack"], "Warning: Deprecated API usage")); |
7 changes: 7 additions & 0 deletions
7
recipes/chalk-to-util-styletext/tests/expected/commonjs-require.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const { styleText } = require("node:util"); | ||
|
|
||
| const error = styleText("red", "Error"); | ||
| const warning = styleText("yellow", "Warning"); | ||
| const info = styleText("blue", "Info"); | ||
|
|
||
| console.log(error, warning, info); |
9 changes: 9 additions & 0 deletions
9
recipes/chalk-to-util-styletext/tests/expected/dynamic-import.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // CommonJS style dynamic import | ||
| async function testCommonJS() { | ||
| const { styleText } = await import("node:util"); | ||
| console.log(styleText("bgBlue", "This is a message")); | ||
| } | ||
|
|
||
| // ESM style dynamic import | ||
| const { styleText } = await import("node:util"); | ||
| console.log(styleText("bgRed", "This is a message")); |
13 changes: 13 additions & 0 deletions
13
recipes/chalk-to-util-styletext/tests/expected/mixed-import.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { styleText } from "node:util"; | ||
| import { otherFunction } from "./utils"; | ||
|
|
||
| function logError(message) { | ||
| console.log(styleText(["red", "bold"], `ERROR: ${message}`)); | ||
| } | ||
|
|
||
| function logSuccess(message) { | ||
| console.log(styleText("green", `SUCCESS: ${message}`)); | ||
| } | ||
|
|
||
| logError("Something went wrong"); | ||
| logSuccess("Operation completed"); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { styleText } from "node:util"; | ||
|
|
||
| console.log(styleText(["bold", "italic", "underline"], "Important announcement")); | ||
| console.log(styleText(["dim", "strikethrough"], "Deprecated feature")); | ||
| console.log(styleText("inverse", "Inverted colors")); |
15 changes: 15 additions & 0 deletions
15
recipes/chalk-to-util-styletext/tests/input/advanced-modifiers.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import chalk from "chalk"; | ||
|
|
||
| // Some of these have limited terminal support - may not work in all environments | ||
| console.log(chalk.bold("Bold text")); | ||
| console.log(chalk.blink("Blinking text")); | ||
| console.log(chalk.dim("Dimmed text")); | ||
| console.log(chalk.doubleunderline("Double underlined")); | ||
| console.log(chalk.framed("Framed text")); | ||
| console.log(chalk.italic("Italic text")); | ||
| console.log(chalk.inverse("Inverted colors")); | ||
| console.log(chalk.hidden("Hidden text")); | ||
| console.log(chalk.overline("Overlined text")); | ||
| console.log(chalk.reset("Reset text")); | ||
| console.log(chalk.strikethrough("Strikethrough text")); | ||
| console.log(chalk.underline("Underlined text")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import chalk from "chalk"; | ||
|
|
||
| console.log(chalk.bgRed.white("Error on red background")); | ||
| console.log(chalk.bgGreen.black("Success on green background")); | ||
| console.log(chalk.bgBlue.whiteBright("Info on blue background")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import chalk from "chalk"; | ||
|
|
||
| console.log(chalk.red("Error message")); | ||
| console.log(chalk.green("Success message")); | ||
| console.log(chalk.blue("Info message")); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import chalk from "chalk"; | ||
|
|
||
| console.log(chalk.red.bold("Error: Operation failed")); | ||
| console.log(chalk.green.underline("Success: All tests passed")); | ||
| console.log(chalk.yellow.bgBlack("Warning: Deprecated API usage")); |
7 changes: 7 additions & 0 deletions
7
recipes/chalk-to-util-styletext/tests/input/commonjs-require.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const chalk = require("chalk"); | ||
|
|
||
| const error = chalk.red("Error"); | ||
| const warning = chalk.yellow("Warning"); | ||
| const info = chalk.blue("Info"); | ||
|
|
||
| console.log(error, warning, info); |
9 changes: 9 additions & 0 deletions
9
recipes/chalk-to-util-styletext/tests/input/dynamic-import.js
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // CommonJS style dynamic import | ||
| async function testCommonJS() { | ||
| const chalk = await import("chalk"); | ||
| console.log(chalk.bgBlue("This is a message")); | ||
| } | ||
|
|
||
| // ESM style dynamic import | ||
| const chalk = await import("chalk"); | ||
| console.log(chalk.bgRed("This is a message")); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.