Skip to content

fix(deps): replace chalk with smaller and faster ansis #7058

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions e2e/install.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { platform } from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import ansis from 'ansis'
import execa from 'execa'
import { runServer } from 'verdaccio'
import { describe, expect, it } from 'vitest'
Expand Down Expand Up @@ -175,11 +176,14 @@ describe.each(tests)('%s → installs the cli and runs the help command without

const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`))
const { stdout } = await execa(binary, ['help'], { cwd })
const normalizedOutput = ansis.strip(stdout.trim())

expect(stdout.trim(), `Help command does not start with 'VERSION':\n\n${stdout}`).toMatch(/^VERSION/)
expect(stdout, `Help command does not include 'netlify-cli/${pkg.version}':\n\n${stdout}`).toContain(
expect(normalizedOutput).toMatch(/VERSION/)
expect(normalizedOutput, `Help command does not include 'netlify-cli/${pkg.version}':\n\n${stdout}`).toContain(
`netlify-cli/${pkg.version}`,
)
expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]')
expect(normalizedOutput, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch(
'$ netlify [COMMAND]',
)
})
})
5 changes: 3 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default tseslint.config(
},
},
{
files: ['src/**/*'],
rules: {
'no-restricted-imports': [
'error',
Expand All @@ -96,9 +97,9 @@ export default tseslint.config(
},

{
name: 'chalk',
name: 'ansis',
message:
'Use the safe chalk import that handles colors for json output: `import { chalk } from "src/utils/command-helpers.js"`',
'Use the configured ansis import that handles colors for json output: `import { ansis } from "src/utils/command-helpers.js"`',
},
],
},
Expand Down
20 changes: 15 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"url": "https://github.com/netlify/cli/issues"
},
"scripts": {
"_format": "prettier --loglevel=warn \"{src,tools,scripts,tests,e2e,.github}/**/*.{mjs,cjs,js,mts,md,yml,json,html,ts}\" \"*.{mjs,cjs,js,mts,yml,json,html,ts}\" \".*.{mjs,cjs,js,yml,json,html,ts}\" \"!CHANGELOG.md\" \"!**/*/package-lock.json\" \"!.github/**/*.md\"",
"_format": "prettier --loglevel=warn \"{src,scripts,tests,e2e,.github}/**/*.{mjs,cjs,js,mts,md,yml,json,html,ts}\" \"*.{mjs,cjs,js,mts,yml,json,html,ts}\" \".*.{mjs,cjs,js,yml,json,html,ts}\" \"!CHANGELOG.md\" \"!**/*/package-lock.json\" \"!.github/**/*.md\"",
"build": "tsc --project tsconfig.build.json",
"clean": "rm -rf dist/",
"dev": "tsc --project tsconfig.build.json --watch",
Expand Down Expand Up @@ -74,10 +74,10 @@
"@pnpm/tabtab": "0.5.4",
"ansi-escapes": "7.0.0",
"ansi-to-html": "0.7.2",
"ansis": "^3.17.0",
"ascii-table": "0.0.9",
"backoff": "2.5.0",
"boxen": "8.0.1",
"chalk": "5.4.1",
"chokidar": "3.6.0",
"ci-info": "4.1.0",
"clean-deep": "3.4.0",
Expand Down Expand Up @@ -217,11 +217,5 @@
"typescript-eslint": "^8.26.0",
"verdaccio": "6.0.5",
"vitest": "1.6.1"
},
"ava": {
"files": [
"tools/**/*.test.js",
"tests/**/*.test.cjs"
]
}
}
40 changes: 7 additions & 33 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,10 @@ import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import process from 'node:process'
// eslint-disable-next-line no-restricted-imports
import chalk from 'chalk'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const identity = (message) => message
import ansis from 'ansis'

/**
*
* @param {string} message
* @param {Array<chalk['Color'] | chalk['Modifiers']>} styles
* @returns
*/
const format = (message, styles) => {
let func = identity
try {
func = chalk
styles.forEach((style) => {
func = func[style]
})
} catch {}
return func(message)
}
const __dirname = path.dirname(fileURLToPath(import.meta.url))

const postInstall = async () => {
const { createMainCommand } = await import('../dist/commands/index.js')
Expand All @@ -45,24 +26,17 @@ const postInstall = async () => {
}

console.log('')
console.log(await format('Success! Netlify CLI has been installed!', ['greenBright', 'bold', 'underline']))
console.log(ansis.greenBright.bold.underline('Success! Netlify CLI has been installed!'))
console.log('')
console.log('Your device is now configured to use Netlify CLI to deploy and manage your Netlify sites.')
console.log('')
console.log('Next steps:')
console.log('')
console.log(
` ${await format('netlify init', [
'cyanBright',
'bold',
])} Connect or create a Netlify site from current directory`,
)
console.log(
` ${await format('netlify deploy', ['cyanBright', 'bold'])} Deploy the latest changes to your Netlify site`,
)
console.log(` ${ansis.cyanBright.bold('netlify init')} Connect or create a Netlify site from current directory`)
console.log(` ${ansis.cyan.bold('netlify deploy')} Deploy the latest changes to your Netlify site`)
console.log('')
console.log(`For more information on the CLI run ${await format('netlify help', ['cyanBright', 'bold'])}`)
console.log(`Or visit the docs at ${await format('https://cli.netlify.com', ['cyanBright', 'bold'])}`)
console.log(`For more information on the CLI run ${ansis.cyanBright.bold('netlify help')}`)
console.log(`Or visit the docs at ${ansis.cyanBright.bold('https://cli.netlify.com')}`)
console.log('')
}

Expand Down
1 change: 0 additions & 1 deletion scripts/prepublishOnly.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const main = async () => {
//
// Leaving development dependencies makes the CLI installation significantly larger and increases
// the risk of platform-specific dependency installation issues.
// eslint-disable-next-line no-restricted-properties
const packageJSONPath = path.join(process.cwd(), 'package.json')
const rawPackageJSON = await fs.readFile(packageJSONPath, 'utf8')

Expand Down
8 changes: 3 additions & 5 deletions site/scripts/util/generate-command-data.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { createMainCommand } from '../../../src/commands/index.js'
import { sortOptions } from '../../../src/utils/command-helpers.js'
import { compareOptions } from '../../../src/utils/command-helpers.js'

const program = createMainCommand()

/** @type {Array<import('../../../src/commands/base-command.js').default>} */
// @ts-ignore typecast needed
const commands = program.commands.sort((cmdA, cmdB) => cmdA.name().localeCompare(cmdB.name()))
const commands = [...program.commands].sort((cmdA, cmdB) => cmdA.name().localeCompare(cmdB.name()))

/**
*
Expand All @@ -19,7 +17,7 @@ const parseCommand = function (command) {

const flags = command.options
.filter((option) => !option.hidden)
.sort(sortOptions)
.sort(compareOptions)
.reduce((prev, cur) => {
const name = cur.long.replace('--', '')
const contentType = cur.argChoices ? cur.argChoices.join(' | ') : 'string'
Expand Down
4 changes: 2 additions & 2 deletions src/commands/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import AsciiTable from 'ascii-table'
import type { OptionValues } from 'commander'
import { methods, type NetlifyAPI } from 'netlify'

import { chalk, logAndThrowError, exit, log, logJson } from '../../utils/command-helpers.js'
import { ansis, logAndThrowError, exit, log, logJson } from '../../utils/command-helpers.js'
import type BaseCommand from '../base-command.js'

type ApiMethodName = keyof NetlifyAPI
Expand All @@ -23,7 +23,7 @@ export const apiCommand = async (apiMethodName: string, options: OptionValues, c
log(table.toString())
log()
log('Above is a list of available API methods')
log(`To run a method use "${chalk.cyanBright('netlify api methodName')}"`)
log(`To run a method use "${ansis.cyanBright('netlify api methodName')}"`)
exit()
}

Expand Down
4 changes: 2 additions & 2 deletions src/commands/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { chalk } from '../../utils/command-helpers.js'
import { ansis } from '../../utils/command-helpers.js'
import BaseCommand from '../base-command.js'

export const createApiCommand = (program: BaseCommand) =>
Expand All @@ -7,7 +7,7 @@ export const createApiCommand = (program: BaseCommand) =>
.argument('[apiMethod]', 'Open API method to run')
.description(
`Run any Netlify API method
For more information on available methods checkout https://open-api.netlify.com/ or run '${chalk.grey(
For more information on available methods checkout https://open-api.netlify.com/ or run '${ansis.gray(
'netlify api --list',
)}'`,
)
Expand Down
34 changes: 17 additions & 17 deletions src/commands/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { getAgent } from '../lib/http-agent.js'
import {
NETLIFY_CYAN,
USER_AGENT,
chalk,
ansis,
logAndThrowError,
exit,
getToken,
Expand All @@ -28,7 +28,7 @@ import {
normalizeConfig,
padLeft,
pollForToken,
sortOptions,
compareOptions,
warn,
logError,
} from '../utils/command-helpers.js'
Expand Down Expand Up @@ -107,7 +107,7 @@ async function selectWorkspace(project: Project, filter?: string): Promise<strin

if (!selected) {
log()
log(chalk.cyan(`We've detected multiple sites inside your repository`))
log(ansis.cyan(`We've detected multiple sites inside your repository`))

if (isCI) {
throw new Error(
Expand All @@ -129,7 +129,7 @@ async function selectWorkspace(project: Project, filter?: string): Promise<strin
(project.workspace?.packages || [])
.filter((pkg) => pkg.path.includes(input))
.map((pkg) => ({
name: `${pkg.name ? `${chalk.bold(pkg.name)} ` : ''}${pkg.path} ${chalk.dim(
name: `${pkg.name ? `${ansis.bold(pkg.name)} ` : ''}${pkg.path} ${ansis.dim(
`--filter ${pkg.name || pkg.path}`,
)}`,
value: pkg.path,
Expand Down Expand Up @@ -286,7 +286,7 @@ export default class BaseCommand extends Command {

if (description) {
const pad = termWidth + HELP_SEPARATOR_WIDTH
const fullText = `${bang}${term.padEnd(pad - (isCommand ? 2 : 0))}${chalk.grey(description)}`
const fullText = `${bang}${term.padEnd(pad - (isCommand ? 2 : 0))}${ansis.gray(description)}`
return helper.wrap(fullText, helpWidth - HELP_INDENT_WIDTH, pad)
}

Expand All @@ -303,34 +303,34 @@ export default class BaseCommand extends Command {

// on the parent help command the version should be displayed
if (this.name() === 'netlify') {
output = [...output, chalk.bold('VERSION'), formatHelpList([formatItem(USER_AGENT)]), '']
output = [...output, ansis.bold('VERSION'), formatHelpList([formatItem(USER_AGENT)]), '']
}

// Usage
output = [...output, chalk.bold('USAGE'), helper.commandUsage(command), '']
output = [...output, ansis.bold('USAGE'), helper.commandUsage(command), '']

// Arguments
const argumentList = helper
.visibleArguments(command)
.map((argument) => formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument)))
if (argumentList.length !== 0) {
output = [...output, chalk.bold('ARGUMENTS'), formatHelpList(argumentList), '']
output = [...output, ansis.bold('ARGUMENTS'), formatHelpList(argumentList), '']
}

if (command.#noBaseOptions === false) {
// Options
const optionList = helper
.visibleOptions(command)
.sort(sortOptions)
.sort(compareOptions)
.map((option) => formatItem(helper.optionTerm(option), helper.optionDescription(option)))
if (optionList.length !== 0) {
output = [...output, chalk.bold('OPTIONS'), formatHelpList(optionList), '']
output = [...output, ansis.bold('OPTIONS'), formatHelpList(optionList), '']
}
}

// Description
if (commandDescription.length !== 0) {
output = [...output, chalk.bold('DESCRIPTION'), formatHelpList(commandDescription), '']
output = [...output, ansis.bold('DESCRIPTION'), formatHelpList(commandDescription), '']
}

// Aliases
Expand All @@ -339,13 +339,13 @@ export default class BaseCommand extends Command {
if (command._aliases.length !== 0) {
// @ts-expect-error TS(2551) FIXME: Property '_aliases' does not exist on type 'Comman... Remove this comment to see the full error message
const aliases = command._aliases.map((alias) => formatItem(`${parentCommand.name()} ${alias}`, null, true))
output = [...output, chalk.bold('ALIASES'), formatHelpList(aliases), '']
output = [...output, ansis.bold('ALIASES'), formatHelpList(aliases), '']
}

if (command.examples.length !== 0) {
output = [
...output,
chalk.bold('EXAMPLES'),
ansis.bold('EXAMPLES'),
formatHelpList(command.examples.map((example) => `${HELP_$} ${example}`)),
'',
]
Expand All @@ -355,7 +355,7 @@ export default class BaseCommand extends Command {
formatItem(cmd.name(), helper.subcommandDescription(cmd).split('\n')[0], true),
)
if (commandList.length !== 0) {
output = [...output, chalk.bold('COMMANDS'), formatHelpList(commandList), '']
output = [...output, ansis.bold('COMMANDS'), formatHelpList(commandList), '']
}

return [...output, ''].join('\n')
Expand Down Expand Up @@ -450,11 +450,11 @@ export default class BaseCommand extends Command {

// Log success
log()
log(chalk.greenBright('You are now logged into your Netlify account!'))
log(ansis.greenBright('You are now logged into your Netlify account!'))
log()
log(`Run ${chalk.cyanBright('netlify status')} for account details`)
log(`Run ${ansis.cyanBright('netlify status')} for account details`)
log()
log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`)
log(`To see all available commands run: ${ansis.cyanBright('netlify help')}`)
log()
return accessToken
}
Expand Down
Loading
Loading