diff --git a/sources/@roots/bud-dashboard/src/application.tsx b/sources/@roots/bud-dashboard/src/application.tsx
index ed6902a1a1..abec1d4f41 100644
--- a/sources/@roots/bud-dashboard/src/application.tsx
+++ b/sources/@roots/bud-dashboard/src/application.tsx
@@ -10,10 +10,12 @@ import Server from '@roots/bud-dashboard/views/server'
import {
Box,
type PropsWithChildren,
+ Static,
Text,
useApp,
useInput,
useState,
+ useStdout,
} from '@roots/bud-support/ink'
export interface Props {
@@ -57,6 +59,8 @@ export const Application = ({
publicProxyUrl,
status,
}: Props) => {
+ const {stdout} = useStdout()
+
if (error) return
compilations = Array.isArray(compilations)
@@ -72,37 +76,43 @@ export const Application = ({
}
return (
-
- {compilations?.map((compilation, id) => {
- if (isolated > 0 && id + 1 !== isolated) return null
-
- return (
-
-
+ {(item, id) => (
+
+
+ {compilations?.map((compilation, id) => {
+ if (isolated > 0 && id + 1 !== isolated) return null
+
+ return (
+
+
+
+
+ )
+ })}
+
+
-
- )
- })}
-
-
-
+
+ )}
+
)
}
diff --git a/sources/@roots/bud-dashboard/test/app.test.tsx b/sources/@roots/bud-dashboard/test/app.test.tsx
index 04b9f7e6bf..38014857e1 100644
--- a/sources/@roots/bud-dashboard/test/app.test.tsx
+++ b/sources/@roots/bud-dashboard/test/app.test.tsx
@@ -147,10 +147,8 @@ describe(`@roots/bud-dashboard app component`, () => {
const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
-
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock\s\[mock\-hash\]\s+\.\/\.\.\/to$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
+ expect(lines[0]).toMatch(/mock\s\[mock\-hash\]\s+\.\/\.\.\/to$/)
})
it(`should render entrypoints`, () => {
@@ -163,26 +161,24 @@ describe(`@roots/bud-dashboard app component`, () => {
)
const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
-
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[mock\-hash\]$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
+ expect(lines[0]).toMatch(/mock \[mock\-hash\]$/)
- expect(lines[2]).toBe(Char.Vertical)
+ expect(lines[1]).toBe(Char.Vertical)
- expect(startsWith(lines[3], Char.Vertical)).toBe(true)
- expect(lines[3]).toMatch(/ foo/)
+ expect(startsWith(lines[2], Char.Vertical)).toBe(true)
+ expect(lines[2]).toMatch(/ foo/)
- expect(isFormattedAsset(lines[4])).toBe(true)
- expect(lines[4]).toMatch(/\sfoo\.js\s/)
+ expect(isFormattedAsset(lines[3])).toBe(true)
+ expect(lines[3]).toMatch(/\sfoo\.js\s/)
- expect(lines[5]).toBe(Char.Vertical)
+ expect(lines[4]).toBe(Char.Vertical)
- expect(startsWith(lines[6], Char.BottomLeft)).toBe(true)
- expect(lines[6]).toMatch(/2 modules \[2\/2 modules cached\]$/)
+ expect(startsWith(lines[5], Char.BottomLeft)).toBe(true)
+ expect(lines[5]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[7]).toBe(Char.Empty)
- expect(lines[8]).toBeUndefined()
+ expect(lines[6]).toBe(Char.Empty)
+ expect(lines[7]).toBeUndefined()
})
it(`should not render entrypoints when entrypoints is empty`, () => {
@@ -194,18 +190,17 @@ describe(`@roots/bud-dashboard app component`, () => {
/>,
)
const lines = stripAnsi(frames2.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[mock\-hash\]$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
+ expect(lines[0]).toMatch(/mock \[mock\-hash\]$/)
+ expect(lines[1]).toBe(Char.Vertical)
expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toBe(Char.Vertical)
- expect(startsWith(lines[4], Char.BottomLeft)).toBe(true)
- expect(lines[4]).toMatch(/2 modules \[2\/2 modules cached\]$/)
+ expect(startsWith(lines[3], Char.BottomLeft)).toBe(true)
+ expect(lines[3]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[5]).toBe(Char.Empty)
+ expect(lines[4]).toBe(Char.Empty)
})
it(`should not render entrypoints when entrypoints are undefined`, () => {
@@ -219,15 +214,15 @@ describe(`@roots/bud-dashboard app component`, () => {
)
const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/ compilation \[foo\]$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
+ expect(lines[0]).toMatch(/ compilation \[foo\]$/)
+
+ expect(lines[1]).toBe(Char.Vertical)
expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toBe(Char.Vertical)
- expect(startsWith(lines[4], Char.BottomLeft)).toBe(true)
- expect(lines[4]).toMatch(/...$/)
+ expect(startsWith(lines[3], Char.BottomLeft)).toBe(true)
+ expect(lines[3]).toMatch(/...$/)
})
it(`should not render entrypoints when entrypoint assets are undefined`, () => {
@@ -247,17 +242,16 @@ describe(`@roots/bud-dashboard app component`, () => {
/>,
)
let lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/ compilation \[foo\]$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
+ expect(lines[0]).toMatch(/ compilation \[foo\]$/)
+ expect(lines[1]).toBe(Char.Vertical)
expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toBe(Char.Vertical)
- expect(startsWith(lines[4], Char.BottomLeft)).toBe(true)
- expect(lines[4]).toMatch(/\.\.\.$/)
- expect(lines[5]).toBe(Char.Empty)
+ expect(startsWith(lines[3], Char.BottomLeft)).toBe(true)
+ expect(lines[3]).toMatch(/\.\.\.$/)
+ expect(lines[4]).toBe(Char.Empty)
const {frames: frames2} = ink.render(
{
/>,
)
- lines = stripAnsi(frames2.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
-
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/compilation \[foo\]$/)
- expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toBe(Char.Vertical)
-
- expect(startsWith(lines[4], Char.BottomLeft)).toBe(true)
- expect(lines[4]).toMatch(/.../)
-
- expect(lines[5]).toBe(Char.Empty)
+ expect(stripAnsi(frames2.pop())).toMatchInlineSnapshot(`
+ "╭ compilation [foo]
+ │
+ │
+ ╰ ...
+ "
+ `)
})
it(`should render compilation count for multi-compiler`, () => {
@@ -298,30 +287,23 @@ describe(`@roots/bud-dashboard app component`, () => {
/>,
)
- const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[1\/2\] \[mock\-hash\]$/)
-
- expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toMatch(/│ foo/)
- expect(lines[4]).toMatch(/│ › foo.js /)
- expect(lines[5]).toBe(Char.Vertical)
-
- expect(startsWith(lines[6], Char.BottomLeft)).toBe(true)
- expect(lines[6]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[7]).toBe(Char.Empty)
-
- expect(startsWith(lines[8], Char.TopLeft)).toBe(true)
- expect(lines[8]).toMatch(/mock \[2\/2\] \[mock\-hash\]$/)
-
- expect(lines[9]).toBe(Char.Vertical)
- expect(lines[10]).toMatch(/│ foo/)
- expect(lines[11]).toMatch(/│ › foo.js /)
- expect(lines[12]).toBe(Char.Vertical)
- expect(lines[13]).toMatch(/╰ 2 modules \[2\/2 modules cached\]/)
- expect(lines[14]).toBe(Char.Empty)
+ expect(stripAnsi(frames.pop())).toMatchInlineSnapshot(`
+ "╭ mock [1/2] [mock-hash]
+ │
+ │ foo
+ │ › foo.js 1 kB
+ │
+ ╰ 2 modules [2/2 modules cached]
+
+ ╭ mock [2/2] [mock-hash]
+ │
+ │ foo
+ │ › foo.js 1 kB
+ │
+ ╰ 2 modules [2/2 modules cached]
+ "
+ `)
})
it(`should render assets`, () => {
@@ -332,26 +314,16 @@ describe(`@roots/bud-dashboard app component`, () => {
mode="production"
/>,
)
- const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
-
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[mock\-hash\]$/)
-
- expect(lines[2]).toBe(Char.Vertical)
-
- expect(startsWith(lines[3], Char.Vertical)).toBe(true)
- expect(lines[3]).toMatch(/│ assets/)
-
- expect(isFormattedAsset(lines[4])).toBe(true)
- expect(lines[4]).toMatch(/foo.png/)
-
- expect(lines[5]).toMatch(/│ … 1 additional asset not shown/)
- expect(lines[6]).toBe(Char.Vertical)
-
- expect(startsWith(lines[7], Char.BottomLeft)).toBe(true)
- expect(lines[7]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[8]).toBe(Char.Empty)
+ expect(stripAnsi(frames.pop())).toMatchInlineSnapshot(`
+ "╭ mock [mock-hash]
+ │
+ │ assets
+ │ › foo.png 1 kB
+ │ … 1 additional asset not shown
+ │
+ ╰ 2 modules [2/2 modules cached]
+ "
+ `)
})
it(`should render error`, () => {
@@ -361,19 +333,14 @@ describe(`@roots/bud-dashboard app component`, () => {
mode="production"
/>,
)
- const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
-
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[mock\-hash\]$/)
-
- expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toMatch(/│ │ Bad error/)
- expect(lines[4]).toBe(Char.Vertical)
-
- expect(startsWith(lines[5], Char.BottomLeft)).toBe(true)
- expect(lines[5]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[6]).toBe(Char.Empty)
+ expect(stripAnsi(frames.pop())).toMatchInlineSnapshot(`
+ "╭ mock [mock-hash]
+ │
+ │ │ Bad error
+ │
+ ╰ 2 modules [2/2 modules cached]
+ "
+ `)
})
it(`should render server info`, () => {
@@ -393,44 +360,42 @@ describe(`@roots/bud-dashboard app component`, () => {
)
const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[0]).toBe(Char.Empty)
+ expect(lines[0]).toMatch(/mock \[mock-hash\]$/)
+ expect(startsWith(lines[0], Char.TopLeft)).toBe(true)
- expect(startsWith(lines[1], Char.TopLeft)).toBe(true)
- expect(lines[1]).toMatch(/mock \[mock\-hash\]$/)
+ expect(lines[1]).toBe(Char.Vertical)
+ expect(lines[2]).toMatch(/│ foo/)
- expect(lines[2]).toBe(Char.Vertical)
- expect(lines[3]).toMatch(/│ foo/)
-
- expect(isFormattedAsset(lines[4])).toBe(true)
- expect(lines[4]).toMatch(/foo.js /)
- expect(lines[4]).toMatch(/1 kB$/)
+ expect(isFormattedAsset(lines[3])).toBe(true)
+ expect(lines[3]).toMatch(/foo.js /)
+ expect(lines[3]).toMatch(/1 kB$/)
- expect(lines[5]).toBe(Char.Vertical)
+ expect(lines[4]).toBe(Char.Vertical)
- expect(startsWith(lines[6], Char.Vertical)).toBe(true)
- expect(lines[6]).toMatch(/assets$/)
+ expect(startsWith(lines[5], Char.Vertical)).toBe(true)
+ expect(lines[5]).toMatch(/assets$/)
- expect(isFormattedAsset(lines[7])).toBe(true)
- expect(lines[7]).toMatch(/ foo.png /)
- expect(lines[7]).toMatch(/1 kB$/)
+ expect(isFormattedAsset(lines[6])).toBe(true)
+ expect(lines[6]).toMatch(/ foo.png /)
+ expect(lines[6]).toMatch(/1 kB$/)
- expect(lines[8]).toMatch(/│ … 1 additional asset not shown/)
- expect(lines[9]).toBe(Char.Vertical)
+ expect(lines[7]).toMatch(/│ … 1 additional asset not shown/)
+ expect(lines[8]).toBe(Char.Vertical)
- expect(startsWith(lines[10], Char.BottomLeft)).toBe(true)
- expect(lines[10]).toMatch(/2 modules \[2\/2 modules cached\]$/)
+ expect(startsWith(lines[9], Char.BottomLeft)).toBe(true)
+ expect(lines[9]).toMatch(/2 modules \[2\/2 modules cached\]$/)
- expect(lines[11]).toBe(Char.Empty)
- expect(lines[12]).toMatch(/Network/)
- expect(lines[14]).toMatch(/ › Proxy ┄ http:\/\/.+:\d+\//)
- expect(lines[15]).toBe(Char.Empty)
- expect(lines[16]).toMatch(/ ┄ http:\/\/.+/)
- expect(lines[17]).toMatch(/ › Dev ┄ http:\/\/.+\//)
- expect(lines[18]).toMatch(
+ expect(lines[10]).toBe(Char.Empty)
+ expect(lines[11]).toMatch(/Network/)
+ expect(lines[13]).toMatch(/ › Proxy ┄ http:\/\/.+:\d+\//)
+ expect(lines[14]).toBe(Char.Empty)
+ expect(lines[15]).toMatch(/ ┄ http:\/\/.+/)
+ expect(lines[16]).toMatch(/ › Dev ┄ http:\/\/.+\//)
+ expect(lines[17]).toMatch(
/ ┄ http:\/\/\d+\.\d+\.\d+\.\d+:\d+\//,
)
- expect(lines[19]).toMatch(/ ┄ http:\/\/.+:\d+\//)
- expect(lines[20]).toBe(Char.Empty)
+ expect(lines[18]).toMatch(/ ┄ http:\/\/.+:\d+\//)
+ expect(lines[19]).toBe(Char.Empty)
})
it(`should not render proxy info when proxy not set`, () => {
@@ -450,7 +415,7 @@ describe(`@roots/bud-dashboard app component`, () => {
/>,
)
const lines = stripAnsi(frames.pop()).split(Char.NewLine)
- expect(lines[14]).toMatch(/ › Dev ┄ http:\/\/.+:\d+\//)
+ expect(lines[13]).toMatch(/ › Dev ┄ http:\/\/.+:\d+\//)
})
it(`should not throw when crazy input happens`, () => {