@heroku/heroku-cli-util 8.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/styled.js ADDED
@@ -0,0 +1,131 @@
1
+ 'use strict'
2
+
3
+ let cli = require('..')
4
+ let util = require('util')
5
+
6
+ /**
7
+ * styledJSON prints out colored, indented JSON
8
+ *
9
+ * @example
10
+ * styledHeader({foo: 'bar'}) # Outputs === {
11
+ * "foo": "bar"
12
+ * }
13
+ *
14
+ * @param {obj} object data to display
15
+ * @returns {null}
16
+ */
17
+ function styledJSON (obj) {
18
+ let json = JSON.stringify(obj, null, 2)
19
+ if (cli.color.enabled) {
20
+ let cardinal = require('cardinal')
21
+ let theme = require('cardinal/themes/jq')
22
+ cli.log(cardinal.highlight(json, { json: true, theme: theme }))
23
+ } else {
24
+ cli.log(json)
25
+ }
26
+ }
27
+
28
+ /**
29
+ * styledHeader logs in a consistent header style
30
+ *
31
+ * @example
32
+ * styledHeader('MyApp') # Outputs === MyApp
33
+ *
34
+ * @param {header} header text
35
+ * @returns {null}
36
+ */
37
+ function styledHeader (header) {
38
+ cli.log(cli.color.dim('=== ') + cli.color.bold(header))
39
+ }
40
+
41
+ /**
42
+ * styledObject logs an object in a consistent columnar style
43
+ *
44
+ * @example
45
+ * styledObject({name: "myapp", collaborators: ["user1@example.com", "user2@example.com"]})
46
+ * Collaborators: user1@example.com
47
+ * user2@example.com
48
+ * Name: myapp
49
+ *
50
+ * @param {obj} object data to print
51
+ * @param {keys} optional array of keys to sort/filter output
52
+ * @returns {null}
53
+ */
54
+ function styledObject (obj, keys) {
55
+ let keyLengths = Object.keys(obj).map(key => key.toString().length)
56
+ let maxKeyLength = Math.max.apply(Math, keyLengths) + 2
57
+ function pp (obj) {
58
+ if (typeof obj === 'string' || typeof obj === 'number') {
59
+ return obj
60
+ } else if (typeof obj === 'object') {
61
+ return Object.keys(obj).map(k => k + ': ' + util.inspect(obj[k])).join(', ')
62
+ } else {
63
+ return util.inspect(obj)
64
+ }
65
+ }
66
+ function logKeyValue (key, value) {
67
+ cli.log(`${key}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value))
68
+ }
69
+ for (var key of (keys || Object.keys(obj).sort())) {
70
+ let value = obj[key]
71
+ if (Array.isArray(value)) {
72
+ if (value.length > 0) {
73
+ logKeyValue(key, value[0])
74
+ for (var e of value.slice(1)) {
75
+ cli.log(' '.repeat(maxKeyLength) + pp(e))
76
+ }
77
+ }
78
+ } else if (value !== null && value !== undefined) {
79
+ logKeyValue(key, value)
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * styledNameValues logs an array of {name: '', values: ['']} objects in a consistent style
86
+ *
87
+ * @example
88
+ * styledNameValues([{name: "Collaborators", values: ["user1@example.com", "user2@example.com"]},
89
+ * {name: "Name", values: ["myapp"]}])
90
+ * Collaborators: user1@example.com
91
+ * user2@example.com
92
+ * Name: myapp
93
+ *
94
+ * @param {nameValues} nameValues
95
+ * @returns {null}
96
+ */
97
+ function styledNameValues (nameValues) {
98
+ let keys = nameValues.map(nameValue => nameValue.name)
99
+ let keyLengths = keys.map(name => name.toString().length)
100
+ let maxKeyLength = Math.max.apply(Math, keyLengths) + 2
101
+ function pp (obj) {
102
+ if (typeof obj === 'string' || typeof obj === 'number') {
103
+ return obj
104
+ } else if (typeof obj === 'object') {
105
+ return Object.keys(obj).map(k => k + ': ' + cli.inspect(obj[k])).join(', ')
106
+ } else {
107
+ return cli.inspect(obj)
108
+ }
109
+ }
110
+ function logKeyValue (key, value) {
111
+ cli.log(`${key}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value))
112
+ }
113
+ for (var nameValue of nameValues) {
114
+ let value = nameValue.values
115
+ if (Array.isArray(value)) {
116
+ if (value.length > 0) {
117
+ logKeyValue(nameValue.name, value[0])
118
+ for (var e of value.slice(1)) {
119
+ cli.log(' '.repeat(maxKeyLength) + pp(e))
120
+ }
121
+ }
122
+ } else if (value !== null && value !== undefined) {
123
+ logKeyValue(nameValue.name, value)
124
+ }
125
+ }
126
+ }
127
+
128
+ module.exports.styledJSON = styledJSON
129
+ module.exports.styledHeader = styledHeader
130
+ module.exports.styledObject = styledObject
131
+ module.exports.styledNameValues = styledNameValues
package/lib/table.js ADDED
@@ -0,0 +1,132 @@
1
+ 'use strict'
2
+
3
+ const stripAnsi = require('strip-ansi')
4
+ const cli = require('..')
5
+ const { ary, defaults, get, identity, keys, noop, partial, property, result, repeat } = require('lodash')
6
+
7
+ /**
8
+ * Generates a Unicode table and feeds it into configured printer.
9
+ *
10
+ * Top-level arguments:
11
+ *
12
+ * @arg {Object[]} data - the records to format as a table.
13
+ * @arg {Object} options - configuration for the table.
14
+ *
15
+ * @arg {Object[]} [options.columns] - Options for formatting and finding values for table columns.
16
+ * @arg {function(string)} [options.headerAnsi] - Zero-width formattter for entire header.
17
+ * @arg {string} [options.colSep] - Separator between columns.
18
+ * @arg {function(row, options)} [options.after] - Function called after each row is printed.
19
+ * @arg {function(string)} [options.printLine] - Function responsible for printing to terminal.
20
+ * @arg {function(cells)} [options.printHeader] - Function to print header cells as a row.
21
+ * @arg {function(cells)} [options.printRow] - Function to print cells as a row.
22
+ *
23
+ * @arg {function(row)|string} [options.columns[].key] - Path to the value in the row or function to retrieve the pre-formatted value for the cell.
24
+ * @arg {function(string)} [options.columns[].label] - Header name for column.
25
+ * @arg {function(string, row)} [options.columns[].format] - Formatter function for column value.
26
+ * @arg {function(row)} [options.columns[].get] - Function to return a value to be presented in cell without formatting.
27
+ *
28
+ */
29
+ function table (data, options) {
30
+ let _defaults = {
31
+ colSep: ' ',
32
+ after: noop,
33
+ headerAnsi: identity,
34
+ printLine: cli.log,
35
+ printRow: function (cells) {
36
+ this.printLine(cells.join(this.colSep).trimRight())
37
+ },
38
+ printHeader: function (cells) {
39
+ this.printRow(cells.map(ary(this.headerAnsi, 1)))
40
+ this.printRow(cells.map(hdr => hdr.replace(/./g, '─')))
41
+ }
42
+ }
43
+
44
+ let colDefaults = {
45
+ format: value => value ? value.toString() : '',
46
+ width: 0,
47
+ label: function () { return this.key.toString() },
48
+
49
+ get: function (row) {
50
+ let value
51
+ let path = result(this, 'key')
52
+
53
+ if (!path) {
54
+ value = row
55
+ } else {
56
+ value = get(row, path)
57
+ }
58
+
59
+ let formatted = this.format(value, row)
60
+ return formatted == null ? '' : formatted.toString()
61
+ }
62
+ }
63
+
64
+ function calcWidth (cell) {
65
+ let lines = stripAnsi(cell).split(/[\r\n]+/)
66
+ let lineLengths = lines.map(property('length'))
67
+ return Math.max.apply(Math, lineLengths)
68
+ }
69
+
70
+ function pad (string, length) {
71
+ let visibleLength = stripAnsi(string).length
72
+ let diff = length - visibleLength
73
+
74
+ return string + repeat(' ', Math.max(0, diff))
75
+ }
76
+
77
+ function render () {
78
+ let columns = options.columns || keys(data[0] || {})
79
+
80
+ if (typeof columns[0] === 'string') {
81
+ columns = columns.map(key => ({ key }))
82
+ }
83
+
84
+ let defaultsApplied = false
85
+ for (let row of data) {
86
+ row.height = 1
87
+ for (let col of columns) {
88
+ if (!defaultsApplied) defaults(col, colDefaults)
89
+
90
+ let cell = col.get(row)
91
+
92
+ col.width = Math.max(
93
+ result(col, 'label').length,
94
+ col.width,
95
+ calcWidth(cell)
96
+ )
97
+
98
+ row.height = Math.max(
99
+ row.height,
100
+ cell.split(/[\r\n]+/).length
101
+ )
102
+ }
103
+ defaultsApplied = true
104
+ }
105
+
106
+ if (options.printHeader) {
107
+ options.printHeader(columns.map(function (col) {
108
+ let label = result(col, 'label')
109
+ return pad(label, col.width || label.length)
110
+ }))
111
+ }
112
+
113
+ function getNthLineOfCell (n, row, col) {
114
+ // TODO memoize this
115
+ let lines = col.get(row).split(/[\r\n]+/)
116
+ return pad(lines[n] || '', col.width)
117
+ }
118
+
119
+ for (let row of data) {
120
+ for (let i = 0; i < row.height; i++) {
121
+ let cells = columns.map(partial(getNthLineOfCell, i, row))
122
+ options.printRow(cells)
123
+ }
124
+ options.after(row, options)
125
+ }
126
+ }
127
+
128
+ defaults(options, _defaults)
129
+ render()
130
+ }
131
+
132
+ module.exports = table
package/lib/util.js ADDED
@@ -0,0 +1,38 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * promiseOrCallback will convert a function that returns a promise
5
+ * into one that will either make a node-style callback or return a promise
6
+ * based on whether or not a callback is passed in.
7
+ *
8
+ * @example
9
+ * prompt('input? ').then(function (input) {
10
+ * // deal with input
11
+ * })
12
+ * var prompt2 = promiseOrCallback(prompt)
13
+ * prompt('input? ', function (err, input) {
14
+ * // deal with input
15
+ * })
16
+ *
17
+ * @param {Function} fn a promise returning function to wrap
18
+ * @returns {Function} a function that behaves like before unless called with a callback
19
+ */
20
+ function promiseOrCallback (fn) {
21
+ return function () {
22
+ if (typeof arguments[arguments.length - 1] === 'function') {
23
+ let args = Array.prototype.slice.call(arguments)
24
+ let callback = args.pop()
25
+ fn.apply(null, args).then(function () {
26
+ let args = Array.prototype.slice.call(arguments)
27
+ args.unshift(null)
28
+ callback.apply(null, args)
29
+ }).catch(function (err) {
30
+ callback(err)
31
+ })
32
+ } else {
33
+ return fn.apply(null, arguments)
34
+ }
35
+ }
36
+ }
37
+
38
+ exports.promiseOrCallback = promiseOrCallback
package/lib/vars.js ADDED
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const url = require('url')
4
+
5
+ class Vars {
6
+ constructor (env) {
7
+ this.env = env
8
+ }
9
+
10
+ get host () { return this.env.HEROKU_HOST || 'heroku.com' }
11
+ get apiUrl () { return this.host.startsWith('http') ? this.host : `https://api.${this.host}` }
12
+ get apiHost () {
13
+ if (this.host.startsWith('http')) {
14
+ const u = url.parse(this.host)
15
+ if (u.host) return u.host
16
+ }
17
+ return `api.${this.host}`
18
+ }
19
+ get httpGitHost () {
20
+ if (this.env.HEROKU_GIT_HOST) return this.env.HEROKU_GIT_HOST
21
+ if (this.host.startsWith('http')) {
22
+ const u = url.parse(this.host)
23
+ if (u.host) return u.host
24
+ }
25
+ return `git.${this.host}`
26
+ }
27
+ }
28
+
29
+ module.exports = new Vars(process.env)
package/lib/yubikey.js ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ function toggle (onoff) {
4
+ const cp = require('child_process')
5
+ if (exports.platform !== 'darwin') return
6
+ try {
7
+ cp.execSync(`osascript -e 'if application "yubiswitch" is running then tell application "yubiswitch" to ${onoff}'`, { stdio: 'inherit' })
8
+ } catch (err) {}
9
+ }
10
+
11
+ exports.enable = () => toggle('KeyOn')
12
+ exports.disable = () => toggle('KeyOff')
13
+
14
+ exports.platform = process.platform
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@heroku/heroku-cli-util",
3
+ "description": "Set of helpful CLI utilities",
4
+ "version": "8.0.12",
5
+ "author": "Heroku",
6
+ "bugs": {
7
+ "url": "https://github.com/heroku/heroku-cli-util/issues"
8
+ },
9
+ "dependencies": {
10
+ "@heroku-cli/color": "^1.1.3",
11
+ "ansi-escapes": "^3.1.0",
12
+ "ansi-styles": "^3.2.1",
13
+ "cardinal": "^2.0.1",
14
+ "chalk": "^2.4.1",
15
+ "got": "^11.8.6",
16
+ "heroku-client": "^3.1.0",
17
+ "lodash": "^4.17.10",
18
+ "netrc-parser": "^3.1.4",
19
+ "opn": "^3.0.3",
20
+ "strip-ansi": "^4.0.0",
21
+ "supports-color": "^5.4.0",
22
+ "tslib": "^1.9.0",
23
+ "tunnel-agent": "^0.6.0"
24
+ },
25
+ "devDependencies": {
26
+ "chai": "^4.1.2",
27
+ "co": "^4.6.0",
28
+ "hook-std": "^0.4.0",
29
+ "mocha": "^10.2.0",
30
+ "mocha-junit-reporter": "^1.17.0",
31
+ "nock": "^11.9.1",
32
+ "nyc": "^15.1.0",
33
+ "proxyquire": "^1.7.11",
34
+ "sinon": "^1.17.7",
35
+ "standard": "^12.0.1",
36
+ "tmp": "^0.0.33",
37
+ "unexpected": "^11.15.1"
38
+ },
39
+ "engines": {
40
+ "node": ">= 14"
41
+ },
42
+ "files": [
43
+ "lib",
44
+ "index.js"
45
+ ],
46
+ "homepage": "https://github.com/heroku/heroku-cli-util",
47
+ "license": "ISC",
48
+ "main": "index.js",
49
+ "mocha": {
50
+ "require": [
51
+ "./test/init"
52
+ ]
53
+ },
54
+ "nyc": {
55
+ "exclude": [
56
+ "lib/linewrap.js"
57
+ ]
58
+ },
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/heroku/heroku-cli-util.git"
62
+ },
63
+ "scripts": {
64
+ "test": "mocha",
65
+ "posttest": "standard"
66
+ },
67
+ "standard": {
68
+ "env": "mocha",
69
+ "ignore": [
70
+ "lib/linewrap.js"
71
+ ]
72
+ },
73
+ "directories": {
74
+ "lib": "lib",
75
+ "test": "test"
76
+ },
77
+ "keywords": [
78
+ "heroku-cli-util"
79
+ ]
80
+ }