diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0f34c9e3..75334d23 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [12, 16, 18] + node: [20, 22] steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 12 + node-version: 22 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true @@ -39,28 +39,19 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 14 + node-version: 22 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm test - run: npm run coverage - esm: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - with: - node-version: 14 - - run: npm install - - run: npm run test:esm deno: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 14 + node-version: 22 - run: npm install - run: npm run compile - uses: denolib/setup-deno@v2 diff --git a/deno.ts b/deno.ts index a94e49f3..70b4e28a 100644 --- a/deno.ts +++ b/deno.ts @@ -1,14 +1,14 @@ // Bootstrap cliui with CommonJS dependencies: import { cliui, UI } from './build/lib/index.js' import type { UIOptions } from './build/lib/index.d.ts' -import { wrap, stripAnsi } from './build/lib/string-utils.js' +import stringWidth from 'string-width' +import stripAnsi from 'strip-ansi' +import wrapAnsi from 'wrap-ansi' export default function ui (opts: UIOptions): UI { return cliui(opts, { - stringWidth: (str: string) => { - return [...str].length - }, + stringWidth, stripAnsi, - wrap + wrap: wrapAnsi }) } diff --git a/index.mjs b/index.mjs index bc7a022b..435bdbc0 100644 --- a/index.mjs +++ b/index.mjs @@ -1,13 +1,13 @@ // Bootstrap cliui with CommonJS dependencies: import { cliui } from './build/lib/index.js' -import { wrap, stripAnsi } from './build/lib/string-utils.js' +import stringWidth from 'string-width' +import stripAnsi from 'strip-ansi' +import wrapAnsi from 'wrap-ansi' export default function ui (opts) { return cliui(opts, { - stringWidth: (str) => { - return [...str].length - }, + stringWidth, stripAnsi, - wrap + wrap: wrapAnsi }) } diff --git a/lib/cjs.ts b/lib/cjs.ts deleted file mode 100644 index bda4241a..00000000 --- a/lib/cjs.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Bootstrap cliui with CommonJS dependencies: -import { cliui, UIOptions } from './index.js' -const stringWidth = require('string-width') -const stripAnsi = require('strip-ansi') -const wrap = require('wrap-ansi') -export default function ui (opts: UIOptions) { - return cliui(opts, { - stringWidth, - stripAnsi, - wrap - }) -} diff --git a/lib/index.ts b/lib/index.ts index 0673c3a5..81c5c48d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -349,7 +349,7 @@ function _minWidth (col: Column) { } function getWindowWidth (): number { - /* istanbul ignore next: depends on terminal */ + /* c8 ignore next 5: depends on terminal */ if (typeof process === 'object' && process.stdout && process.stdout.columns) { return process.stdout.columns } @@ -371,7 +371,7 @@ function alignCenter (str: string, width: number): string { str = str.trim() const strWidth = mixin.stringWidth(str) - /* istanbul ignore next */ + /* c8 ignore next 3 */ if (strWidth >= width) { return str } diff --git a/lib/string-utils.ts b/lib/string-utils.ts deleted file mode 100644 index 23d78fdb..00000000 --- a/lib/string-utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Minimal replacement for ansi string helpers "wrap-ansi" and "strip-ansi". -// to facilitate ESM and Deno modules. -// TODO: look at porting https://www.npmjs.com/package/wrap-ansi to ESM. - -// The npm application -// Copyright (c) npm, Inc. and Contributors -// Licensed on the terms of The Artistic License 2.0 -// See: https://github.com/npm/cli/blob/4c65cd952bc8627811735bea76b9b110cc4fc80e/lib/utils/ansi-trim.js -const ansi = new RegExp('\x1b(?:\\[(?:\\d+[ABCDEFGJKSTm]|\\d+;\\d+[Hfm]|' + -'\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)', 'g') - -export function stripAnsi (str: string) { - return str.replace(ansi, '') -} - -export function wrap (str: string, width: number) { - const [start, end] = str.match(ansi) || ['', ''] - str = stripAnsi(str) - let wrapped = '' - for (let i = 0; i < str.length; i++) { - if (i !== 0 && (i % width) === 0) { - wrapped += '\n' - } - wrapped += str.charAt(i) - } - if (start && end) { - wrapped = `${start}${wrapped}${end}` - } - return wrapped -} diff --git a/package.json b/package.json index eab6bf47..75d16a00 100644 --- a/package.json +++ b/package.json @@ -2,30 +2,21 @@ "name": "cliui", "version": "8.0.1", "description": "easily create complex multi-column command-line-interfaces", - "main": "build/index.cjs", + "main": "build/index.mjs", "exports": { - ".": [ - { - "import": "./index.mjs", - "require": "./build/index.cjs" - }, - "./build/index.cjs" - ] + ".": "./index.mjs" }, "type": "module", "module": "./index.mjs", "scripts": { - "check": "standardx '**/*.ts' && standardx '**/*.js' && standardx '**/*.cjs'", - "fix": "standardx --fix '**/*.ts' && standardx --fix '**/*.js' && standardx --fix '**/*.cjs'", - "pretest": "rimraf build && tsc -p tsconfig.test.json && cross-env NODE_ENV=test npm run build:cjs", - "test": "c8 mocha ./test/*.cjs", - "test:esm": "c8 mocha ./test/esm/cliui-test.mjs", + "check": "standardx '**/*.ts' && standardx '**/*.js'", + "fix": "standardx --fix '**/*.ts' && standardx --fix '**/*.js'", + "pretest": "rimraf build && tsc -p tsconfig.test.json", + "test": "c8 mocha ./test/*.mjs", "postest": "check", "coverage": "c8 report --check-coverage", "precompile": "rimraf build", "compile": "tsc", - "postcompile": "npm run build:cjs", - "build:cjs": "rollup -c", "prepare": "npm run compile" }, "repository": "yargs/cliui", @@ -49,28 +40,26 @@ "author": "Ben Coe ", "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "devDependencies": { - "@types/node": "^14.0.27", + "@types/node": "^22.13.10", "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^4.0.0", - "c8": "^7.3.0", - "chai": "^4.2.0", - "chalk": "^4.1.0", + "c8": "^10.1.3", + "chai": "^5.2.0", + "chalk": "^5.4.1", "cross-env": "^7.0.2", "eslint": "^7.6.0", "eslint-plugin-import": "^2.22.0", "eslint-plugin-node": "^11.1.0", - "gts": "^3.0.0", - "mocha": "^10.0.0", - "rimraf": "^3.0.2", - "rollup": "^2.23.1", - "rollup-plugin-ts": "^3.0.2", + "gts": "^6.0.2", + "mocha": "^11.1.0", + "rimraf": "^6.0.1", "standardx": "^7.0.0", - "typescript": "^4.0.0" + "typescript": "^5.8.2" }, "files": [ "build", @@ -78,6 +67,6 @@ "!*.d.ts" ], "engines": { - "node": ">=12" + "node": ">=20" } } diff --git a/test/cliui.cjs b/test/cliui.mjs similarity index 98% rename from test/cliui.cjs rename to test/cliui.mjs index 6dd86e7e..b2d5aa02 100644 --- a/test/cliui.cjs +++ b/test/cliui.mjs @@ -1,16 +1,14 @@ -'use strict' +import chalk from 'chalk' +import cliui from '../index.mjs' +import stripAnsi from 'strip-ansi' +import { should } from 'chai' /* global describe, it */ - -require('chai').should() +should() // Force chalk to enable color, if it's disabled the test fails. process.env.FORCE_COLOR = 1 -const chalk = require('chalk') -const cliui = require('../build/index.cjs') -const stripAnsi = require('strip-ansi') - describe('cliui', () => { describe('resetOutput', () => { it('should set lines to empty', () => { diff --git a/test/deno/cliui-test.ts b/test/deno/cliui-test.ts index ce24068d..2f3bcaf1 100644 --- a/test/deno/cliui-test.ts +++ b/test/deno/cliui-test.ts @@ -6,6 +6,8 @@ import { } from 'https://deno.land/std/testing/asserts.ts' import cliui from '../../deno.ts' +// Just run a couple of the tests as a light check working from the Deno runtime. + Deno.test("wraps text at 'width' if a single column is given", () => { const ui = cliui({ width: 10 @@ -39,10 +41,11 @@ Deno.test('evenly divides text across columns if multiple columns are given', () // TODO: we should flesh out the Deno and ESM implementation // such that it spreads words out over multiple columns appropriately: const expected = [ - 'i am a string ti am a seconi am a third', - 'hat should be wd string tha string that', - 'rapped t should be should be w', - ' wrapped rapped' + 'i am a string i am a i am a third', + 'that should be second string that', + 'wrapped string that should be', + ' should be wrapped', + ' wrapped' ] ui.toString().split('\n').forEach((line: string, i: number) => { diff --git a/test/esm/cliui-test.mjs b/test/esm/cliui-test.mjs deleted file mode 100644 index f57d77d1..00000000 --- a/test/esm/cliui-test.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import {ok as assert, strictEqual} from 'assert' -import cliui from '../../index.mjs' - -describe('ESM', () => { - it("wraps text at 'width' if a single column is given", () => { - const ui = cliui({ - width: 10 - }) - - ui.div('i am a string that should be wrapped') - - ui.toString().split('\n').forEach((row) => { - assert(row.length <= 10) - }) - }) - - it('evenly divides text across columns if multiple columns are given', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - { text: 'i am a string that should be wrapped', width: 15 }, - 'i am a second string that should be wrapped', - 'i am a third string that should be wrapped' - ) - - // total width of all columns is <= - // the width cliui is initialized with. - ui.toString().split('\n').forEach((row) => { - assert(row.length <= 40) - }) - - // it should wrap each column appropriately. - // TODO: we should flesh out the Deno and ESM implementation - // such that it spreads words out over multiple columns appropriately: - const expected = [ - 'i am a string ti am a seconi am a third', - 'hat should be wd string tha string that', - 'rapped t should be should be w', - ' wrapped rapped' - ] - ui.toString().split('\n').forEach((line, i) => { - strictEqual(line, expected[i]) - }) - }) -}) \ No newline at end of file