diff --git a/README.md b/README.md index d07466d..fd2cb37 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@
Generates borderless text table strings suitable for printing to stdout. Fast. 🏁
++ Generates borderless text table strings suitable for printing to stdout. + Fast. + 🏁 +
@@ -22,11 +26,132 @@ npm i text-table-fast
```
```ts
-import { greet } from "text-table-fast";
+import { textTable } from "text-table-fast";
+
+console.log(
+ textTable([
+ ["main", "0123456789abcdef"],
+ ["staging", "fedcba9876543210"],
+ ]),
+);
+```
+
+```plaintext
+main 0123456789abcdef
+staging fedcba9876543210
+```
+
+`textTable` takes in an array of arrays containing strings, numbers, or other printable values.
+
+## Options
+
+`text-table-fast`'s `textTable` can take in an optional second parameter as an object with options
+
+> 🔄 These options are equivalent to [`text-table`](https://www.npmjs.com/package/text-table)'s options, but with expanded names.
+
+### `align`
+
+- Default: `[]`
+- Type: `("center" | "left" | "right")[]`
+
+The alignment for columns, in order.
+These each default to `"left"`.
+
+```ts
+import { textTable } from "text-table-fast";
+
+console.log(
+ textTable(
+ [
+ ["abc", "abcd", "ab"],
+ [1234, 12, 1234],
+ ],
+ {
+ alignment: ["left", "center", "right"],
+ },
+ ),
+);
+```
-greet("Hello, world! 💖");
+```plaintext
+abc abcd abc
+1234 12 1234
```
+### `horizontalSeparator`
+
+- Default: `" "`
+- Type: `string`
+
+Characters to put between each column.
+
+```ts
+import { textTable } from "text-table-fast";
+
+console.log(
+ textTable(
+ [
+ ["abc", "abcd", "ab"],
+ [1234, 12, 1234],
+ ],
+ {
+ horizontalSeparator: " | ",
+ },
+ ),
+);
+```
+
+```plaintext
+abc | abcd | abc
+1234 | 12 | 1234
+```
+
+### `stringLength`
+
+- Default: `(value) => String(value).length`
+- Type: `(value: string) => number`
+
+How to compute the length of strings, such as for stripping ANSI characters.
+
+```ts
+import color from "cli-color";
+import { textTable } from "text-table-fast";
+
+console.log(
+ textTable(
+ [
+ [color.red("abc"), color.blue("def")],
+ [12, 34],
+ ],
+ {
+ stringLength: (value) => color.strip(value).length,
+ },
+ ),
+);
+```
+
+```plaintext
+\x1B[31mabc\x1B[39m \x1B[34mdef\x1B[39m
+12 34
+```
+
+## Comparison to [`text-table`](https://www.npmjs.com/package/text-table)
+
+`text-table-fast` has three advantages over `text-table`:
+
+- It is fast in almost all scenarios, and significantly faster on larger tables.
+- It is under active maintenance, whereas `text-table` hasn't been updated in over a decade.
+- It's written in TypeScript and ships with its own `.d.ts` types, whereas `text-table` requires `@types/text-table` for typings.
+
+### Performance Comparison
+
+`text-table-fast` contains two meaningful optimizations over `text-table`:
+
+- `text-table` includes usage of of [an quadratically expensive `/\s+$/`](https://ota-meshi.github.io/eslint-plugin-regexp/playground/#eJyrVkrOT0lVslLSj4kp1lbRV6oFADQgBS4=); `text-table-fast` uses [`String.prototype.trimEnd`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) instead.
+- `text-table` executes a regular expression match on each row cell for its `'.'` (decimal) alignment option; `text-table-fast` will skip that match if and when decimal alignment support is added.
+
+> ESLint issue to be filed soon with a performance comparison. ⚡️
+
## Contributors
@@ -47,6 +172,9 @@ greet("Hello, world! 💖");
-
+## Acknowledgements
+
+This package is a near-drop-in replacement for venerable [`text-table`](https://www.npmjs.com/package/text-table), which has served a plethora of projects -including ESLint- well for over a decade.
+Many thanks to substack for creating the original `text-table` package! 💖
> 💙 This package was templated with [`create-typescript-app`](https://github.com/JoshuaKGoldberg/create-typescript-app).
diff --git a/cspell.json b/cspell.json
index 21e111e..b65fdfc 100644
--- a/cspell.json
+++ b/cspell.json
@@ -8,5 +8,16 @@
"node_modules",
"pnpm-lock.yaml"
],
- "words": ["borderless", "knip", "packagejson", "tseslint", "tsup"]
+ "words": [
+ "borderless",
+ "hsep",
+ "knip",
+ "mabc",
+ "mdef",
+ "packagejson",
+ "substack",
+ "tseslint",
+ "tsup",
+ "vitest"
+ ]
}
diff --git a/eslint.config.js b/eslint.config.js
index 22bfb19..2aeae40 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -5,7 +5,7 @@ import jsonc from "eslint-plugin-jsonc";
import markdown from "eslint-plugin-markdown";
import n from "eslint-plugin-n";
import packageJson from "eslint-plugin-package-json/configs/recommended";
-import perfectionistNatural from "eslint-plugin-perfectionist/configs/recommended-natural";
+import perfectionist from "eslint-plugin-perfectionist";
import * as regexp from "eslint-plugin-regexp";
import vitest from "eslint-plugin-vitest";
import yml from "eslint-plugin-yml";
@@ -35,7 +35,9 @@ export default tseslint.config(
jsdoc.configs["flat/recommended-typescript-error"],
n.configs["flat/recommended"],
packageJson,
- perfectionistNatural,
+ // After updating for https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1588 ...
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
+ perfectionist.configs["recommended-natural"],
regexp.configs["flat/recommended"],
...tseslint.config({
extends: [
@@ -74,7 +76,7 @@ export default tseslint.config(
"error",
{
order: "asc",
- "partition-by-comment": true,
+ partitionByComment: true,
type: "natural",
},
],
@@ -97,6 +99,7 @@ export default tseslint.config(
files: ["**/*.md/*.ts"],
rules: {
"n/no-missing-import": ["error", { allowModules: ["text-table-fast"] }],
+ "n/no-unpublished-import": ["error", { allowModules: ["cli-color"] }],
},
},
{
diff --git a/knip.json b/knip.json
index d73a20f..2600f80 100644
--- a/knip.json
+++ b/knip.json
@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/knip@latest/schema.json",
- "entry": ["src/index.ts!"],
+ "entry": ["src/index.ts!", "src/**/*.test.ts!"],
"ignoreExportsUsedInFile": { "interface": true, "type": true },
"project": ["src/**/*.ts!"]
}
diff --git a/package.json b/package.json
index 77c6847..76f0f8e 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,11 @@
"@eslint-community/eslint-plugin-eslint-comments": "^4.3.0",
"@eslint/js": "^9.7.0",
"@release-it/conventional-changelog": "^8.0.1",
+ "@types/cli-color": "^2.0.6",
"@types/eslint-plugin-markdown": "^2.0.2",
+ "@types/eslint__js": "^8.42.3",
"@vitest/coverage-v8": "^2.0.4",
+ "cli-color": "^2.0.4",
"console-fail-test": "^0.4.4",
"cspell": "^8.11.0",
"eslint": "^9.7.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a9a554a..0343354 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,12 +17,21 @@ importers:
'@release-it/conventional-changelog':
specifier: ^8.0.1
version: 8.0.1(release-it@17.6.0(typescript@5.5.3))
+ '@types/cli-color':
+ specifier: ^2.0.6
+ version: 2.0.6
'@types/eslint-plugin-markdown':
specifier: ^2.0.2
version: 2.0.2
+ '@types/eslint__js':
+ specifier: ^8.42.3
+ version: 8.42.3
'@vitest/coverage-v8':
specifier: ^2.0.4
version: 2.0.4(vitest@2.0.4(@types/node@20.14.11))
+ cli-color:
+ specifier: ^2.0.4
+ version: 2.0.4
console-fail-test:
specifier: ^0.4.4
version: 0.4.4
@@ -918,12 +927,18 @@ packages:
'@tootallnate/quickjs-emscripten@0.23.0':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
+ '@types/cli-color@2.0.6':
+ resolution: {integrity: sha512-uLK0/0dOYdkX8hNsezpYh1gc8eerbhf9bOKZ3e24sP67703mw9S14/yW6mSTatiaKO9v+mU/a1EVy4rOXXeZTA==}
+
'@types/eslint-plugin-markdown@2.0.2':
resolution: {integrity: sha512-ImmEw5xBVb9vCaFfQ+5kUcVatUO4XPpTvryAmhpKzalUKhDb3EZmeuHvIUO6E1/WDOTw+/b9qlWsZhxULhZdfQ==}
'@types/eslint@8.56.10':
resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==}
+ '@types/eslint__js@8.42.3':
+ resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==}
+
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
@@ -1258,6 +1273,10 @@ packages:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
engines: {node: '>=10'}
+ cli-color@2.0.4:
+ resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==}
+ engines: {node: '>=0.10'}
+
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@@ -1465,6 +1484,10 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ d@1.0.2:
+ resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
+ engines: {node: '>=0.12'}
+
dargs@8.1.0:
resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==}
engines: {node: '>=12'}
@@ -1595,6 +1618,20 @@ packages:
es-module-lexer@1.5.4:
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
+ es5-ext@0.10.64:
+ resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
+ engines: {node: '>=0.10'}
+
+ es6-iterator@2.0.3:
+ resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
+
+ es6-symbol@3.1.4:
+ resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
+ engines: {node: '>=0.12'}
+
+ es6-weak-map@2.0.3:
+ resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
+
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
@@ -1726,6 +1763,10 @@ packages:
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
+ esniff@2.0.1:
+ resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
+ engines: {node: '>=0.10'}
+
espree@10.1.0:
resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1758,6 +1799,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ event-emitter@0.3.5:
+ resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
+
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
@@ -1769,6 +1813,9 @@ packages:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
+ ext@1.7.0:
+ resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
+
external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
@@ -2182,6 +2229,9 @@ packages:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
+ is-promise@2.2.2:
+ resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
+
is-ssh@1.4.0:
resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==}
@@ -2417,6 +2467,9 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
+ lru-queue@0.1.0:
+ resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
+
macos-release@3.2.0:
resolution: {integrity: sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -2468,6 +2521,10 @@ packages:
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+ memoizee@0.4.17:
+ resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==}
+ engines: {node: '>=0.12'}
+
meow@12.1.1:
resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==}
engines: {node: '>=16.10'}
@@ -2566,6 +2623,9 @@ packages:
resolution: {integrity: sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ next-tick@1.1.0:
+ resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
+
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@@ -3229,6 +3289,10 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+ timers-ext@0.1.8:
+ resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
+ engines: {node: '>=0.12'}
+
tinybench@2.8.0:
resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==}
@@ -3318,6 +3382,9 @@ packages:
resolution: {integrity: sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==}
engines: {node: '>=16'}
+ type@2.7.3:
+ resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
+
typedarray-to-buffer@3.1.5:
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
@@ -4214,6 +4281,8 @@ snapshots:
'@tootallnate/quickjs-emscripten@0.23.0': {}
+ '@types/cli-color@2.0.6': {}
+
'@types/eslint-plugin-markdown@2.0.2':
dependencies:
'@types/eslint': 8.56.10
@@ -4224,6 +4293,10 @@ snapshots:
'@types/estree': 1.0.5
'@types/json-schema': 7.0.15
+ '@types/eslint__js@8.42.3':
+ dependencies:
+ '@types/eslint': 8.56.10
+
'@types/estree@1.0.5': {}
'@types/glob@7.2.0':
@@ -4601,6 +4674,14 @@ snapshots:
cli-boxes@3.0.0: {}
+ cli-color@2.0.4:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-iterator: 2.0.3
+ memoizee: 0.4.17
+ timers-ext: 0.1.8
+
cli-cursor@3.1.0:
dependencies:
restore-cursor: 3.1.0
@@ -4869,6 +4950,11 @@ snapshots:
strip-ansi: 7.1.0
vscode-uri: 3.0.8
+ d@1.0.2:
+ dependencies:
+ es5-ext: 0.10.64
+ type: 2.7.3
+
dargs@8.1.0: {}
data-uri-to-buffer@4.0.1: {}
@@ -4965,6 +5051,31 @@ snapshots:
es-module-lexer@1.5.4: {}
+ es5-ext@0.10.64:
+ dependencies:
+ es6-iterator: 2.0.3
+ es6-symbol: 3.1.4
+ esniff: 2.0.1
+ next-tick: 1.1.0
+
+ es6-iterator@2.0.3:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-symbol: 3.1.4
+
+ es6-symbol@3.1.4:
+ dependencies:
+ d: 1.0.2
+ ext: 1.7.0
+
+ es6-weak-map@2.0.3:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-iterator: 2.0.3
+ es6-symbol: 3.1.4
+
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
@@ -5193,6 +5304,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ esniff@2.0.1:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ event-emitter: 0.3.5
+ type: 2.7.3
+
espree@10.1.0:
dependencies:
acorn: 8.12.1
@@ -5223,6 +5341,11 @@ snapshots:
esutils@2.0.3: {}
+ event-emitter@0.3.5:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+
eventemitter3@5.0.1: {}
execa@5.1.1:
@@ -5249,6 +5372,10 @@ snapshots:
signal-exit: 4.1.0
strip-final-newline: 3.0.0
+ ext@1.7.0:
+ dependencies:
+ type: 2.7.3
+
external-editor@3.1.0:
dependencies:
chardet: 0.7.0
@@ -5651,6 +5778,8 @@ snapshots:
is-plain-obj@4.1.0: {}
+ is-promise@2.2.2: {}
+
is-ssh@1.4.0:
dependencies:
protocols: 2.0.1
@@ -5887,6 +6016,10 @@ snapshots:
lru-cache@7.18.3: {}
+ lru-queue@0.1.0:
+ dependencies:
+ es5-ext: 0.10.64
+
macos-release@3.2.0: {}
magic-string@0.30.10:
@@ -5961,6 +6094,17 @@ snapshots:
mdurl@2.0.0: {}
+ memoizee@0.4.17:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-weak-map: 2.0.3
+ event-emitter: 0.3.5
+ is-promise: 2.2.2
+ lru-queue: 0.1.0
+ next-tick: 1.1.0
+ timers-ext: 0.1.8
+
meow@12.1.1: {}
merge-stream@2.0.0: {}
@@ -6037,6 +6181,8 @@ snapshots:
dependencies:
type-fest: 2.19.0
+ next-tick@1.1.0: {}
+
node-domexception@1.0.0: {}
node-fetch@3.3.2:
@@ -6736,6 +6882,11 @@ snapshots:
through@2.3.8: {}
+ timers-ext@0.1.8:
+ dependencies:
+ es5-ext: 0.10.64
+ next-tick: 1.1.0
+
tinybench@2.8.0: {}
tinypool@1.0.0: {}
@@ -6809,6 +6960,8 @@ snapshots:
type-fest@4.23.0: {}
+ type@2.7.3: {}
+
typedarray-to-buffer@3.1.5:
dependencies:
is-typedarray: 1.0.0
diff --git a/src/greet.test.ts b/src/greet.test.ts
deleted file mode 100644
index f729115..0000000
--- a/src/greet.test.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { describe, expect, it, vi } from "vitest";
-
-import { greet } from "./greet.js";
-
-const message = "Yay, testing!";
-
-describe("greet", () => {
- it("logs to the console once when message is provided as a string", () => {
- const logger = vi.spyOn(console, "log").mockImplementation(() => undefined);
-
- greet(message);
-
- expect(logger).toHaveBeenCalledWith(message);
- expect(logger).toHaveBeenCalledTimes(1);
- });
-
- it("logs to the console once when message is provided as an object", () => {
- const logger = vi.spyOn(console, "log").mockImplementation(() => undefined);
-
- greet({ message });
-
- expect(logger).toHaveBeenCalledWith(message);
- expect(logger).toHaveBeenCalledTimes(1);
- });
-
- it("logs once when times is not provided in an object", () => {
- const logger = vi.fn();
-
- greet({ logger, message });
-
- expect(logger).toHaveBeenCalledWith(message);
- expect(logger).toHaveBeenCalledTimes(1);
- });
-
- it("logs a specified number of times when times is provided", () => {
- const logger = vi.fn();
- const times = 7;
-
- greet({ logger, message, times });
-
- expect(logger).toHaveBeenCalledWith(message);
- expect(logger).toHaveBeenCalledTimes(7);
- });
-});
diff --git a/src/greet.ts b/src/greet.ts
deleted file mode 100644
index a0d3b4c..0000000
--- a/src/greet.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { GreetOptions } from "./types.js";
-
-export function greet(options: GreetOptions | string) {
- const {
- logger = console.log.bind(console),
- message,
- times = 1,
- } = typeof options === "string" ? { message: options } : options;
-
- for (let i = 0; i < times; i += 1) {
- logger(message);
- }
-}
diff --git a/src/index.ts b/src/index.ts
index a39b40f..e1b1c51 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,2 +1,68 @@
-export * from "./greet.js";
-export * from "./types.js";
+export type GetStringLength = (value: string) => number;
+
+export type TableAlignment = "center" | "left" | "right";
+
+export interface TextTableOptions {
+ /**
+ * The alignment for columns, in order.
+ */
+ align?: TableAlignment[];
+
+ /**
+ * Characters to put between each column.
+ */
+ horizontalSeparator?: string;
+
+ /**
+ * How to compute the length of strings, such as for stripping ANSI characters.
+ */
+ stringLength?: GetStringLength;
+}
+
+const defaultStringLength: GetStringLength = (value) => String(value).length;
+
+export function textTable(rows: string[][], opts: TextTableOptions = {}) {
+ const {
+ align = [],
+ horizontalSeparator = " ",
+ stringLength = defaultStringLength,
+ } = opts;
+
+ const sizes = rows.reduce