@ossy/app 1.15.0 → 1.15.2

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.
@@ -1,242 +0,0 @@
1
- const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
2
-
3
- const dim = (s) => `\x1b[2m${s}\x1b[0m`
4
- const bold = (s) => `\x1b[1m${s}\x1b[0m`
5
- const green = (s) => `\x1b[32m${s}\x1b[0m`
6
- const red = (s) => `\x1b[31m${s}\x1b[0m`
7
- const yellow = (s) => `\x1b[33m${s}\x1b[0m`
8
-
9
- function stripAnsi (s) {
10
- return String(s).replace(/\x1b\[[0-9;]*m/g, '')
11
- }
12
-
13
- function padVisible (s, width) {
14
- const n = stripAnsi(s).length
15
- return n >= width ? s : s + ' '.repeat(width - n)
16
- }
17
-
18
- function truncateVisible (s, maxW) {
19
- const plain = stripAnsi(s)
20
- if (plain.length <= maxW) return s
21
- const cut = Math.max(0, maxW - 1)
22
- return plain.slice(0, cut) + '…'
23
- }
24
-
25
- export function pageIdFromHydrateEntryName (entryName) {
26
- const p = String(entryName)
27
- return p.startsWith('hydrate__') ? p.slice('hydrate__'.length) : p
28
- }
29
-
30
- function fmtPart (part, shortLabel, frame) {
31
- if (part.s === 'idle') return dim('—')
32
- if (part.s === 'running') {
33
- const sp = SPINNER[frame % SPINNER.length]
34
- return `${yellow(sp)} ${dim(shortLabel + '…')}`
35
- }
36
- if (part.s === 'ok') return `${green('✔')} ${dim(`${part.ms}ms`)}`
37
- if (part.s === 'fail') return `${red('✖')} ${dim(shortLabel)}`
38
- if (part.s === 'skip') return dim(part.note ? `— ${part.note}` : '—')
39
- return dim('—')
40
- }
41
-
42
- function rowLeadIcon (r, mode) {
43
- if (mode === 'prerender-only') {
44
- const p = r.prerender.s
45
- if (p === 'fail') return red('✖')
46
- if (p === 'running') return yellow('◐')
47
- if (p === 'ok' || p === 'skip') return green('✔')
48
- if (p === 'idle') return dim('·')
49
- return green('✔')
50
- }
51
- const b = r.bundle.s
52
- const p = r.prerender.s
53
- if (b === 'fail' || p === 'fail') return red('✖')
54
- if (b === 'running' || p === 'running') return yellow('◐')
55
- if (b === 'ok' && p === 'idle') return yellow('◐')
56
- if (b === 'ok' && (p === 'ok' || p === 'skip')) return green('✔')
57
- if (b === 'idle' && p === 'idle') return dim('·')
58
- return green('✔')
59
- }
60
-
61
- function useTty (stream) {
62
- return (
63
- stream.isTTY === true &&
64
- process.env.TERM !== 'dumb' &&
65
- process.env.CI !== 'true' &&
66
- process.env.OSSY_PLAIN_OUTPUT !== '1'
67
- )
68
- }
69
-
70
- export function createBuildDashboard ({
71
- scope = '@ossy/app',
72
- mode = 'full',
73
- pageIds,
74
- idToPath = {},
75
- overview = { title: '@ossy/app', configRel: null, apiRoutes: [] },
76
- stream = process.stdout,
77
- } = {}) {
78
- const ids = [...pageIds].sort()
79
- const maxId = Math.max(6, ...ids.map((id) => String(id).length))
80
- const tty = useTty(stream)
81
- const termW = Math.max(60, stream.columns || 100)
82
-
83
- const rows = new Map()
84
- for (const id of ids) {
85
- rows.set(id, {
86
- bundle: { s: 'idle' },
87
- prerender: { s: 'idle' },
88
- })
89
- }
90
-
91
- let frame = 0
92
- let spinTimer = null
93
- let blockLines = 0
94
-
95
- const anyRunning = () => {
96
- for (const r of rows.values()) {
97
- if (r.bundle.s === 'running' || r.prerender.s === 'running') return true
98
- }
99
- return false
100
- }
101
-
102
- function headerLines () {
103
- const out = []
104
- out.push(bold(overview.title || `${scope} build`))
105
- if (overview.configRel) {
106
- out.push(`${dim('config')} ${truncateVisible(overview.configRel, termW - 10)}`)
107
- }
108
- const api = overview.apiRoutes || []
109
- if (api.length > 0) {
110
- if (api.length <= 2) {
111
- for (const r of api) {
112
- out.push(`${dim('api')} ${truncateVisible(`${r.id} ${r.path}`, termW - 6)}`)
113
- }
114
- } else {
115
- out.push(`${dim('api')} ${api.length} routes`)
116
- }
117
- }
118
- out.push(dim('─'.repeat(Math.min(termW - 2, 44))))
119
- return out
120
- }
121
-
122
- function lineForPageRow (id) {
123
- const r = rows.get(id)
124
- const lead = rowLeadIcon(r, mode)
125
- const pathStr = idToPath[id] != null ? dim(String(idToPath[id])) : ''
126
- const idCol = padVisible(dim(String(id)), maxId)
127
- const pathPad = padVisible(pathStr, 14)
128
- const p = fmtPart(r.prerender, 'html', frame)
129
- if (mode === 'prerender-only') {
130
- return ` ${lead} ${dim(scope)} ${idCol} ${pathPad} ${p}`
131
- }
132
- const b = fmtPart(r.bundle, 'hydrate', frame)
133
- return ` ${lead} ${dim(scope)} ${idCol} ${pathPad} ${padVisible(b, 20)} ${p}`
134
- }
135
-
136
- function buildLines () {
137
- return [...headerLines(), ...ids.map((id) => lineForPageRow(id))]
138
- }
139
-
140
- function redraw () {
141
- if (!tty) return
142
- const lines = buildLines()
143
- if (blockLines === 0) {
144
- for (const ln of lines) stream.write(ln + '\n')
145
- blockLines = lines.length
146
- return
147
- }
148
- stream.write(`\x1b[${blockLines}A`)
149
- for (const ln of lines) stream.write(`\x1b[2K\r${ln}\n`)
150
- }
151
-
152
- function ensureSpin () {
153
- if (!tty || spinTimer) return
154
- spinTimer = setInterval(() => {
155
- frame += 1
156
- redraw()
157
- }, 80)
158
- }
159
-
160
- function stopSpin () {
161
- if (spinTimer) {
162
- clearInterval(spinTimer)
163
- spinTimer = null
164
- }
165
- }
166
-
167
- function nonTtyLine (id, phase, ok, ms, err) {
168
- const label = phase === 'bundle' ? 'hydrate' : 'prerender'
169
- const icon = ok ? green('✔') : red('✖')
170
- const time = ms != null ? dim(` (${ms}ms)`) : ''
171
- stream.write(`${icon} ${dim(`${scope}:${label}:`)}${id}${time}\n`)
172
- if (err && !ok) stream.write(` ${red(String(err.message || err))}\n`)
173
- }
174
-
175
- return {
176
- start () {
177
- if (!tty) {
178
- for (const ln of headerLines()) stream.write(`${ln}\n`)
179
- stream.write('\n')
180
- return
181
- }
182
- redraw()
183
- },
184
-
185
- startBundle (pageId) {
186
- if (mode === 'prerender-only') return
187
- const r = rows.get(pageId)
188
- if (!r) return
189
- r.bundle = { s: 'running' }
190
- ensureSpin()
191
- redraw()
192
- },
193
-
194
- completeBundle (pageId, { ok, ms, error }) {
195
- if (mode === 'prerender-only') return
196
- const r = rows.get(pageId)
197
- if (!r) return
198
- r.bundle = ok ? { s: 'ok', ms } : { s: 'fail', ms, error }
199
- if (!ok) {
200
- r.prerender = { s: 'skip', note: 'bundle failed' }
201
- if (!tty) nonTtyLine(pageId, 'bundle', false, ms, error)
202
- }
203
- if (!anyRunning()) stopSpin()
204
- redraw()
205
- },
206
-
207
- startPrerender (pageId) {
208
- const r = rows.get(pageId)
209
- if (!r || r.prerender.s === 'skip') return
210
- r.prerender = { s: 'running' }
211
- ensureSpin()
212
- redraw()
213
- },
214
-
215
- completePrerender (pageId, { ok, ms, error }) {
216
- const r = rows.get(pageId)
217
- if (!r || r.prerender.s === 'skip') return
218
- r.prerender = ok ? { s: 'ok', ms } : { s: 'fail', ms, error }
219
- if (!tty) nonTtyLine(pageId, 'prerender', ok, ms, error)
220
- if (!anyRunning()) stopSpin()
221
- redraw()
222
- },
223
-
224
- skipPrerender (pageId, note) {
225
- const r = rows.get(pageId)
226
- if (!r) return
227
- if (r.bundle.s === 'fail') return
228
- r.prerender = { s: 'skip', note: note || 'skipped' }
229
- if (!tty) stream.write(`${dim('—')} ${scope}:prerender:${pageId} (${note})\n`)
230
- redraw()
231
- },
232
-
233
- dispose () {
234
- stopSpin()
235
- if (tty && blockLines > 0) {
236
- redraw()
237
- stream.write('\n')
238
- blockLines = 0
239
- }
240
- },
241
- }
242
- }