Skip to content

Commit 967597c

Browse files
wardpeetsidharthachatterjee
authored andcommitted
feat(gatsby-cli): move progressbar into ink (#14220)
1 parent b225b92 commit 967597c

File tree

17 files changed

+429
-126
lines changed

17 files changed

+429
-126
lines changed

packages/gatsby-cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"object.entries": "^1.1.0",
3434
"opentracing": "^0.14.3",
3535
"pretty-error": "^2.1.1",
36+
"progress": "^2.0.3",
3637
"prompts": "^2.1.0",
3738
"react": "^16.8.4",
3839
"resolve-cwd": "^2.0.0",

packages/gatsby-cli/src/reporter/index.js

+78-3
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,88 @@ const reporter: Reporter = {
9393
const spanArgs = parentSpan ? { childOf: parentSpan } : {}
9494
const span = tracer.startSpan(name, spanArgs)
9595

96-
const activity = reporterInstance.createActivity(name)
96+
const activity = reporterInstance.createActivity({
97+
type: `spinner`,
98+
id: name,
99+
status: ``,
100+
})
97101

98102
return {
99-
...activity,
103+
start() {
104+
activity.update({
105+
startTime: process.hrtime(),
106+
})
107+
},
108+
setStatus(status) {
109+
activity.update({
110+
status: status,
111+
})
112+
},
100113
end() {
101114
span.finish()
102-
activity.end()
115+
activity.done()
116+
},
117+
span,
118+
}
119+
},
120+
121+
/**
122+
* Create a progress bar for an activity
123+
* @param {string} name - Name of activity.
124+
* @param {number} total - Total items to be processed.
125+
* @param {number} start - Start count to show.
126+
* @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
127+
* @returns {ActivityTracker} The activity tracker.
128+
*/
129+
createProgress(
130+
name: string,
131+
total,
132+
start = 0,
133+
activityArgs: ActivityArgs = {}
134+
): ActivityTracker {
135+
const { parentSpan } = activityArgs
136+
const spanArgs = parentSpan ? { childOf: parentSpan } : {}
137+
const span = tracer.startSpan(name, spanArgs)
138+
139+
let hasStarted = false
140+
let current = start
141+
const activity = reporterInstance.createActivity({
142+
type: `progress`,
143+
id: name,
144+
current,
145+
total,
146+
})
147+
148+
return {
149+
start() {
150+
if (hasStarted) {
151+
return
152+
}
153+
154+
hasStarted = true
155+
activity.update({
156+
startTime: process.hrtime(),
157+
})
158+
},
159+
setStatus(status) {
160+
activity.update({
161+
status: status,
162+
})
163+
},
164+
tick() {
165+
activity.update({
166+
current: ++current,
167+
})
168+
},
169+
done() {
170+
span.finish()
171+
activity.done()
172+
},
173+
set total(value) {
174+
total = value
175+
activity.update({
176+
total: value,
177+
})
103178
},
104179
span,
105180
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from "react"
2+
import { Box } from "ink"
3+
import calcElapsedTime from "../../../../util/calc-elapsed-time"
4+
5+
const maxWidth = 30
6+
const minWidth = 10
7+
8+
const getLength = prop => String(prop).length
9+
10+
export default function ProgressBar({ message, current, total, startTime }) {
11+
const percentage = total ? Math.round((current / total) * 100) : 0
12+
const terminalWidth = process.stdout.columns || 80
13+
const availableWidth =
14+
terminalWidth -
15+
getLength(message) -
16+
getLength(current) -
17+
getLength(total) -
18+
getLength(percentage) -
19+
11 // margins + extra characters
20+
21+
const progressBarWidth = Math.max(
22+
minWidth,
23+
Math.min(maxWidth, availableWidth)
24+
)
25+
26+
return (
27+
<Box flexDirection="row">
28+
<Box marginRight={3} width={progressBarWidth}>
29+
[
30+
<Box width={progressBarWidth - 2}>
31+
{`=`.repeat(((progressBarWidth - 2) * percentage) / 100)}
32+
</Box>
33+
]
34+
</Box>
35+
<Box marginRight={1}>{calcElapsedTime(startTime)} s</Box>
36+
<Box marginRight={1}>
37+
{current}/{total}
38+
</Box>
39+
<Box marginRight={1}>{`` + percentage}%</Box>
40+
<Box textWrap="truncate">{message}</Box>
41+
</Box>
42+
)
43+
}

packages/gatsby-cli/src/reporter/reporters/ink/components/activity.js renamed to packages/gatsby-cli/src/reporter/reporters/ink/components/spinner.js

-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import React from "react"
2-
import convertHrtime from "convert-hrtime"
32
import { Box } from "ink"
43
import Spinner from "ink-spinner"
54

6-
export const calcElapsedTime = startTime => {
7-
const elapsed = process.hrtime(startTime)
8-
9-
return convertHrtime(elapsed)[`seconds`].toFixed(3)
10-
}
11-
125
export default function Activity({ name, status }) {
136
let statusText = name
147
if (status) {

packages/gatsby-cli/src/reporter/reporters/ink/reporter.js

+73-46
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
import React from "react"
22
import { Static, Box } from "ink"
3-
import { isCI } from "ci-info"
43
import chalk from "chalk"
5-
import Activity, { calcElapsedTime } from "./components/activity"
4+
import Spinner from "./components/spinner"
5+
import ProgressBar from "./components/progress-bar"
66
import { Message } from "./components/messages"
7+
import isTTY from "../../../util/is-tty"
8+
import calcElapsedTime from "../../../util/calc-elapsed-time"
9+
10+
const showProgress = isTTY
11+
12+
const successTextGenerator = {
13+
spinner: activity => {
14+
let successText = `${activity.id} - ${calcElapsedTime(
15+
activity.startTime
16+
)} s`
17+
if (activity.status) {
18+
successText += ` — ${activity.status}`
19+
}
720

8-
const showProgress = process.stdout.isTTY && !isCI
9-
10-
const generateActivityFinishedText = (name, activity) => {
11-
let successText = `${name} - ${calcElapsedTime(activity.startTime)} s`
12-
if (activity.status) {
13-
successText += ` — ${activity.status}`
14-
}
15-
16-
return successText
21+
return successText
22+
},
23+
progress: activity =>
24+
`${activity.id}${activity.current}/${activity.total} - ${calcElapsedTime(
25+
activity.startTime
26+
)} s`,
1727
}
1828

1929
export default class GatsbyReporter extends React.Component {
@@ -26,44 +36,40 @@ export default class GatsbyReporter extends React.Component {
2636

2737
format = chalk
2838

29-
createActivity = name => {
39+
createActivity = ({ id, ...options }) => {
40+
this.setState(state => {
41+
return {
42+
activities: {
43+
...state.activities,
44+
[id]: {
45+
id,
46+
...options,
47+
},
48+
},
49+
}
50+
})
51+
3052
return {
31-
start: () => {
32-
this.setState(state => {
33-
return {
34-
activities: {
35-
...state.activities,
36-
[name]: {
37-
status: ``,
38-
startTime: process.hrtime(),
39-
},
40-
},
41-
}
42-
})
43-
},
44-
setStatus: status => {
53+
update: newState => {
4554
this.setState(state => {
46-
const activity = state.activities[name]
47-
4855
return {
4956
activities: {
5057
...state.activities,
51-
[name]: {
52-
...activity,
53-
status: status,
58+
[id]: {
59+
...state.activities[id],
60+
...newState,
5461
},
5562
},
5663
}
5764
})
5865
},
59-
end: () => {
60-
const activity = this.state.activities[name]
61-
62-
this.success(generateActivityFinishedText(name, activity))
66+
done: () => {
67+
const activity = this.state.activities[id]
68+
this.success(successTextGenerator[activity.type]({ id, ...activity }))
6369

6470
this.setState(state => {
6571
const activities = { ...state.activities }
66-
delete activities[name]
72+
delete activities[id]
6773

6874
return {
6975
activities,
@@ -116,27 +122,48 @@ export default class GatsbyReporter extends React.Component {
116122
}
117123

118124
render() {
125+
const { activities, messages, disableColors } = this.state
126+
127+
const spinners = []
128+
const progressBars = []
129+
if (showProgress) {
130+
Object.keys(activities).forEach(activityName => {
131+
const activity = activities[activityName]
132+
if (activity.type === `spinner`) {
133+
spinners.push(activity)
134+
}
135+
if (activity.type === `progress` && activity.startTime) {
136+
progressBars.push(activity)
137+
}
138+
})
139+
}
140+
119141
return (
120142
<Box flexDirection="column">
121143
<Box flexDirection="column">
122144
<Static>
123-
{this.state.messages.map((msg, index) => (
145+
{messages.map((msg, index) => (
124146
<Box textWrap="wrap" key={index}>
125-
<Message type={msg.type} hideColors={this.state.disableColors}>
147+
<Message type={msg.type} hideColors={disableColors}>
126148
{msg.text}
127149
</Message>
128150
</Box>
129151
))}
130152
</Static>
131153

132-
{showProgress &&
133-
Object.keys(this.state.activities).map(activityName => (
134-
<Activity
135-
key={activityName}
136-
name={activityName}
137-
{...this.state.activities[activityName]}
138-
/>
139-
))}
154+
{spinners.map(activity => (
155+
<Spinner key={activity.id} name={activity.id} {...activity} />
156+
))}
157+
158+
{progressBars.map(activity => (
159+
<ProgressBar
160+
key={activity.id}
161+
message={activity.id}
162+
total={activity.total}
163+
current={activity.current}
164+
startTime={activity.startTime}
165+
/>
166+
))}
140167
</Box>
141168
</Box>
142169
)

0 commit comments

Comments
 (0)