diff --git a/deno.ts b/deno.ts index a94e49f..2cfaaa8 100644 --- a/deno.ts +++ b/deno.ts @@ -1,7 +1,8 @@ // 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 wrap from 'wrap-ansi'; +import stripAnsi from 'strip-ansi'; export default function ui (opts: UIOptions): UI { return cliui(opts, { diff --git a/index.mjs b/index.mjs index bc7a022..9f4ec06 100644 --- a/index.mjs +++ b/index.mjs @@ -1,6 +1,7 @@ // Bootstrap cliui with CommonJS dependencies: import { cliui } from './build/lib/index.js' -import { wrap, stripAnsi } from './build/lib/string-utils.js' +import wrap from 'wrap-ansi'; +import stripAnsi from 'strip-ansi'; export default function ui (opts) { return cliui(opts, { diff --git a/lib/cjs.ts b/lib/cjs.ts index bda4241..cf52e36 100644 --- a/lib/cjs.ts +++ b/lib/cjs.ts @@ -1,8 +1,8 @@ // 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') +import stringWidth from 'string-width' +import wrap from 'wrap-ansi' +import stripAnsi from 'strip-ansi' export default function ui (opts: UIOptions) { return cliui(opts, { stringWidth, diff --git a/package.json b/package.json index 1eaac1b..110a198 100644 --- a/package.json +++ b/package.json @@ -49,15 +49,17 @@ "author": "Ben Coe ", "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@rollup/plugin-commonjs": "^22.0.0", + "@types/wrap-ansi": "^8.0.1", + "string-width": "^5.1.2", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "devDependencies": { + "@rollup/plugin-node-resolve": "^13.3.0", "@types/node": "^14.0.27", "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^4.0.0", - "@wessberg/rollup-plugin-ts": "^1.3.2", "c8": "^7.3.0", "chai": "^4.2.0", "chalk": "^4.1.0", @@ -69,6 +71,7 @@ "mocha": "^9.1.3", "rimraf": "^3.0.2", "rollup": "^2.23.1", + "rollup-plugin-ts": "^2.0.7", "standardx": "^7.0.0", "typescript": "^4.0.0" }, diff --git a/rollup.config.js b/rollup.config.js index ec8a2ac..804e583 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,4 +1,6 @@ -import ts from '@wessberg/rollup-plugin-ts' +import ts from 'rollup-plugin-ts' +import nodeResolve from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' const output = { format: 'cjs', @@ -12,6 +14,13 @@ export default { input: './lib/cjs.ts', output, plugins: [ - ts({ /* options */ }) + ts({ /* options */ }), + nodeResolve(), + commonjs({ + include: [ + 'node_modules/emoji-regex/index.js', + 'node_modules/eastasianwidth/eastasianwidth.js' + ] + }) ] } diff --git a/test/cliui.cjs b/test/cliui.cjs index 6dd86e7..8ca1394 100644 --- a/test/cliui.cjs +++ b/test/cliui.cjs @@ -7,9 +7,7 @@ require('chai').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', () => { @@ -178,315 +176,4 @@ describe('cliui', () => { ui.toString().split('\n').should.eql(expected) }) }) - - describe('padding', () => { - it('handles left/right padding', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - { text: 'i have padding on my left', padding: [0, 0, 0, 4] }, - { text: 'i have padding on my right', padding: [0, 2, 0, 0], align: 'center' }, - { text: 'i have no padding', padding: [0, 0, 0, 0] } - ) - - // it should add left/right padding to columns. - const expected = [ - ' i have i have i have no', - ' padding padding on padding', - ' on my my right', - ' left' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('handles top/bottom padding', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - 'i am a string', - { text: 'i am a second string', padding: [2, 0, 0, 0] }, - { text: 'i am a third string that should be wrapped', padding: [0, 0, 1, 0] } - ) - - // it should add top/bottom padding to second - // and third columns. - const expected = [ - 'i am a string i am a third', - ' string that', - ' i am a secondshould be', - ' string wrapped', - '' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('preserves leading whitespace as padding', () => { - const ui = cliui() - - ui.div(' LEADING WHITESPACE') - ui.div('\u001b[34m with ansi\u001b[39m') - - const expected = [ - ' LEADING WHITESPACE', - ' with ansi' - ] - - ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) - }) - }) - - describe('border', () => { - it('allows a border to be placed around a div', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - { text: 'i am a first string', padding: [0, 0, 0, 0], border: true }, - { text: 'i am a second string', padding: [1, 0, 0, 0], border: true } - ) - - const expected = [ - '.------------------.', - '| i am a first |.------------------.', - '| string || i am a second |', - "'------------------'| string |", - " '------------------'" - ] - - ui.toString().split('\n').should.eql(expected) - }) - }) - - describe('wrap', () => { - it('allows wordwrap to be disabled', () => { - const ui = cliui({ - wrap: false - }) - - ui.div( - { text: 'i am a string', padding: [0, 1, 0, 0] }, - { text: 'i am a second string', padding: [0, 2, 0, 0] }, - { text: 'i am a third string that should not be wrapped', padding: [0, 0, 0, 2] } - ) - - ui.toString().should.equal('i am a string i am a second string i am a third string that should not be wrapped') - }) - }) - - describe('span', () => { - it('appends the next row to the end of the prior row if it fits', () => { - const ui = cliui({ - width: 40 - }) - - ui.span( - { text: 'i am a string that will be wrapped', width: 30 } - ) - - ui.div( - { text: ' [required] [default: 99]', align: 'right' } - ) - - const expected = [ - 'i am a string that will be', - 'wrapped [required] [default: 99]' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('does not append the string if it does not fit on the prior row', () => { - const ui = cliui({ - width: 40 - }) - - ui.span( - { text: 'i am a string that will be wrapped', width: 30 } - ) - - ui.div( - { text: 'i am a second row', align: 'left' } - ) - - const expected = [ - 'i am a string that will be', - 'wrapped', - 'i am a second row' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('always appends text to prior span if wrap is disabled', () => { - const ui = cliui({ - wrap: false, - width: 40 - }) - - ui.span( - { text: 'i am a string that will be wrapped', width: 30 } - ) - - ui.div( - { text: 'i am a second row', align: 'left', padding: [0, 0, 0, 3] } - ) - - ui.div('a third line') - - const expected = [ - 'i am a string that will be wrapped i am a second row', - 'a third line' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('appends to prior line appropriately when strings contain ansi escape codes', () => { - const ui = cliui({ - width: 40 - }) - - ui.span( - { text: chalk.green('i am a string that will be wrapped'), width: 30 } - ) - - ui.div( - { text: chalk.blue(' [required] [default: 99]'), align: 'right' } - ) - - const expected = [ - 'i am a string that will be', - 'wrapped [required] [default: 99]' - ] - - ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) - }) - }) - - describe('layoutDSL', () => { - it('turns tab into multiple columns', () => { - const ui = cliui({ - width: 60 - }) - - ui.div( - ' \tmy awesome regex\n \tanother row\t a third column' - ) - - const expected = [ - ' my awesome regex', - ' another row a third column' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('turns newline into multiple rows', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - 'Usage: $0\n \t my awesome regex\n \t my awesome glob\t [required]' - ) - const expected = [ - 'Usage: $0', - ' my awesome regex', - ' my awesome [required]', - ' glob' - ] - - ui.toString().split('\n').should.eql(expected) - }) - - it('aligns rows appropriately when they contain ansi escape codes', () => { - const ui = cliui({ - width: 40 - }) - - ui.div( - ' \t ' + chalk.red('my awesome regex') + '\t [regex]\n ' + chalk.blue('') + '\t my awesome glob\t [required]' - ) - - const expected = [ - ' my awesome [regex]', - ' regex', - ' my awesome [required]', - ' glob' - ] - - ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) - }) - - it('ignores ansi escape codes when measuring padding', () => { - // Forcefully enable color-codes for this test - const { enabled, level } = chalk - chalk.enabled = true - chalk.level = 1 - - const ui = cliui({ - width: 25 - }) - - // using figlet font 'Shadow' rendering of text 'true' here - ui.div( - chalk.blue(' | \n __| __| | | _ \\ \n | | | | __/ \n \\__| _| \\__,_| \\___| \n ') - ) - - // relevant part is first line - leading whitespace should be preserved as left padding - const expected = [ - ' |', - ' __| __| | | _ \\', - ' | | | | __/', - ' \\__| _| \\__,_| \\___|', - ' ' - ] - - ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) - chalk.enabled = enabled - chalk.level = level - }) - - it('correctly handles lack of ansi escape codes when measuring padding', () => { - const ui = cliui({ - width: 25 - }) - - // using figlet font 'Shadow' rendering of text 'true' here - ui.div( - ' | \n __| __| | | _ \\ \n | | | | __/ \n \\__| _| \\__,_| \\___| \n ' - ) - - // The difference - const expected = [ - ' |', - ' __| __| | | _ \\', - ' | | | | __/', - ' \\__| _| \\__,_| \\___|', - '' - ] - - ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) - }) - - it('does not apply DSL if wrap is false', () => { - const ui = cliui({ - width: 40, - wrap: false - }) - - ui.div( - 'Usage: $0\ttwo\tthree' - ) - - ui.toString().should.eql('Usage: $0\ttwo\tthree') - }) - }) }) diff --git a/test/esm/cliui-test.mjs b/test/esm/cliui-test.mjs index f57d77d..13a465c 100644 --- a/test/esm/cliui-test.mjs +++ b/test/esm/cliui-test.mjs @@ -1,6 +1,9 @@ import {ok as assert, strictEqual} from 'assert' import cliui from '../../index.mjs' +import chalk from 'chalk' +import stripAnsi from 'strip-ansi' + describe('ESM', () => { it("wraps text at 'width' if a single column is given", () => { const ui = cliui({ @@ -44,4 +47,316 @@ describe('ESM', () => { strictEqual(line, expected[i]) }) }) + + describe('padding', () => { + it('handles left/right padding', () => { + const ui = cliui({ + width: 40 + }) + + ui.div( + { text: 'i have padding on my left', padding: [0, 0, 0, 4] }, + { text: 'i have padding on my right', padding: [0, 2, 0, 0], align: 'center' }, + { text: 'i have no padding', padding: [0, 0, 0, 0] } + ) + + // it should add left/right padding to columns. + const expected = [ + ' i have i have i have no', + ' padding padding on padding', + ' on my my right', + ' left' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('handles top/bottom padding', () => { + const ui = cliui({ + width: 40 + }) + + ui.div( + 'i am a string', + { text: 'i am a second string', padding: [2, 0, 0, 0] }, + { text: 'i am a third string that should be wrapped', padding: [0, 0, 1, 0] } + ) + + // it should add top/bottom padding to second + // and third columns. + const expected = [ + 'i am a string i am a third', + ' string that', + ' i am a secondshould be', + ' string wrapped', + '' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('preserves leading whitespace as padding', () => { + const ui = cliui() + + ui.div(' LEADING WHITESPACE') + ui.div('\u001b[34m with ansi\u001b[39m') + + const expected = [ + ' LEADING WHITESPACE', + ' with ansi' + ] + + ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) + }) + }) + + describe('border', () => { + it('allows a border to be placed around a div', () => { + const ui = cliui({ + width: 40 + }) + + ui.div( + { text: 'i am a first string', padding: [0, 0, 0, 0], border: true }, + { text: 'i am a second string', padding: [1, 0, 0, 0], border: true } + ) + + const expected = [ + '.------------------.', + '| i am a first |.------------------.', + '| string || i am a second |', + "'------------------'| string |", + " '------------------'" + ] + + ui.toString().split('\n').should.eql(expected) + }) + }) + + describe('wrap', () => { + it('allows wordwrap to be disabled', () => { + const ui = cliui({ + wrap: false + }) + + ui.div( + { text: 'i am a string', padding: [0, 1, 0, 0] }, + { text: 'i am a second string', padding: [0, 2, 0, 0] }, + { text: 'i am a third string that should not be wrapped', padding: [0, 0, 0, 2] } + ) + + ui.toString().should.equal('i am a string i am a second string i am a third string that should not be wrapped') + }) + }) + + describe('span', () => { + it('appends the next row to the end of the prior row if it fits', () => { + const ui = cliui({ + width: 40 + }) + + ui.span( + { text: 'i am a string that will be wrapped', width: 30 } + ) + + ui.div( + { text: ' [required] [default: 99]', align: 'right' } + ) + + const expected = [ + 'i am a string that will be', + 'wrapped [required] [default: 99]' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('does not append the string if it does not fit on the prior row', () => { + const ui = cliui({ + width: 40 + }) + + ui.span( + { text: 'i am a string that will be wrapped', width: 30 } + ) + + ui.div( + { text: 'i am a second row', align: 'left' } + ) + + const expected = [ + 'i am a string that will be', + 'wrapped', + 'i am a second row' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('always appends text to prior span if wrap is disabled', () => { + const ui = cliui({ + wrap: false, + width: 40 + }) + + ui.span( + { text: 'i am a string that will be wrapped', width: 30 } + ) + + ui.div( + { text: 'i am a second row', align: 'left', padding: [0, 0, 0, 3] } + ) + + ui.div('a third line') + + const expected = [ + 'i am a string that will be wrapped i am a second row', + 'a third line' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('appends to prior line appropriately when strings contain ansi escape codes', () => { + const ui = cliui({ + width: 40 + }) + + ui.span( + { text: chalk.green('i am a string that will be wrapped'), width: 30 } + ) + + ui.div( + { text: chalk.blue(' [required] [default: 99]'), align: 'right' } + ) + + const expected = [ + 'i am a string that will be', + 'wrapped [required] [default: 99]' + ] + + ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) + }) + }) + + describe('layoutDSL', () => { + it('turns tab into multiple columns', () => { + const ui = cliui({ + width: 60 + }) + + ui.div( + ' \tmy awesome regex\n \tanother row\t a third column' + ) + + const expected = [ + ' my awesome regex', + ' another row a third column' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('turns newline into multiple rows', () => { + const ui = cliui({ + width: 40 + }) + + ui.div( + 'Usage: $0\n \t my awesome regex\n \t my awesome glob\t [required]' + ) + const expected = [ + 'Usage: $0', + ' my awesome regex', + ' my awesome [required]', + ' glob' + ] + + ui.toString().split('\n').should.eql(expected) + }) + + it('aligns rows appropriately when they contain ansi escape codes', () => { + const ui = cliui({ + width: 40 + }) + + ui.div( + ' \t ' + chalk.red('my awesome regex') + '\t [regex]\n ' + chalk.blue('') + '\t my awesome glob\t [required]' + ) + + const expected = [ + ' my awesome [regex]', + ' regex', + ' my awesome [required]', + ' glob' + ] + + ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) + }) + + it('ignores ansi escape codes when measuring padding', () => { + // Forcefully enable color-codes for this test + const { enabled, level } = chalk + chalk.enabled = true + chalk.level = 1 + + const ui = cliui({ + width: 25 + }) + + // using figlet font 'Shadow' rendering of text 'true' here + ui.div( + chalk.blue(' | \n __| __| | | _ \\ \n | | | | __/ \n \\__| _| \\__,_| \\___| \n ') + ) + + // relevant part is first line - leading whitespace should be preserved as left padding + const expected = [ + ' |', + ' __| __| | | _ \\', + ' | | | | __/', + ' \\__| _| \\__,_| \\___|', + ' ' + ] + + ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) + chalk.enabled = enabled + chalk.level = level + }) + + it('correctly handles lack of ansi escape codes when measuring padding', () => { + const ui = cliui({ + width: 25 + }) + + // using figlet font 'Shadow' rendering of text 'true' here + ui.div( + ' | \n __| __| | | _ \\ \n | | | | __/ \n \\__| _| \\__,_| \\___| \n ' + ) + + // The difference + const expected = [ + ' |', + ' __| __| | | _ \\', + ' | | | | __/', + ' \\__| _| \\__,_| \\___|', + '' + ] + + ui.toString().split('\n').map(l => stripAnsi(l)).should.eql(expected) + }) + + it('does not apply DSL if wrap is false', () => { + const ui = cliui({ + width: 40, + wrap: false + }) + + ui.div( + 'Usage: $0\ttwo\tthree' + ) + + ui.toString().should.eql('Usage: $0\ttwo\tthree') + }) + }) + }) \ No newline at end of file