poops 1.0.20 → 1.2.0
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/README.md +674 -138
- package/lib/copy.js +14 -18
- package/lib/markup/collections.js +247 -0
- package/lib/markup/engines/liquid.js +240 -0
- package/lib/markup/engines/nunjucks.js +261 -0
- package/lib/markup/helpers.js +170 -0
- package/lib/markup/highlight.js +77 -0
- package/lib/markup/indexer.js +154 -0
- package/lib/markup/stop-words-en.json +25 -0
- package/lib/markups.js +233 -456
- package/lib/postcss.js +127 -0
- package/lib/reactor.js +169 -0
- package/lib/scripts.js +20 -23
- package/lib/styles.js +28 -117
- package/lib/utils/helpers.js +41 -181
- package/lib/utils/log.js +64 -0
- package/package.json +38 -12
- package/poops.js +153 -158
- package/lib/utils/print-style.js +0 -72
package/poops.js
CHANGED
|
@@ -1,193 +1,168 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
import chokidar from 'chokidar'
|
|
4
|
+
import connect from 'connect'
|
|
5
|
+
import Copy from './lib/copy.js'
|
|
6
|
+
import { pathExists, doesFileBelongToPath } from './lib/utils/helpers.js'
|
|
7
|
+
import http from 'node:http'
|
|
8
|
+
import os from 'node:os'
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
import livereload from 'livereload'
|
|
11
|
+
import Markups from './lib/markups.js'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import serveStatic from 'serve-static'
|
|
14
|
+
import Reactor from './lib/reactor.js'
|
|
15
|
+
import Scripts from './lib/scripts.js'
|
|
16
|
+
import log, { styledLog } from './lib/utils/log.js'
|
|
17
|
+
import Styles from './lib/styles.js'
|
|
18
|
+
import PostCSS from './lib/postcss.js'
|
|
19
|
+
import Argoyle from 'argoyle'
|
|
20
|
+
import portscanner from 'portscanner'
|
|
18
21
|
|
|
19
22
|
const cwd = process.cwd() // Current Working Directory
|
|
20
|
-
const pkg =
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
break
|
|
36
|
-
case '-c':
|
|
37
|
-
case '--config':
|
|
38
|
-
if (args.length === i + 1 || args[i + 1].startsWith('-')) {
|
|
39
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Missing config file path`)
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
defaultConfigPath = args[i + 1]
|
|
43
|
-
i++
|
|
44
|
-
break
|
|
45
|
-
case '-p':
|
|
46
|
-
case '--port':
|
|
47
|
-
if (args.length === i + 1 || args[i + 1].startsWith('-') || isNaN(args[i + 1])) {
|
|
48
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Missing port number`)
|
|
49
|
-
process.exit(1)
|
|
50
|
-
}
|
|
51
|
-
overridePort = args[i + 1]
|
|
52
|
-
i++
|
|
53
|
-
break
|
|
54
|
-
case '-l':
|
|
55
|
-
case '--livereload':
|
|
56
|
-
if (args.length === i + 1 || args[i + 1].startsWith('-') || isNaN(args[i + 1])) {
|
|
57
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Missing livereload port number`)
|
|
58
|
-
process.exit(1)
|
|
59
|
-
}
|
|
60
|
-
overrideLivereloadPort = args[i + 1]
|
|
61
|
-
i++
|
|
62
|
-
break
|
|
63
|
-
case '-v':
|
|
64
|
-
case '--version':
|
|
65
|
-
console.log(pkg.version)
|
|
66
|
-
process.exit(0)
|
|
67
|
-
break
|
|
68
|
-
case '-h':
|
|
69
|
-
case '--help':
|
|
70
|
-
console.log(`Usage: ${pkg.name} [config-file] [options]
|
|
71
|
-
-b, --build\t\tBuild the project and exit
|
|
72
|
-
-c, --config\t\tSpecify the config file
|
|
73
|
-
-h, --help\t\tShow this help message
|
|
74
|
-
-l, --livereload\t\tSpecify the port to use for the livereload server, overrides the config file
|
|
75
|
-
-p, --port\t\tSpecify the port to use for the server, overrides the config file
|
|
76
|
-
-v, --version\t\tShow version number`)
|
|
77
|
-
process.exit(0)
|
|
78
|
-
break
|
|
79
|
-
default:
|
|
80
|
-
if (arg.startsWith('-')) {
|
|
81
|
-
console.log(`Unknown option: ${arg}`)
|
|
82
|
-
process.exit(1)
|
|
83
|
-
} else {
|
|
84
|
-
defaultConfigPath = arg
|
|
85
|
-
}
|
|
86
|
-
}
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf-8'))
|
|
24
|
+
|
|
25
|
+
const cli = new Argoyle(pkg.version)
|
|
26
|
+
.line(`Usage: ${pkg.name} [config-file] [options]\n`)
|
|
27
|
+
.option('build', { short: 'b', description: 'Build the project and exit' })
|
|
28
|
+
.option('config', { short: 'c', value: '<path>', description: 'Specify the config file' })
|
|
29
|
+
.option('port', { short: 'p', value: '<number>', description: 'Specify the port for the server, overrides the config file' })
|
|
30
|
+
.option('livereload-port', { short: 'l', value: '<number>', description: 'Specify the port for the livereload server, overrides the config file' })
|
|
31
|
+
|
|
32
|
+
let flags, positionals
|
|
33
|
+
try {
|
|
34
|
+
({ flags, positionals } = cli.parse())
|
|
35
|
+
} catch (err) {
|
|
36
|
+
log({ tag: 'error', text: err.message })
|
|
37
|
+
process.exit(1)
|
|
87
38
|
}
|
|
88
39
|
|
|
40
|
+
const build = flags.build
|
|
41
|
+
const defaultConfigPath = flags.config || positionals[0] || 'poops.json'
|
|
42
|
+
const overridePort = flags.port
|
|
43
|
+
const overrideLivereloadPort = flags['livereload-port']
|
|
44
|
+
|
|
89
45
|
let configPath = path.join(cwd, defaultConfigPath)
|
|
90
46
|
if (!pathExists(configPath)) configPath = path.join(cwd, '💩.json') // TODO: Ok dude, I know it's late, but you can do better than this.
|
|
91
47
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
48
|
+
async function resolveLiveReloadPort(config) {
|
|
49
|
+
if (!config.livereload) return null
|
|
50
|
+
let liveReloadPort = overrideLivereloadPort || config.livereload.port || 35729
|
|
51
|
+
if (!overrideLivereloadPort) liveReloadPort = await getAvailablePort(liveReloadPort, liveReloadPort + 10)
|
|
52
|
+
config.livereload_port = liveReloadPort
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setupLiveReloadServer(config) {
|
|
56
|
+
if (!config.livereload) return null
|
|
57
|
+
const liveReloadExcludes = ['.git', '.svn', '.hg']
|
|
58
|
+
|
|
59
|
+
if (config.watch) {
|
|
60
|
+
liveReloadExcludes.push(...config.watch)
|
|
99
61
|
}
|
|
100
62
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const copy = new Copy(config)
|
|
63
|
+
if (config.includePaths) {
|
|
64
|
+
liveReloadExcludes.push(...config.includePaths)
|
|
65
|
+
}
|
|
105
66
|
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
await scripts.compile()
|
|
109
|
-
await markups.compile()
|
|
110
|
-
await copy.execute()
|
|
111
|
-
process.exit(0)
|
|
67
|
+
if (config.livereload.exclude) {
|
|
68
|
+
liveReloadExcludes.push(...config.livereload.exclude)
|
|
112
69
|
}
|
|
113
70
|
|
|
114
|
-
|
|
115
|
-
|
|
71
|
+
const liveReloadServer = livereload.createServer({
|
|
72
|
+
exclusions: [...new Set(liveReloadExcludes)],
|
|
73
|
+
port: config.livereload_port
|
|
74
|
+
})
|
|
75
|
+
styledLog(`🔃 {dim}LiveReload :{/} ${liveReloadServer.config.port}`)
|
|
76
|
+
console.log()
|
|
77
|
+
liveReloadServer.watch(cwd)
|
|
78
|
+
}
|
|
116
79
|
|
|
117
|
-
|
|
118
|
-
|
|
80
|
+
function setupWatchers(config, modules) {
|
|
81
|
+
if (!config.watch) return
|
|
82
|
+
|
|
83
|
+
// TODO: think about watching the updates of the config file itself, we can reload the config and recompile everything.
|
|
84
|
+
// TODO: ability to automatically create a watch list of directories if watch is set to true. The list will be generated from the `in` property of each task.
|
|
85
|
+
chokidar.watch(config.watch, { ignoreInitial: true }).on('change', (file) => {
|
|
86
|
+
if (/(\.m?jsx?|\.tsx?)$/i.test(file)) {
|
|
87
|
+
modules.scripts.compile().catch(err => console.error(err))
|
|
88
|
+
|
|
89
|
+
if (modules.reactor.belongsToReactor(file)) {
|
|
90
|
+
modules.reactor.compile().then(() => {
|
|
91
|
+
if (modules.reactor.renderedChanged) {
|
|
92
|
+
config.reactorData = modules.reactor.getRendered()
|
|
93
|
+
modules.markups.compile().then(() => modules.postcss.compile()).catch(err => console.error(err))
|
|
94
|
+
}
|
|
95
|
+
}).catch(err => console.error(err))
|
|
96
|
+
}
|
|
119
97
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
if (/(\.sass|\.scss|\.css)$/i.test(file)) {
|
|
99
|
+
modules.styles.compile().then(() => modules.postcss.compile()).catch(err => console.error(err))
|
|
100
|
+
}
|
|
101
|
+
if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.liquid|\.md)$/i.test(file)) {
|
|
102
|
+
modules.markups.compile().then(() => modules.postcss.compile()).catch(err => console.error(err))
|
|
123
103
|
}
|
|
124
104
|
|
|
125
|
-
if
|
|
126
|
-
|
|
105
|
+
// TODO: We can actually reload the page only if the data file from data has changed.
|
|
106
|
+
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
107
|
+
modules.markups.reloadDataFiles().then(() => modules.markups.compile()).catch(err => console.error(err))
|
|
127
108
|
}
|
|
128
109
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
110
|
+
doesFileBelongToPath(file, config.copy) && modules.copy.execute().catch(err => console.error(err))
|
|
111
|
+
}).on('unlink', (file) => {
|
|
112
|
+
if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.liquid|\.md)$/i.test(file)) {
|
|
113
|
+
modules.markups.compile().catch(err => console.error(err))
|
|
114
|
+
}
|
|
115
|
+
modules.copy.unlink(file, doesFileBelongToPath(file, config.copy))
|
|
116
|
+
}).on('unlinkDir', (dirPath) => {
|
|
117
|
+
doesFileBelongToPath(dirPath, config.markup) && modules.markups.compile().catch(err => console.error(err))
|
|
118
|
+
modules.copy.unlink(dirPath, doesFileBelongToPath(dirPath, config.copy))
|
|
119
|
+
}).on('add', (file) => {
|
|
120
|
+
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
121
|
+
modules.markups.reloadDataFiles().then(() => modules.markups.compile()).catch(err => console.error(err))
|
|
122
|
+
}
|
|
123
|
+
doesFileBelongToPath(file, config.copy) && modules.copy.execute().catch(err => console.error(err))
|
|
124
|
+
})
|
|
125
|
+
}
|
|
136
126
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
127
|
+
// Main function 💩
|
|
128
|
+
async function poops() {
|
|
129
|
+
const styles = new Styles(config)
|
|
130
|
+
const postcss = new PostCSS(config)
|
|
131
|
+
const reactor = new Reactor(config)
|
|
132
|
+
const scripts = new Scripts(config)
|
|
133
|
+
const markups = new Markups(config)
|
|
134
|
+
const copy = new Copy(config)
|
|
141
135
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// TODO: We can actually reload the page only if the data file from data has changed.
|
|
151
|
-
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
152
|
-
markups.reloadDataFiles().then(() => {
|
|
153
|
-
markups.compile()
|
|
154
|
-
})
|
|
155
|
-
}
|
|
136
|
+
try { await styles.compile() } catch (err) { console.error(err) }
|
|
137
|
+
try { await reactor.compile() } catch (err) { console.error(err) }
|
|
138
|
+
config.reactorData = reactor.getRendered()
|
|
139
|
+
try { await scripts.compile() } catch (err) { console.error(err) }
|
|
140
|
+
try { await markups.compile() } catch (err) { console.error(err) }
|
|
141
|
+
try { await postcss.compile() } catch (err) { console.error(err) }
|
|
142
|
+
try { await copy.execute() } catch (err) { console.error(err) }
|
|
156
143
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.md)$/i.test(file)) markups.compile()
|
|
160
|
-
copy.unlink(file, doesFileBelongToPath(file, config.copy))
|
|
161
|
-
}).on('unlinkDir', (path) => {
|
|
162
|
-
doesFileBelongToPath(path, config.markup) && markups.compile()
|
|
163
|
-
copy.unlink(path, doesFileBelongToPath(path, config.copy))
|
|
164
|
-
}).on('add', (file) => {
|
|
165
|
-
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
166
|
-
markups.reloadDataFiles().then(() => {
|
|
167
|
-
markups.compile()
|
|
168
|
-
})
|
|
169
|
-
}
|
|
170
|
-
doesFileBelongToPath(file, config.copy) && copy.execute()
|
|
171
|
-
})
|
|
144
|
+
if (build || (!config.watch && !config.livereload && !config.serve)) {
|
|
145
|
+
process.exit(0)
|
|
172
146
|
}
|
|
147
|
+
|
|
148
|
+
setupWatchers(config, { styles, postcss, reactor, scripts, markups, copy })
|
|
173
149
|
}
|
|
174
150
|
|
|
175
151
|
// CLI Header
|
|
176
152
|
const title = `💩 Poops — v${pkg.version}`
|
|
177
|
-
|
|
178
|
-
${title.replace(/./g, '-')}${pstyle.reset + pstyle.bell}\n`)
|
|
153
|
+
styledLog(`\n{#8b4513}${title}\n${title.replace(/./g, '-')}{/}{bell}\n`)
|
|
179
154
|
|
|
180
155
|
// Check if poops.json exists
|
|
181
156
|
if (!pathExists(configPath)) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
157
|
+
styledLog(`{bold.redBright|[error]} \`{underline|${defaultConfigPath}}\` or \`{underline|💩.json}\` not found.
|
|
158
|
+
{dim}Configuration file \`${defaultConfigPath}\` or \`💩.json\` not found in your working directory: {underline}${cwd}{/}{dim}\n
|
|
159
|
+
{/}{dim}Please specify another file path or create a \`poops.json\` or \`💩.json\` file in your working directory and try again.\n
|
|
160
|
+
{/}{dim}For information on the structure of the configuration file, please visit: \n{underline}https://stamat.github.io/poops{/}\n`)
|
|
186
161
|
process.exit(1)
|
|
187
162
|
}
|
|
188
163
|
|
|
189
164
|
// Load poops.json
|
|
190
|
-
const config =
|
|
165
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
191
166
|
|
|
192
167
|
if (config.watch) {
|
|
193
168
|
config.watch = Array.isArray(config.watch) ? config.watch : [config.watch]
|
|
@@ -199,6 +174,11 @@ if (config.includePaths) {
|
|
|
199
174
|
config.includePaths = ['node_modules']
|
|
200
175
|
}
|
|
201
176
|
|
|
177
|
+
// Backwards compatibility: support "ssg" as alias for "reactor"
|
|
178
|
+
if (!config.reactor && config.ssg) {
|
|
179
|
+
config.reactor = config.ssg
|
|
180
|
+
}
|
|
181
|
+
|
|
202
182
|
async function getAvailablePort(port, max) {
|
|
203
183
|
while (port < max) {
|
|
204
184
|
const status = await portscanner.checkPortStatus(port, 'localhost')
|
|
@@ -211,7 +191,19 @@ async function getAvailablePort(port, max) {
|
|
|
211
191
|
return port
|
|
212
192
|
}
|
|
213
193
|
|
|
194
|
+
function getLocalIP() {
|
|
195
|
+
const interfaces = os.networkInterfaces()
|
|
196
|
+
for (const iface of Object.values(interfaces)) {
|
|
197
|
+
for (const info of iface) {
|
|
198
|
+
if (info.family === 'IPv4' && !info.internal) return info.address
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return 'localhost'
|
|
202
|
+
}
|
|
203
|
+
|
|
214
204
|
async function startServer() {
|
|
205
|
+
await resolveLiveReloadPort(config)
|
|
206
|
+
await poops() // Initial compilation before starting the server
|
|
215
207
|
const app = connect()
|
|
216
208
|
|
|
217
209
|
if (config.serve.base && pathExists(cwd, config.serve.base)) {
|
|
@@ -223,9 +215,12 @@ async function startServer() {
|
|
|
223
215
|
let port = overridePort || config.serve.port || 4040
|
|
224
216
|
if (!overridePort) port = await getAvailablePort(port, port + 10)
|
|
225
217
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
// eslint-disable-next-line @stylistic/space-before-function-paren
|
|
219
|
+
http.createServer(app).listen(parseInt(port), '0.0.0.0', async () => {
|
|
220
|
+
console.log()
|
|
221
|
+
styledLog(`🏠 {dim}Local server:{/} {underline|http://localhost:${port}}`)
|
|
222
|
+
styledLog(`🛜 {dim} Network :{/} {underline|http://${getLocalIP()}:${port}}`)
|
|
223
|
+
setupLiveReloadServer(config)
|
|
229
224
|
})
|
|
230
225
|
}
|
|
231
226
|
|
package/lib/utils/print-style.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
module.exports = class PrintStyle {
|
|
2
|
-
reset = '\x1b[0m'
|
|
3
|
-
bold = '\x1b[1m'
|
|
4
|
-
dim = '\x1b[2m'
|
|
5
|
-
italic = '\x1b[3m'
|
|
6
|
-
underline = '\x1b[4m'
|
|
7
|
-
blink = '\x1b[5m'
|
|
8
|
-
inverse = '\x1b[7m'
|
|
9
|
-
hidden = '\x1b[8m'
|
|
10
|
-
strikethrough = '\x1b[9m'
|
|
11
|
-
black = '\x1b[30m'
|
|
12
|
-
red = '\x1b[31m'
|
|
13
|
-
redBright = '\x1b[91m'
|
|
14
|
-
green = '\x1b[32m'
|
|
15
|
-
greenBright = '\x1b[92m'
|
|
16
|
-
yellow = '\x1b[33m'
|
|
17
|
-
yellowBright = '\x1b[93m'
|
|
18
|
-
blue = '\x1b[34m'
|
|
19
|
-
blueBright = '\x1b[94m'
|
|
20
|
-
magenta = '\x1b[35m'
|
|
21
|
-
magentaBright = '\x1b[95m'
|
|
22
|
-
cyan = '\x1b[36m'
|
|
23
|
-
cyanBright = '\x1b[96m'
|
|
24
|
-
white = '\x1b[37m'
|
|
25
|
-
whiteBright = '\x1b[97m'
|
|
26
|
-
gray = '\x1b[90m'
|
|
27
|
-
bgBlack = '\x1b[40m'
|
|
28
|
-
bgRed = '\x1b[41m'
|
|
29
|
-
bgRedBright = '\x1b[101m'
|
|
30
|
-
bgGreen = '\x1b[42m'
|
|
31
|
-
bgGreenBright = '\x1b[102m'
|
|
32
|
-
bgYellow = '\x1b[43m'
|
|
33
|
-
bgYellowBright = '\x1b[103m'
|
|
34
|
-
bgBlue = '\x1b[44m'
|
|
35
|
-
bgBlueBright = '\x1b[104m'
|
|
36
|
-
bgMagenta = '\x1b[45m'
|
|
37
|
-
bgMagentaBright = '\x1b[105m'
|
|
38
|
-
bgCyan = '\x1b[46m'
|
|
39
|
-
bgCyanBright = '\x1b[106m'
|
|
40
|
-
bgWhite = '\x1b[47m'
|
|
41
|
-
bgWhiteBright = '\x1b[107m'
|
|
42
|
-
bgGray = '\x1b[100m'
|
|
43
|
-
bell = '\x07'
|
|
44
|
-
|
|
45
|
-
hexToRgb(hex) {
|
|
46
|
-
const sanitizedHex = hex.replace('#', '')
|
|
47
|
-
const red = parseInt(sanitizedHex.substring(0, 2), 16)
|
|
48
|
-
const green = parseInt(sanitizedHex.substring(2, 4), 16)
|
|
49
|
-
const blue = parseInt(sanitizedHex.substring(4, 6), 16)
|
|
50
|
-
|
|
51
|
-
return [red, green, blue]
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
terminalColorIndex(red, green, blue) {
|
|
55
|
-
return 16 +
|
|
56
|
-
Math.round(red / 255 * 5) * 36 +
|
|
57
|
-
Math.round(green / 255 * 5) * 6 +
|
|
58
|
-
Math.round(blue / 255 * 5)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
color(hex) {
|
|
62
|
-
const [red, green, blue] = this.hexToRgb(hex)
|
|
63
|
-
|
|
64
|
-
return `\x1b[38;5;${this.terminalColorIndex(red, green, blue)}m`
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
background(hex) {
|
|
68
|
-
const [red, green, blue] = this.hexToRgb(hex)
|
|
69
|
-
|
|
70
|
-
return `\x1b[48;5;${this.terminalColorIndex(red, green, blue)}m`
|
|
71
|
-
}
|
|
72
|
-
}
|