poops 1.2.3 → 1.2.4
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 +239 -65
- package/lib/copy.js +4 -2
- package/lib/images.js +57 -0
- package/lib/markup/collections.js +34 -13
- package/lib/markup/engines/liquid.js +15 -27
- package/lib/markup/engines/nunjucks.js +17 -30
- package/lib/markup/helpers.js +133 -37
- package/lib/markup/image-cache.js +114 -0
- package/lib/markup/indexer.js +6 -2
- package/lib/markups.js +54 -10
- package/lib/postcss.js +16 -30
- package/lib/reactor.js +11 -29
- package/lib/scripts.js +37 -34
- package/lib/styles.js +17 -31
- package/lib/utils/helpers.js +20 -16
- package/lib/utils/log.js +15 -1
- package/lib/utils/minify.js +41 -0
- package/package.json +7 -3
- package/poops.js +106 -27
package/lib/postcss.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { transform } from 'esbuild'
|
|
2
1
|
import fs from 'node:fs'
|
|
3
2
|
import {
|
|
4
3
|
pathExists,
|
|
5
4
|
mkPath,
|
|
6
|
-
insertMinSuffix,
|
|
7
5
|
buildStyleOutputFilePath,
|
|
8
6
|
fillBannerTemplate,
|
|
9
7
|
buildTime,
|
|
10
8
|
fileSize
|
|
11
9
|
} from './utils/helpers.js'
|
|
10
|
+
import minifyToFile from './utils/minify.js'
|
|
12
11
|
import log from './utils/log.js'
|
|
13
12
|
|
|
14
13
|
let postcss
|
|
@@ -65,10 +64,13 @@ export default class PostCSS {
|
|
|
65
64
|
|
|
66
65
|
this.config.postcss = Array.isArray(this.config.postcss) ? this.config.postcss : [this.config.postcss]
|
|
67
66
|
for (const entry of this.config.postcss) {
|
|
68
|
-
if (entry.in
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
if (!entry.in || !entry.out) continue
|
|
68
|
+
if (!pathExists(entry.in)) {
|
|
69
|
+
log({ tag: 'postcss', error: true, text: 'Entry does not exist:', link: entry.in })
|
|
70
|
+
continue
|
|
71
71
|
}
|
|
72
|
+
mkPath(entry.out)
|
|
73
|
+
await this.compileEntry(entry.in, entry.out, entry.options)
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -98,30 +100,14 @@ export default class PostCSS {
|
|
|
98
100
|
const end = performance.now()
|
|
99
101
|
if (!options.justMinified) log({ tag: 'postcss', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(start, end) })
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
}
|
|
103
|
+
await minifyToFile({
|
|
104
|
+
outfilePath,
|
|
105
|
+
loader: 'css',
|
|
106
|
+
code: css,
|
|
107
|
+
banner: this.banner,
|
|
108
|
+
tag: 'postcss',
|
|
109
|
+
options,
|
|
110
|
+
startTime: options.justMinified ? start : undefined
|
|
111
|
+
})
|
|
126
112
|
}
|
|
127
113
|
}
|
package/lib/reactor.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { build
|
|
1
|
+
import { build } from 'esbuild'
|
|
2
2
|
import { deepMerge } from 'book-of-spells'
|
|
3
3
|
import {
|
|
4
4
|
pathExists,
|
|
5
5
|
mkPath,
|
|
6
|
-
insertMinSuffix,
|
|
7
6
|
pathContainsPathSegment,
|
|
8
7
|
fillBannerTemplate,
|
|
9
8
|
buildTime,
|
|
10
9
|
fileSize
|
|
11
10
|
} from './utils/helpers.js'
|
|
11
|
+
import minifyToFile from './utils/minify.js'
|
|
12
12
|
import fs from 'node:fs'
|
|
13
13
|
import path from 'node:path'
|
|
14
14
|
import { createRequire } from 'node:module'
|
|
@@ -29,9 +29,12 @@ export default class Reactor {
|
|
|
29
29
|
this.rendered = {}
|
|
30
30
|
|
|
31
31
|
for (const entry of this.config.reactor) {
|
|
32
|
-
if (entry.component
|
|
33
|
-
|
|
32
|
+
if (!entry.component || !entry.inject) continue
|
|
33
|
+
if (!pathExists(entry.component)) {
|
|
34
|
+
log({ tag: 'reactor', error: true, text: 'Component does not exist:', link: entry.component })
|
|
35
|
+
continue
|
|
34
36
|
}
|
|
37
|
+
await this.compileEntry(entry)
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
this.renderedChanged = JSON.stringify(this.rendered) !== JSON.stringify(prevRendered)
|
|
@@ -83,6 +86,9 @@ export const html = renderToString(React.createElement(Component));
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
// --- Step 2: Bundle the client entry for the browser ---
|
|
89
|
+
if (client && out && !pathExists(client)) {
|
|
90
|
+
log({ tag: 'reactor', error: true, text: 'Entry does not exist:', link: client })
|
|
91
|
+
}
|
|
86
92
|
if (client && out && pathExists(client)) {
|
|
87
93
|
mkPath(out)
|
|
88
94
|
|
|
@@ -129,31 +135,7 @@ export const html = renderToString(React.createElement(Component));
|
|
|
129
135
|
if (!options.justMinified) log({ tag: 'reactor', text: 'Compiled:', link: out, size: fileSize(out), time: buildTime(esbuildStart, esbuildEnd) })
|
|
130
136
|
if (options.sourcemap) log({ tag: 'reactor', text: 'Compiled:', link: `${out}.map` })
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
const minPath = insertMinSuffix(out)
|
|
134
|
-
try {
|
|
135
|
-
const terserStart = performance.now()
|
|
136
|
-
const minifyResult = await transform(fs.readFileSync(out, 'utf-8'), {
|
|
137
|
-
minify: true,
|
|
138
|
-
loader: 'js'
|
|
139
|
-
})
|
|
140
|
-
const terserEnd = performance.now()
|
|
141
|
-
|
|
142
|
-
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
143
|
-
fs.writeFileSync(minPath, minifyResult.code)
|
|
144
|
-
log({ tag: 'reactor', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
145
|
-
} catch (err) {
|
|
146
|
-
log({ tag: 'reactor', error: true, text: 'Failed compiling:', link: minPath })
|
|
147
|
-
console.error(err)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (options.justMinified) {
|
|
151
|
-
fs.unlinkSync(out)
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
const minPath = insertMinSuffix(out)
|
|
155
|
-
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
156
|
-
}
|
|
138
|
+
await minifyToFile({ outfilePath: out, loader: 'js', banner: this.banner, tag: 'reactor', options })
|
|
157
139
|
}
|
|
158
140
|
}
|
|
159
141
|
|
package/lib/scripts.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { build
|
|
1
|
+
import { build } from 'esbuild'
|
|
2
|
+
import { globSync, hasMagic } from 'glob'
|
|
2
3
|
import { deepMerge } from 'book-of-spells'
|
|
3
4
|
import {
|
|
4
5
|
pathExists,
|
|
5
6
|
mkPath,
|
|
6
7
|
pathForFile,
|
|
7
|
-
insertMinSuffix,
|
|
8
|
-
buildScriptOutputFilePath,
|
|
9
8
|
fillBannerTemplate,
|
|
10
9
|
buildTime,
|
|
11
10
|
fileSize
|
|
12
11
|
} from './utils/helpers.js'
|
|
13
|
-
import
|
|
12
|
+
import minifyToFile from './utils/minify.js'
|
|
14
13
|
import log from './utils/log.js'
|
|
15
14
|
|
|
16
15
|
export default class Scripts {
|
|
@@ -24,10 +23,30 @@ export default class Scripts {
|
|
|
24
23
|
this.config.scripts = Array.isArray(this.config.scripts) ? this.config.scripts : [this.config.scripts]
|
|
25
24
|
|
|
26
25
|
for (const scriptEntry of this.config.scripts) {
|
|
27
|
-
if (scriptEntry.in
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if (!scriptEntry.in || !scriptEntry.out) continue
|
|
27
|
+
// `in` may be an array of entry points — pathExists on an array throws
|
|
28
|
+
const configured = Array.isArray(scriptEntry.in) ? scriptEntry.in : [scriptEntry.in]
|
|
29
|
+
const entryPoints = []
|
|
30
|
+
let missing = false
|
|
31
|
+
for (const entry of configured) {
|
|
32
|
+
if (hasMagic(entry)) {
|
|
33
|
+
// Globs must use `/` even on Windows; sort for deterministic build order
|
|
34
|
+
const matches = globSync(entry, { posix: true }).sort()
|
|
35
|
+
if (!matches.length) {
|
|
36
|
+
log({ tag: 'script', error: true, text: 'Entry does not exist:', link: entry })
|
|
37
|
+
missing = true
|
|
38
|
+
}
|
|
39
|
+
entryPoints.push(...matches)
|
|
40
|
+
} else if (!pathExists(entry)) {
|
|
41
|
+
log({ tag: 'script', error: true, text: 'Entry does not exist:', link: entry })
|
|
42
|
+
missing = true
|
|
43
|
+
} else {
|
|
44
|
+
entryPoints.push(entry)
|
|
45
|
+
}
|
|
30
46
|
}
|
|
47
|
+
if (missing) continue
|
|
48
|
+
mkPath(scriptEntry.out)
|
|
49
|
+
await this.compileEntry(entryPoints, scriptEntry.out, scriptEntry.options)
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
|
|
@@ -73,9 +92,16 @@ export default class Scripts {
|
|
|
73
92
|
|
|
74
93
|
deepMerge(opts, optionsClone) // ability to pass other esbuild options `node_modules/esbuild/lib/main.d.ts`
|
|
75
94
|
|
|
95
|
+
// Multi-dir entry points nest output under their common ancestor (esbuild's
|
|
96
|
+
// outbase), so output paths can't be derived from basenames — read them
|
|
97
|
+
// from the metafile instead. Keys are relative to absWorkingDir.
|
|
98
|
+
opts.metafile = true
|
|
99
|
+
opts.absWorkingDir = process.cwd()
|
|
100
|
+
|
|
76
101
|
const esbuildStart = performance.now()
|
|
102
|
+
let result
|
|
77
103
|
try {
|
|
78
|
-
await build(opts)
|
|
104
|
+
result = await build(opts)
|
|
79
105
|
} catch (err) {
|
|
80
106
|
log({ tag, error: true, text: 'Failed compiling:', link: outfilePath })
|
|
81
107
|
console.error(err)
|
|
@@ -83,36 +109,13 @@ export default class Scripts {
|
|
|
83
109
|
}
|
|
84
110
|
const esbuildEnd = performance.now()
|
|
85
111
|
|
|
86
|
-
for (const
|
|
87
|
-
|
|
88
|
-
const minPath = insertMinSuffix(newOutFilePath)
|
|
112
|
+
for (const [newOutFilePath, output] of Object.entries(result.metafile.outputs)) {
|
|
113
|
+
if (!output.entryPoint) continue // sourcemaps, chunks
|
|
89
114
|
|
|
90
115
|
if (!options.justMinified) log({ tag, text: 'Compiled:', link: newOutFilePath, size: fileSize(newOutFilePath), time: buildTime(esbuildStart, esbuildEnd) })
|
|
91
116
|
if (options.sourcemap) log({ tag, text: 'Compiled:', link: `${newOutFilePath}.map` })
|
|
92
117
|
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const terserStart = performance.now()
|
|
96
|
-
const minifyResult = await transform(fs.readFileSync(newOutFilePath, 'utf-8'), {
|
|
97
|
-
minify: true,
|
|
98
|
-
loader: 'js'
|
|
99
|
-
})
|
|
100
|
-
const terserEnd = performance.now()
|
|
101
|
-
|
|
102
|
-
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
103
|
-
fs.writeFileSync(minPath, minifyResult.code)
|
|
104
|
-
log({ tag, text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
105
|
-
} catch (err) {
|
|
106
|
-
log({ tag, error: true, text: 'Failed compiling:', link: minPath })
|
|
107
|
-
console.error(err)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (options.justMinified) {
|
|
111
|
-
fs.unlinkSync(newOutFilePath)
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
115
|
-
}
|
|
118
|
+
await minifyToFile({ outfilePath: newOutFilePath, loader: 'js', banner: this.banner, tag, options })
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
}
|
package/lib/styles.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { transform } from 'esbuild'
|
|
2
1
|
import fs from 'node:fs'
|
|
3
2
|
import {
|
|
4
3
|
pathExists,
|
|
5
4
|
mkPath,
|
|
6
|
-
insertMinSuffix,
|
|
7
5
|
buildStyleOutputFilePath,
|
|
8
6
|
fillBannerTemplate,
|
|
9
7
|
buildTime,
|
|
10
8
|
fileSize
|
|
11
9
|
} from './utils/helpers.js'
|
|
10
|
+
import minifyToFile from './utils/minify.js'
|
|
12
11
|
import path from 'node:path'
|
|
13
12
|
import * as sass from 'sass'
|
|
14
13
|
import log from './utils/log.js'
|
|
@@ -25,10 +24,13 @@ export default class Styles {
|
|
|
25
24
|
if (!this.config.styles) return
|
|
26
25
|
this.config.styles = Array.isArray(this.config.styles) ? this.config.styles : [this.config.styles]
|
|
27
26
|
for (const styleEntry of this.config.styles) {
|
|
28
|
-
if (styleEntry.in
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (!styleEntry.in || !styleEntry.out) continue
|
|
28
|
+
if (!pathExists(styleEntry.in)) {
|
|
29
|
+
log({ tag: 'style', error: true, text: 'Entry does not exist:', link: styleEntry.in })
|
|
30
|
+
continue
|
|
31
31
|
}
|
|
32
|
+
mkPath(styleEntry.out)
|
|
33
|
+
await this.compileEntry(styleEntry.in, styleEntry.out, styleEntry.options)
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -55,7 +57,7 @@ export default class Styles {
|
|
|
55
57
|
opts.sourceMapIncludeSources = options.sourcemap
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
outfilePath = buildStyleOutputFilePath(infilePath, outfilePath
|
|
60
|
+
outfilePath = buildStyleOutputFilePath(infilePath, outfilePath)
|
|
59
61
|
|
|
60
62
|
const stylesStart = performance.now()
|
|
61
63
|
let compiledSass
|
|
@@ -79,30 +81,14 @@ export default class Styles {
|
|
|
79
81
|
log({ tag: 'style', text: 'Compiled:', link: `${outfilePath}.map` })
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (this.banner) minified.code = this.banner + '\n' + minified.code
|
|
93
|
-
fs.writeFileSync(minPath, minified.code)
|
|
94
|
-
const stylesMinEnd = performance.now()
|
|
95
|
-
log({ tag: 'style', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(stylesMinStart, stylesMinEnd) })
|
|
96
|
-
} catch (err) {
|
|
97
|
-
log({ tag: 'style', error: true, text: 'Failed compiling:', link: minPath })
|
|
98
|
-
console.error(err)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (options.justMinified) {
|
|
102
|
-
fs.unlinkSync(outfilePath)
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
106
|
-
}
|
|
84
|
+
await minifyToFile({
|
|
85
|
+
outfilePath,
|
|
86
|
+
loader: 'css',
|
|
87
|
+
code: compiledSass.css,
|
|
88
|
+
banner: this.banner,
|
|
89
|
+
tag: 'style',
|
|
90
|
+
options,
|
|
91
|
+
startTime: options.justMinified ? stylesStart : undefined
|
|
92
|
+
})
|
|
107
93
|
}
|
|
108
94
|
}
|
package/lib/utils/helpers.js
CHANGED
|
@@ -4,6 +4,10 @@ import path from 'node:path'
|
|
|
4
4
|
import yaml from 'yaml'
|
|
5
5
|
import { convertGlobToRegex } from 'book-of-spells'
|
|
6
6
|
|
|
7
|
+
export function toPosix(filePath) {
|
|
8
|
+
return path.sep === '/' ? filePath : filePath.split(path.sep).join('/')
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
export function pathExists() {
|
|
8
12
|
return fs.existsSync(path.join(...arguments))
|
|
9
13
|
}
|
|
@@ -36,13 +40,6 @@ export function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
|
36
40
|
return path.join(path.join(outputPath, `${name}.css`))
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
export function buildScriptOutputFilePath(inputPath, outputPath) {
|
|
40
|
-
if (pathForFile(outputPath)) return outputPath
|
|
41
|
-
const { name, ext } = path.parse(inputPath)
|
|
42
|
-
const outExt = /\.(tsx?|jsx)$/i.test(ext) ? '.js' : ext
|
|
43
|
-
return path.join(outputPath, `${name}${outExt}`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
43
|
export function fillBannerTemplate(template, packagesPath) {
|
|
47
44
|
packagesPath = packagesPath || process.cwd()
|
|
48
45
|
const packagesFilePath = path.join(packagesPath, 'package.json')
|
|
@@ -63,13 +60,14 @@ export function fillBannerTemplate(template, packagesPath) {
|
|
|
63
60
|
|
|
64
61
|
export function buildTime(start, end) {
|
|
65
62
|
const time = Math.round(end - start)
|
|
66
|
-
|
|
67
|
-
const seconds = Math.floor(time / 1000)
|
|
63
|
+
const minutes = Math.floor(time / 60000)
|
|
64
|
+
const seconds = Math.floor(time / 1000) % 60
|
|
68
65
|
const ms = time % 1000
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
const parts = []
|
|
67
|
+
if (minutes) parts.push(`${minutes}m`)
|
|
68
|
+
if (seconds) parts.push(`${seconds}s`)
|
|
69
|
+
if (ms) parts.push(`${ms}ms`)
|
|
70
|
+
return parts.join(' ') || '0ms'
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
export function fileSize(filePath) {
|
|
@@ -77,8 +75,12 @@ export function fileSize(filePath) {
|
|
|
77
75
|
const fileSizeInBytes = stats.size
|
|
78
76
|
if (fileSizeInBytes < 1000) return `${fileSizeInBytes}B`
|
|
79
77
|
if (fileSizeInBytes < 1000 * 1000) return `${(fileSizeInBytes / 1000).toFixed(0)}KB`
|
|
80
|
-
if (fileSizeInBytes < 1000 * 1000 * 1000)
|
|
81
|
-
|
|
78
|
+
if (fileSizeInBytes < 1000 * 1000 * 1000) {
|
|
79
|
+
const kb = Math.floor((fileSizeInBytes % (1000 * 1000)) / 1000)
|
|
80
|
+
return `${Math.floor(fileSizeInBytes / 1000 / 1000)}MB${kb ? ` ${kb}KB` : ''}`
|
|
81
|
+
}
|
|
82
|
+
const mb = Math.floor((fileSizeInBytes % (1000 * 1000 * 1000)) / 1000 / 1000)
|
|
83
|
+
return `${Math.floor(fileSizeInBytes / 1000 / 1000 / 1000)}GB${mb ? ` ${mb}MB` : ''}`
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export function readJsonFile(filePath) {
|
|
@@ -146,10 +148,12 @@ export function copyDirectory(src, dest) {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
export function stripDirNavSegments(filePath) {
|
|
149
|
-
return path.normalize(filePath).replace(/(\.\.\/|\.\/|\/\.\.|\.\.\\|\.\\|\\\.\.)/g, '')
|
|
151
|
+
return toPosix(path.normalize(filePath)).replace(/(\.\.\/|\.\/|\/\.\.|\.\.\\|\.\\|\\\.\.)/g, '')
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
export function pathContainsPathSegment(filePath, segment) {
|
|
155
|
+
// Watcher paths arrive with native separators, config segments with `/`
|
|
156
|
+
filePath = toPosix(filePath)
|
|
153
157
|
segment = stripDirNavSegments(segment)
|
|
154
158
|
if (hasMagic(segment)) {
|
|
155
159
|
segment = convertGlobToRegex(segment)
|
package/lib/utils/log.js
CHANGED
|
@@ -8,6 +8,7 @@ const TAG_COLORS = {
|
|
|
8
8
|
style: 'magentaBright',
|
|
9
9
|
markup: 'cyanBright',
|
|
10
10
|
copy: 'green',
|
|
11
|
+
image: 'greenBright',
|
|
11
12
|
info: 'blue',
|
|
12
13
|
error: 'redBright'
|
|
13
14
|
}
|
|
@@ -18,18 +19,31 @@ const DEFAULT_COLOR = 'white'
|
|
|
18
19
|
* @param {Object} options
|
|
19
20
|
* @param {string} options.tag - Tag label (e.g. 'script', 'style', 'markup', 'reactor', 'copy', 'info')
|
|
20
21
|
* @param {boolean} [options.error] - Whether this is an error message
|
|
22
|
+
* @param {boolean} [options.warn] - Whether this is a warning (does not fail the build)
|
|
21
23
|
* @param {string} options.text - Main message text
|
|
22
24
|
* @param {string} [options.link] - File path or URL to display underlined
|
|
23
25
|
* @param {string} [options.size] - File size string
|
|
24
26
|
* @param {string} [options.time] - Build time string
|
|
25
27
|
*/
|
|
26
|
-
|
|
28
|
+
let errorLogged = false
|
|
29
|
+
|
|
30
|
+
// Build mode exits non-zero if any module reported an error, even ones
|
|
31
|
+
// swallowed internally (log-and-continue). log() is the single point
|
|
32
|
+
// every module error passes through, so the flag lives here.
|
|
33
|
+
export function hasLoggedErrors() {
|
|
34
|
+
return errorLogged
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function log({ tag, error, warn, text, link, size, time }) {
|
|
27
38
|
if (tag === 'error') error = true
|
|
39
|
+
if (error) errorLogged = true
|
|
28
40
|
const color = TAG_COLORS[tag] || DEFAULT_COLOR
|
|
29
41
|
let msg = pstyle.paint(`{${color}.bold|[${tag}]}`)
|
|
30
42
|
|
|
31
43
|
if (error) {
|
|
32
44
|
msg += pstyle.paint(' {redBright.bold|[error]}')
|
|
45
|
+
} else if (warn) {
|
|
46
|
+
msg += pstyle.paint(' {yellowBright.bold|[warn]}')
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
msg += pstyle.paint(` {dim|${text}}`)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { transform } from 'esbuild'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import { pathExists, insertMinSuffix, fileSize, buildTime } from './helpers.js'
|
|
4
|
+
import log from './log.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared post-compile minify step: writes `<name>.min<ext>` next to
|
|
8
|
+
* `outfilePath`, deletes the unminified file when `options.justMinified`,
|
|
9
|
+
* or deletes a stale `.min` file when minify is off.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} params
|
|
12
|
+
* @param {string} params.outfilePath - Compiled (unminified) output file
|
|
13
|
+
* @param {'js'|'css'} params.loader - esbuild transform loader
|
|
14
|
+
* @param {string} [params.code] - Source to minify; read from outfilePath when omitted
|
|
15
|
+
* @param {string} [params.banner] - Banner prepended to the minified code
|
|
16
|
+
* @param {string} params.tag - Log tag
|
|
17
|
+
* @param {Object} [params.options] - Entry options (`minify`, `justMinified`)
|
|
18
|
+
* @param {number} [params.startTime] - Timing start override (justMinified logs include compile time)
|
|
19
|
+
*/
|
|
20
|
+
export default async function minifyToFile({ outfilePath, loader, code, banner, tag, options = {}, startTime }) {
|
|
21
|
+
const minPath = insertMinSuffix(outfilePath)
|
|
22
|
+
|
|
23
|
+
if (!options.minify) {
|
|
24
|
+
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const start = startTime ?? performance.now()
|
|
30
|
+
if (code == null) code = fs.readFileSync(outfilePath, 'utf-8')
|
|
31
|
+
const minified = await transform(code, { loader, minify: true })
|
|
32
|
+
if (banner) minified.code = banner + '\n' + minified.code
|
|
33
|
+
fs.writeFileSync(minPath, minified.code)
|
|
34
|
+
log({ tag, text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(start, performance.now()) })
|
|
35
|
+
} catch (err) {
|
|
36
|
+
log({ tag, error: true, text: 'Failed compiling:', link: minPath })
|
|
37
|
+
console.error(err)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.justMinified) fs.unlinkSync(outfilePath)
|
|
41
|
+
}
|
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.2.
|
|
4
|
+
"version": "1.2.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "poops.js",
|
|
@@ -34,16 +34,20 @@
|
|
|
34
34
|
"static-site-generator"
|
|
35
35
|
],
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"postcss": "^8.0.0"
|
|
37
|
+
"postcss": "^8.0.0",
|
|
38
|
+
"poops-images": ">=1.2.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependenciesMeta": {
|
|
40
41
|
"postcss": {
|
|
41
42
|
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"poops-images": {
|
|
45
|
+
"optional": true
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"scripts": {
|
|
45
49
|
"build": "node ./poops.js -b",
|
|
46
|
-
"lint": "eslint
|
|
50
|
+
"lint": "eslint .",
|
|
47
51
|
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
48
52
|
},
|
|
49
53
|
"dependencies": {
|