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/lib/postcss.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { transform } from 'esbuild'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import {
|
|
4
|
+
pathExists,
|
|
5
|
+
mkPath,
|
|
6
|
+
insertMinSuffix,
|
|
7
|
+
buildStyleOutputFilePath,
|
|
8
|
+
fillBannerTemplate,
|
|
9
|
+
buildTime,
|
|
10
|
+
fileSize
|
|
11
|
+
} from './utils/helpers.js'
|
|
12
|
+
import log from './utils/log.js'
|
|
13
|
+
|
|
14
|
+
let postcss
|
|
15
|
+
|
|
16
|
+
async function loadPostCSS() {
|
|
17
|
+
if (postcss) return true
|
|
18
|
+
try {
|
|
19
|
+
postcss = (await import('postcss')).default
|
|
20
|
+
return true
|
|
21
|
+
} catch (err) {
|
|
22
|
+
log({ tag: 'postcss', error: true, text: 'Missing dependency. Install: npm i -D postcss' })
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function resolvePlugins(plugins) {
|
|
28
|
+
if (!plugins || !plugins.length) return []
|
|
29
|
+
|
|
30
|
+
const resolved = []
|
|
31
|
+
for (const plugin of plugins) {
|
|
32
|
+
if (typeof plugin === 'string') {
|
|
33
|
+
try {
|
|
34
|
+
const mod = await import(plugin)
|
|
35
|
+
resolved.push((mod.default || mod)())
|
|
36
|
+
} catch (err) {
|
|
37
|
+
log({ tag: 'postcss', error: true, text: `Failed to load plugin: ${plugin}` })
|
|
38
|
+
throw err
|
|
39
|
+
}
|
|
40
|
+
} else if (Array.isArray(plugin)) {
|
|
41
|
+
const [name, options] = plugin
|
|
42
|
+
try {
|
|
43
|
+
const mod = await import(name)
|
|
44
|
+
resolved.push((mod.default || mod)(options))
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log({ tag: 'postcss', error: true, text: `Failed to load plugin: ${name}` })
|
|
47
|
+
throw err
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
resolved.push(plugin)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return resolved
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default class PostCSS {
|
|
57
|
+
constructor(config) {
|
|
58
|
+
this.config = config
|
|
59
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner) : null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async compile() {
|
|
63
|
+
if (!this.config.postcss) return
|
|
64
|
+
if (!(await loadPostCSS())) return
|
|
65
|
+
|
|
66
|
+
this.config.postcss = Array.isArray(this.config.postcss) ? this.config.postcss : [this.config.postcss]
|
|
67
|
+
for (const entry of this.config.postcss) {
|
|
68
|
+
if (entry.in && entry.out && pathExists(entry.in)) {
|
|
69
|
+
mkPath(entry.out)
|
|
70
|
+
await this.compileEntry(entry.in, entry.out, entry.options)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
76
|
+
outfilePath = buildStyleOutputFilePath(infilePath, outfilePath)
|
|
77
|
+
|
|
78
|
+
const input = fs.readFileSync(infilePath, 'utf-8')
|
|
79
|
+
const start = performance.now()
|
|
80
|
+
|
|
81
|
+
let result
|
|
82
|
+
try {
|
|
83
|
+
const plugins = await resolvePlugins(options.plugins)
|
|
84
|
+
const processor = postcss(plugins)
|
|
85
|
+
result = await processor.process(input, {
|
|
86
|
+
from: infilePath,
|
|
87
|
+
to: outfilePath
|
|
88
|
+
})
|
|
89
|
+
} catch (err) {
|
|
90
|
+
log({ tag: 'postcss', error: true, text: 'Failed compiling:', link: outfilePath })
|
|
91
|
+
console.error(err)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let css = result.css
|
|
96
|
+
if (this.banner) css = this.banner + '\n' + css
|
|
97
|
+
fs.writeFileSync(outfilePath, css)
|
|
98
|
+
const end = performance.now()
|
|
99
|
+
if (!options.justMinified) log({ tag: 'postcss', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(start, end) })
|
|
100
|
+
|
|
101
|
+
const minPath = insertMinSuffix(outfilePath)
|
|
102
|
+
if (options.minify) {
|
|
103
|
+
try {
|
|
104
|
+
const minStart = !options.justMinified ? performance.now() : start
|
|
105
|
+
|
|
106
|
+
const minified = await transform(css, {
|
|
107
|
+
loader: 'css',
|
|
108
|
+
minify: true
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (this.banner) minified.code = this.banner + '\n' + minified.code
|
|
112
|
+
fs.writeFileSync(minPath, minified.code)
|
|
113
|
+
const minEnd = performance.now()
|
|
114
|
+
log({ tag: 'postcss', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(minStart, minEnd) })
|
|
115
|
+
} catch (err) {
|
|
116
|
+
log({ tag: 'postcss', error: true, text: 'Failed compiling:', link: minPath })
|
|
117
|
+
console.error(err)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (options.justMinified) {
|
|
121
|
+
fs.unlinkSync(outfilePath)
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -4,42 +4,45 @@ import {
|
|
|
4
4
|
pathExists,
|
|
5
5
|
mkPath,
|
|
6
6
|
insertMinSuffix,
|
|
7
|
+
pathContainsPathSegment,
|
|
7
8
|
fillBannerTemplate,
|
|
8
9
|
buildTime,
|
|
9
10
|
fileSize
|
|
10
11
|
} from './utils/helpers.js'
|
|
11
12
|
import fs from 'node:fs'
|
|
12
13
|
import path from 'node:path'
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
14
|
+
import { createRequire } from 'node:module'
|
|
15
|
+
import log from './utils/log.js'
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export default class SSG {
|
|
17
|
+
export default class Reactor {
|
|
19
18
|
constructor(config) {
|
|
20
19
|
this.config = config
|
|
21
20
|
this.rendered = {}
|
|
22
|
-
this.
|
|
21
|
+
this.renderedChanged = false
|
|
22
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner, config.pkg) : null
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async compile() {
|
|
26
|
-
if (!this.config.
|
|
27
|
-
this.config.
|
|
26
|
+
if (!this.config.reactor) return
|
|
27
|
+
this.config.reactor = Array.isArray(this.config.reactor) ? this.config.reactor : [this.config.reactor]
|
|
28
|
+
const prevRendered = this.rendered
|
|
28
29
|
this.rendered = {}
|
|
29
30
|
|
|
30
|
-
for (const entry of this.config.
|
|
31
|
+
for (const entry of this.config.reactor) {
|
|
31
32
|
if (entry.component && entry.inject && pathExists(entry.component)) {
|
|
32
33
|
await this.compileEntry(entry)
|
|
33
34
|
}
|
|
34
35
|
}
|
|
36
|
+
|
|
37
|
+
this.renderedChanged = JSON.stringify(this.rendered) !== JSON.stringify(prevRendered)
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
async compileEntry(entry) {
|
|
38
41
|
const { component, in: client, out, inject, options = {} } = entry
|
|
39
42
|
|
|
40
43
|
// --- Step 1: Server-side render the component ---
|
|
41
|
-
const tmpWrapper = component.replace(/(\.[^.]+)$/,
|
|
42
|
-
const tmpBundle = component.replace(/(\.[^.]+)$/,
|
|
44
|
+
const tmpWrapper = component.replace(/(\.[^.]+)$/, `.reactor-tmp-${inject}$1`)
|
|
45
|
+
const tmpBundle = component.replace(/(\.[^.]+)$/, `.reactor-bundle-${inject}.cjs`)
|
|
43
46
|
|
|
44
47
|
const wrapperCode = `
|
|
45
48
|
import { renderToString } from 'react-dom/server';
|
|
@@ -51,7 +54,7 @@ export const html = renderToString(React.createElement(Component));
|
|
|
51
54
|
try {
|
|
52
55
|
fs.writeFileSync(tmpWrapper, wrapperCode)
|
|
53
56
|
|
|
54
|
-
const
|
|
57
|
+
const renderStart = performance.now()
|
|
55
58
|
await build({
|
|
56
59
|
logLevel: 'error',
|
|
57
60
|
entryPoints: [tmpWrapper],
|
|
@@ -63,15 +66,17 @@ export const html = renderToString(React.createElement(Component));
|
|
|
63
66
|
nodePaths: this.config.includePaths
|
|
64
67
|
})
|
|
65
68
|
|
|
66
|
-
const
|
|
67
|
-
const
|
|
69
|
+
const bundlePath = path.resolve(tmpBundle)
|
|
70
|
+
const require = createRequire(import.meta.url)
|
|
71
|
+
delete require.cache[bundlePath]
|
|
72
|
+
const mod = require(bundlePath)
|
|
68
73
|
this.rendered[inject] = mod.html
|
|
69
|
-
const
|
|
74
|
+
const renderEnd = performance.now()
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
log({ tag: 'reactor', text: `Rendered: ${component} →`, link: inject, time: buildTime(renderStart, renderEnd) })
|
|
72
77
|
} catch (err) {
|
|
73
|
-
|
|
74
|
-
console.
|
|
78
|
+
log({ tag: 'reactor', error: true, text: 'Failed rendering:', link: component })
|
|
79
|
+
console.error(err)
|
|
75
80
|
} finally {
|
|
76
81
|
if (fs.existsSync(tmpWrapper)) fs.unlinkSync(tmpWrapper)
|
|
77
82
|
if (fs.existsSync(tmpBundle)) fs.unlinkSync(tmpBundle)
|
|
@@ -115,14 +120,14 @@ export const html = renderToString(React.createElement(Component));
|
|
|
115
120
|
try {
|
|
116
121
|
await build(opts)
|
|
117
122
|
} catch (err) {
|
|
118
|
-
|
|
119
|
-
console.
|
|
123
|
+
log({ tag: 'reactor', error: true, text: 'Failed compiling client:', link: out })
|
|
124
|
+
console.error(err)
|
|
120
125
|
return
|
|
121
126
|
}
|
|
122
127
|
const esbuildEnd = performance.now()
|
|
123
128
|
|
|
124
|
-
if (!options.justMinified)
|
|
125
|
-
if (options.sourcemap)
|
|
129
|
+
if (!options.justMinified) log({ tag: 'reactor', text: 'Compiled:', link: out, size: fileSize(out), time: buildTime(esbuildStart, esbuildEnd) })
|
|
130
|
+
if (options.sourcemap) log({ tag: 'reactor', text: 'Compiled:', link: `${out}.map` })
|
|
126
131
|
|
|
127
132
|
if (options.minify) {
|
|
128
133
|
const minPath = insertMinSuffix(out)
|
|
@@ -136,10 +141,10 @@ export const html = renderToString(React.createElement(Component));
|
|
|
136
141
|
|
|
137
142
|
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
138
143
|
fs.writeFileSync(minPath, minifyResult.code)
|
|
139
|
-
|
|
144
|
+
log({ tag: 'reactor', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
140
145
|
} catch (err) {
|
|
141
|
-
|
|
142
|
-
console.
|
|
146
|
+
log({ tag: 'reactor', error: true, text: 'Failed compiling:', link: minPath })
|
|
147
|
+
console.error(err)
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
if (options.justMinified) {
|
|
@@ -155,4 +160,10 @@ export const html = renderToString(React.createElement(Component));
|
|
|
155
160
|
getRendered() {
|
|
156
161
|
return this.rendered
|
|
157
162
|
}
|
|
163
|
+
|
|
164
|
+
belongsToReactor(file) {
|
|
165
|
+
if (!this.config.reactor) return false
|
|
166
|
+
const entries = Array.isArray(this.config.reactor) ? this.config.reactor : [this.config.reactor]
|
|
167
|
+
return entries.some(e => e.component && pathContainsPathSegment(file, path.dirname(e.component)))
|
|
168
|
+
}
|
|
158
169
|
}
|
package/lib/scripts.js
CHANGED
|
@@ -11,19 +11,18 @@ import {
|
|
|
11
11
|
fileSize
|
|
12
12
|
} from './utils/helpers.js'
|
|
13
13
|
import fs from 'node:fs'
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
const pstyle = new PrintStyle()
|
|
14
|
+
import log from './utils/log.js'
|
|
17
15
|
|
|
18
16
|
export default class Scripts {
|
|
19
17
|
constructor(config) {
|
|
20
18
|
this.config = config
|
|
21
|
-
this.banner = fillBannerTemplate(config.banner, config.pkg)
|
|
19
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner, config.pkg) : null
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
async compile() {
|
|
25
23
|
if (!this.config.scripts) return
|
|
26
24
|
this.config.scripts = Array.isArray(this.config.scripts) ? this.config.scripts : [this.config.scripts]
|
|
25
|
+
|
|
27
26
|
for (const scriptEntry of this.config.scripts) {
|
|
28
27
|
if (scriptEntry.in && scriptEntry.out && pathExists(scriptEntry.in)) {
|
|
29
28
|
mkPath(scriptEntry.out)
|
|
@@ -32,7 +31,7 @@ export default class Scripts {
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
34
|
+
async compileEntry(infilePath, outfilePath, options = {}, tag = 'script') {
|
|
36
35
|
if (!Array.isArray(infilePath)) infilePath = [infilePath]
|
|
37
36
|
|
|
38
37
|
const opts = {
|
|
@@ -57,7 +56,7 @@ export default class Scripts {
|
|
|
57
56
|
opts.outdir = outfilePath
|
|
58
57
|
} else {
|
|
59
58
|
if (infilePath.length > 1) {
|
|
60
|
-
|
|
59
|
+
log({ tag: 'error', text: 'Cannot output multiple script files to a single file. Please specify an output directory path instead.' })
|
|
61
60
|
process.exit(1)
|
|
62
61
|
}
|
|
63
62
|
opts.outfile = outfilePath
|
|
@@ -78,8 +77,8 @@ export default class Scripts {
|
|
|
78
77
|
try {
|
|
79
78
|
await build(opts)
|
|
80
79
|
} catch (err) {
|
|
81
|
-
|
|
82
|
-
console.
|
|
80
|
+
log({ tag, error: true, text: 'Failed compiling:', link: outfilePath })
|
|
81
|
+
console.error(err)
|
|
83
82
|
return
|
|
84
83
|
}
|
|
85
84
|
const esbuildEnd = performance.now()
|
|
@@ -88,8 +87,8 @@ export default class Scripts {
|
|
|
88
87
|
const newOutFilePath = buildScriptOutputFilePath(entry, outfilePath)
|
|
89
88
|
const minPath = insertMinSuffix(newOutFilePath)
|
|
90
89
|
|
|
91
|
-
if (!options.justMinified)
|
|
92
|
-
if (options.sourcemap)
|
|
90
|
+
if (!options.justMinified) log({ tag, text: 'Compiled:', link: newOutFilePath, size: fileSize(newOutFilePath), time: buildTime(esbuildStart, esbuildEnd) })
|
|
91
|
+
if (options.sourcemap) log({ tag, text: 'Compiled:', link: `${newOutFilePath}.map` })
|
|
93
92
|
|
|
94
93
|
if (options.minify) {
|
|
95
94
|
try {
|
|
@@ -102,10 +101,10 @@ export default class Scripts {
|
|
|
102
101
|
|
|
103
102
|
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
104
103
|
fs.writeFileSync(minPath, minifyResult.code)
|
|
105
|
-
|
|
104
|
+
log({ tag, text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
106
105
|
} catch (err) {
|
|
107
|
-
|
|
108
|
-
console.
|
|
106
|
+
log({ tag, error: true, text: 'Failed compiling:', link: minPath })
|
|
107
|
+
console.error(err)
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
if (options.justMinified) {
|
package/lib/styles.js
CHANGED
|
@@ -11,10 +11,9 @@ import {
|
|
|
11
11
|
} from './utils/helpers.js'
|
|
12
12
|
import path from 'node:path'
|
|
13
13
|
import * as sass from 'sass'
|
|
14
|
-
import
|
|
15
|
-
import sassPathResolver from 'sass-path-resolver'
|
|
16
|
-
|
|
17
|
-
const pstyle = new PrintStyle()
|
|
14
|
+
import log from './utils/log.js'
|
|
15
|
+
import { sassPathResolver } from 'sass-path-resolver'
|
|
16
|
+
import { sassTokenImporter } from 'sass-token-importer'
|
|
18
17
|
|
|
19
18
|
export default class Styles {
|
|
20
19
|
constructor(config) {
|
|
@@ -36,10 +35,19 @@ export default class Styles {
|
|
|
36
35
|
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
37
36
|
const includePaths = this.config.includePaths || []
|
|
38
37
|
|
|
38
|
+
const importers = [sassPathResolver(includePaths)]
|
|
39
|
+
|
|
40
|
+
if (options.tokenPaths) {
|
|
41
|
+
const tokenOpts = {}
|
|
42
|
+
if (options.tokenOutput) tokenOpts.output = options.tokenOutput
|
|
43
|
+
if (options.resolveAliases !== undefined) tokenOpts.resolveAliases = options.resolveAliases
|
|
44
|
+
importers.push(sassTokenImporter(options.tokenPaths, tokenOpts))
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
const opts = {
|
|
40
48
|
sourceMap: false,
|
|
41
49
|
sourceMapIncludeSources: false,
|
|
42
|
-
importers
|
|
50
|
+
importers
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
if (options.sourcemap) {
|
|
@@ -54,8 +62,8 @@ export default class Styles {
|
|
|
54
62
|
try {
|
|
55
63
|
compiledSass = sass.compile(infilePath, opts)
|
|
56
64
|
} catch (err) {
|
|
57
|
-
|
|
58
|
-
console.
|
|
65
|
+
log({ tag: 'style', error: true, text: 'Failed compiling:', link: outfilePath })
|
|
66
|
+
console.error(err)
|
|
59
67
|
return
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -63,12 +71,12 @@ export default class Styles {
|
|
|
63
71
|
if (this.banner) compiledSass.css = this.banner + '\n' + compiledSass.css
|
|
64
72
|
fs.writeFileSync(outfilePath, compiledSass.css + mapsrc)
|
|
65
73
|
const stylesEnd = performance.now()
|
|
66
|
-
if (!options.justMinified)
|
|
74
|
+
if (!options.justMinified) log({ tag: 'style', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(stylesStart, stylesEnd) })
|
|
67
75
|
|
|
68
76
|
if (compiledSass.sourceMap) {
|
|
69
77
|
if (this.banner) compiledSass.sourceMap.mappings = ';' + compiledSass.sourceMap.mappings
|
|
70
78
|
fs.writeFileSync(`${outfilePath}.map`, JSON.stringify(compiledSass.sourceMap))
|
|
71
|
-
|
|
79
|
+
log({ tag: 'style', text: 'Compiled:', link: `${outfilePath}.map` })
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
const minPath = insertMinSuffix(outfilePath)
|
|
@@ -84,10 +92,10 @@ export default class Styles {
|
|
|
84
92
|
if (this.banner) minified.code = this.banner + '\n' + minified.code
|
|
85
93
|
fs.writeFileSync(minPath, minified.code)
|
|
86
94
|
const stylesMinEnd = performance.now()
|
|
87
|
-
|
|
95
|
+
log({ tag: 'style', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(stylesMinStart, stylesMinEnd) })
|
|
88
96
|
} catch (err) {
|
|
89
|
-
|
|
90
|
-
console.
|
|
97
|
+
log({ tag: 'style', error: true, text: 'Failed compiling:', link: minPath })
|
|
98
|
+
console.error(err)
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
if (options.justMinified) {
|
package/lib/utils/helpers.js
CHANGED
|
@@ -4,8 +4,6 @@ import path from 'node:path'
|
|
|
4
4
|
import yaml from 'yaml'
|
|
5
5
|
import { convertGlobToRegex } from 'book-of-spells'
|
|
6
6
|
|
|
7
|
-
const frontMatterCache = new Map()
|
|
8
|
-
|
|
9
7
|
export function pathExists() {
|
|
10
8
|
return fs.existsSync(path.join(...arguments))
|
|
11
9
|
}
|
|
@@ -96,60 +94,6 @@ export function readYamlFile(filePath) {
|
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
export function parseFrontMatter(filePath) {
|
|
100
|
-
let stat
|
|
101
|
-
try {
|
|
102
|
-
stat = fs.statSync(filePath)
|
|
103
|
-
} catch (e) {
|
|
104
|
-
throw new Error(`Error stating file at ${filePath}: ${e.message}`)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const cached = frontMatterCache.get(filePath)
|
|
108
|
-
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
109
|
-
return { frontMatter: { ...cached.value.frontMatter }, content: cached.value.content }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let content = ''
|
|
113
|
-
try {
|
|
114
|
-
content = fs.readFileSync(filePath, 'utf8')
|
|
115
|
-
} catch (e) {
|
|
116
|
-
throw new Error(`Error reading file at ${filePath}: ${e.message}`)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!content) {
|
|
120
|
-
throw new Error(`File at ${filePath} is empty`)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const frontMatterRegex = /^\s*---\s*[\r\n]+([\s\S]*?)\s*---\s*[\r\n]+/
|
|
124
|
-
const match = content.match(frontMatterRegex)
|
|
125
|
-
|
|
126
|
-
if (!match) {
|
|
127
|
-
const value = { frontMatter: {}, content }
|
|
128
|
-
frontMatterCache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, value })
|
|
129
|
-
return { frontMatter: {}, content }
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let frontMatter = {}
|
|
133
|
-
try {
|
|
134
|
-
frontMatter = yaml.parse(match[1])
|
|
135
|
-
} catch (e) {
|
|
136
|
-
throw new Error(`Error parsing front matter in file at ${filePath}: ${e.message}`)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const contentWithoutFrontMatter = content.slice(match[0].length)
|
|
140
|
-
const value = { frontMatter, content: contentWithoutFrontMatter }
|
|
141
|
-
frontMatterCache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, value })
|
|
142
|
-
return { frontMatter: { ...frontMatter }, content: contentWithoutFrontMatter }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function clearFrontMatterCache(filePath) {
|
|
146
|
-
if (!filePath) {
|
|
147
|
-
frontMatterCache.clear()
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
frontMatterCache.delete(filePath)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
97
|
export function readDataFile(filePath) {
|
|
154
98
|
if (/(\.json)$/i.test(filePath)) return readJsonFile(filePath)
|
|
155
99
|
if (/(\.ya?ml)$/i.test(filePath)) return readYamlFile(filePath)
|
package/lib/utils/log.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import PrintStyle from 'printstyle'
|
|
2
|
+
|
|
3
|
+
const pstyle = new PrintStyle()
|
|
4
|
+
|
|
5
|
+
const TAG_COLORS = {
|
|
6
|
+
script: 'yellowBright',
|
|
7
|
+
reactor: 'yellowBright',
|
|
8
|
+
style: 'magentaBright',
|
|
9
|
+
markup: 'cyanBright',
|
|
10
|
+
copy: 'green',
|
|
11
|
+
info: 'blue',
|
|
12
|
+
error: 'redBright'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_COLOR = 'white'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Object} options
|
|
19
|
+
* @param {string} options.tag - Tag label (e.g. 'script', 'style', 'markup', 'reactor', 'copy', 'info')
|
|
20
|
+
* @param {boolean} [options.error] - Whether this is an error message
|
|
21
|
+
* @param {string} options.text - Main message text
|
|
22
|
+
* @param {string} [options.link] - File path or URL to display underlined
|
|
23
|
+
* @param {string} [options.size] - File size string
|
|
24
|
+
* @param {string} [options.time] - Build time string
|
|
25
|
+
*/
|
|
26
|
+
export default function log({ tag, error, text, link, size, time }) {
|
|
27
|
+
if (tag === 'error') error = true
|
|
28
|
+
const color = TAG_COLORS[tag] || DEFAULT_COLOR
|
|
29
|
+
let msg = pstyle.paint(`{${color}.bold|[${tag}]}`)
|
|
30
|
+
|
|
31
|
+
if (error) {
|
|
32
|
+
msg += pstyle.paint(' {redBright.bold|[error]}')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
msg += pstyle.paint(` {dim|${text}}`)
|
|
36
|
+
|
|
37
|
+
if (link) {
|
|
38
|
+
msg += pstyle.paint(` {italic.underline|${link}}`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (size) {
|
|
42
|
+
msg += pstyle.paint(` {greenBright|${size}}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (time) {
|
|
46
|
+
msg += pstyle.paint(` {green|(${time})}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (error) {
|
|
50
|
+
msg += pstyle.bell
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (error) {
|
|
54
|
+
console.error(msg)
|
|
55
|
+
} else {
|
|
56
|
+
console.log(msg)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const bell = pstyle.bell
|
|
61
|
+
|
|
62
|
+
export function styledLog(template) {
|
|
63
|
+
console.log(pstyle.paint(template))
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poops",
|
|
3
3
|
"description": "Straightforward, no-bullshit bundler for the web.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "poops.js",
|
|
@@ -26,40 +26,58 @@
|
|
|
26
26
|
"javascript",
|
|
27
27
|
"sass",
|
|
28
28
|
"nunjucks",
|
|
29
|
+
"liquid",
|
|
29
30
|
"esbuild",
|
|
30
31
|
"dart-sass",
|
|
32
|
+
"tailwindcss",
|
|
31
33
|
"toolchain-script",
|
|
32
34
|
"static-site-generator"
|
|
33
35
|
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"postcss": "^8.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"postcss": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
34
44
|
"scripts": {
|
|
35
45
|
"build": "node ./poops.js -b",
|
|
36
46
|
"lint": "eslint ./poops.js",
|
|
37
47
|
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
38
48
|
},
|
|
39
49
|
"dependencies": {
|
|
50
|
+
"argoyle": "^1.0.0",
|
|
40
51
|
"book-of-spells": "^1.0.32",
|
|
41
52
|
"chokidar": "^3.5.3",
|
|
42
53
|
"connect": "^3.7.0",
|
|
54
|
+
"dayjs": "^1.11.19",
|
|
43
55
|
"esbuild": "^0.25.0",
|
|
44
56
|
"glob": "^13.0.6",
|
|
57
|
+
"highlight.js": "^11.11.1",
|
|
58
|
+
"liquidjs": "^10.24.0",
|
|
45
59
|
"livereload": "^0.9.3",
|
|
46
60
|
"marked": "^9.0.3",
|
|
47
|
-
"moment": "^2.29.4",
|
|
48
61
|
"nunjucks": "^3.2.4",
|
|
49
62
|
"portscanner": "^2.2.0",
|
|
63
|
+
"printstyle": "^1.0.0",
|
|
50
64
|
"sass": "^1.63.4",
|
|
51
65
|
"sass-path-resolver": "^1.0.2",
|
|
66
|
+
"sass-token-importer": "^1.0.0",
|
|
52
67
|
"serve-static": "^1.15.0",
|
|
53
68
|
"yaml": "^2.3.1"
|
|
54
69
|
},
|
|
55
70
|
"devDependencies": {
|
|
56
71
|
"@jest/globals": "^30.2.0",
|
|
72
|
+
"@tailwindcss/postcss": "^4.2.1",
|
|
57
73
|
"eslint": "^9.39.3",
|
|
58
74
|
"jest": "^30.2.0",
|
|
59
75
|
"neostandard": "^0.12.2",
|
|
76
|
+
"postcss": "^8.5.8",
|
|
60
77
|
"react": "^19.2.4",
|
|
61
78
|
"react-dom": "^19.2.4",
|
|
62
|
-
"sulphuris": "^2.0.0"
|
|
79
|
+
"sulphuris": "^2.0.0",
|
|
80
|
+
"tailwindcss": "^4.2.1"
|
|
63
81
|
},
|
|
64
82
|
"overrides": {
|
|
65
83
|
"minimatch": ">=10.2.1"
|