poops 1.1.0 → 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 +515 -55
- package/lib/copy.js +7 -9
- 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 +223 -459
- package/lib/postcss.js +127 -0
- package/lib/{ssg.js → reactor.js} +36 -25
- package/lib/scripts.js +12 -13
- package/lib/styles.js +20 -12
- package/lib/utils/helpers.js +0 -56
- package/lib/utils/log.js +64 -0
- package/package.json +21 -3
- package/poops.js +87 -107
- package/lib/utils/print-style.js +0 -72
package/poops.js
CHANGED
|
@@ -5,87 +5,43 @@ import connect from 'connect'
|
|
|
5
5
|
import Copy from './lib/copy.js'
|
|
6
6
|
import { pathExists, doesFileBelongToPath } from './lib/utils/helpers.js'
|
|
7
7
|
import http from 'node:http'
|
|
8
|
+
import os from 'node:os'
|
|
8
9
|
import fs from 'node:fs'
|
|
9
10
|
import livereload from 'livereload'
|
|
10
11
|
import Markups from './lib/markups.js'
|
|
11
12
|
import path from 'node:path'
|
|
12
13
|
import serveStatic from 'serve-static'
|
|
14
|
+
import Reactor from './lib/reactor.js'
|
|
13
15
|
import Scripts from './lib/scripts.js'
|
|
14
|
-
import
|
|
15
|
-
import PrintStyle from './lib/utils/print-style.js'
|
|
16
|
+
import log, { styledLog } from './lib/utils/log.js'
|
|
16
17
|
import Styles from './lib/styles.js'
|
|
18
|
+
import PostCSS from './lib/postcss.js'
|
|
19
|
+
import Argoyle from 'argoyle'
|
|
17
20
|
import portscanner from 'portscanner'
|
|
18
21
|
|
|
19
22
|
const cwd = process.cwd() // Current Working Directory
|
|
20
23
|
const pkg = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf-8'))
|
|
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
|
-
}
|
|
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
|
|
|
@@ -116,7 +72,8 @@ function setupLiveReloadServer(config) {
|
|
|
116
72
|
exclusions: [...new Set(liveReloadExcludes)],
|
|
117
73
|
port: config.livereload_port
|
|
118
74
|
})
|
|
119
|
-
|
|
75
|
+
styledLog(`🔃 {dim}LiveReload :{/} ${liveReloadServer.config.port}`)
|
|
76
|
+
console.log()
|
|
120
77
|
liveReloadServer.watch(cwd)
|
|
121
78
|
}
|
|
122
79
|
|
|
@@ -127,74 +84,80 @@ function setupWatchers(config, modules) {
|
|
|
127
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.
|
|
128
85
|
chokidar.watch(config.watch, { ignoreInitial: true }).on('change', (file) => {
|
|
129
86
|
if (/(\.m?jsx?|\.tsx?)$/i.test(file)) {
|
|
130
|
-
modules.scripts.compile()
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
modules.
|
|
135
|
-
|
|
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))
|
|
136
96
|
}
|
|
137
97
|
}
|
|
138
|
-
if (/(\.sass|\.scss|\.css)$/i.test(file))
|
|
139
|
-
|
|
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))
|
|
103
|
+
}
|
|
140
104
|
|
|
141
105
|
// TODO: We can actually reload the page only if the data file from data has changed.
|
|
142
106
|
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
143
|
-
modules.markups.reloadDataFiles().then(() =>
|
|
144
|
-
modules.markups.compile()
|
|
145
|
-
})
|
|
107
|
+
modules.markups.reloadDataFiles().then(() => modules.markups.compile()).catch(err => console.error(err))
|
|
146
108
|
}
|
|
147
109
|
|
|
148
|
-
doesFileBelongToPath(file, config.copy) && modules.copy.execute()
|
|
110
|
+
doesFileBelongToPath(file, config.copy) && modules.copy.execute().catch(err => console.error(err))
|
|
149
111
|
}).on('unlink', (file) => {
|
|
150
|
-
if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.md)$/i.test(file))
|
|
112
|
+
if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.liquid|\.md)$/i.test(file)) {
|
|
113
|
+
modules.markups.compile().catch(err => console.error(err))
|
|
114
|
+
}
|
|
151
115
|
modules.copy.unlink(file, doesFileBelongToPath(file, config.copy))
|
|
152
|
-
}).on('unlinkDir', (
|
|
153
|
-
doesFileBelongToPath(
|
|
154
|
-
modules.copy.unlink(
|
|
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))
|
|
155
119
|
}).on('add', (file) => {
|
|
156
120
|
if (/(\.json|\.ya?ml)$/i.test(file)) {
|
|
157
|
-
modules.markups.reloadDataFiles().then(() =>
|
|
158
|
-
modules.markups.compile()
|
|
159
|
-
})
|
|
121
|
+
modules.markups.reloadDataFiles().then(() => modules.markups.compile()).catch(err => console.error(err))
|
|
160
122
|
}
|
|
161
|
-
doesFileBelongToPath(file, config.copy) && modules.copy.execute()
|
|
123
|
+
doesFileBelongToPath(file, config.copy) && modules.copy.execute().catch(err => console.error(err))
|
|
162
124
|
})
|
|
163
125
|
}
|
|
164
126
|
|
|
165
127
|
// Main function 💩
|
|
166
128
|
async function poops() {
|
|
167
129
|
const styles = new Styles(config)
|
|
168
|
-
const
|
|
130
|
+
const postcss = new PostCSS(config)
|
|
131
|
+
const reactor = new Reactor(config)
|
|
169
132
|
const scripts = new Scripts(config)
|
|
170
133
|
const markups = new Markups(config)
|
|
171
134
|
const copy = new Copy(config)
|
|
172
135
|
|
|
173
|
-
try { await styles.compile() } catch (err) { console.
|
|
174
|
-
try { await
|
|
175
|
-
config.
|
|
176
|
-
try { await scripts.compile() } catch (err) { console.
|
|
177
|
-
try { await markups.compile() } catch (err) { console.
|
|
178
|
-
try { await
|
|
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) }
|
|
179
143
|
|
|
180
144
|
if (build || (!config.watch && !config.livereload && !config.serve)) {
|
|
181
145
|
process.exit(0)
|
|
182
146
|
}
|
|
183
147
|
|
|
184
|
-
setupWatchers(config, { styles,
|
|
148
|
+
setupWatchers(config, { styles, postcss, reactor, scripts, markups, copy })
|
|
185
149
|
}
|
|
186
150
|
|
|
187
151
|
// CLI Header
|
|
188
152
|
const title = `💩 Poops — v${pkg.version}`
|
|
189
|
-
|
|
190
|
-
${title.replace(/./g, '-')}${pstyle.reset + pstyle.bell}\n`)
|
|
153
|
+
styledLog(`\n{#8b4513}${title}\n${title.replace(/./g, '-')}{/}{bell}\n`)
|
|
191
154
|
|
|
192
155
|
// Check if poops.json exists
|
|
193
156
|
if (!pathExists(configPath)) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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`)
|
|
198
161
|
process.exit(1)
|
|
199
162
|
}
|
|
200
163
|
|
|
@@ -211,6 +174,11 @@ if (config.includePaths) {
|
|
|
211
174
|
config.includePaths = ['node_modules']
|
|
212
175
|
}
|
|
213
176
|
|
|
177
|
+
// Backwards compatibility: support "ssg" as alias for "reactor"
|
|
178
|
+
if (!config.reactor && config.ssg) {
|
|
179
|
+
config.reactor = config.ssg
|
|
180
|
+
}
|
|
181
|
+
|
|
214
182
|
async function getAvailablePort(port, max) {
|
|
215
183
|
while (port < max) {
|
|
216
184
|
const status = await portscanner.checkPortStatus(port, 'localhost')
|
|
@@ -223,7 +191,19 @@ async function getAvailablePort(port, max) {
|
|
|
223
191
|
return port
|
|
224
192
|
}
|
|
225
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
|
+
|
|
226
204
|
async function startServer() {
|
|
205
|
+
await resolveLiveReloadPort(config)
|
|
206
|
+
await poops() // Initial compilation before starting the server
|
|
227
207
|
const app = connect()
|
|
228
208
|
|
|
229
209
|
if (config.serve.base && pathExists(cwd, config.serve.base)) {
|
|
@@ -236,10 +216,10 @@ async function startServer() {
|
|
|
236
216
|
if (!overridePort) port = await getAvailablePort(port, port + 10)
|
|
237
217
|
|
|
238
218
|
// eslint-disable-next-line @stylistic/space-before-function-paren
|
|
239
|
-
http.createServer(app).listen(parseInt(port), async () => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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}}`)
|
|
243
223
|
setupLiveReloadServer(config)
|
|
244
224
|
})
|
|
245
225
|
}
|
package/lib/utils/print-style.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
export default 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
|
-
}
|