Skip to content

Commit e514f9a

Browse files
authored
feat(gatsby-cli): enable gatsby pages output (#33188)
1 parent e2263e8 commit e514f9a

File tree

8 files changed

+383
-2
lines changed

8 files changed

+383
-2
lines changed

packages/gatsby-cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@babel/runtime": "^7.15.4",
1515
"@types/common-tags": "^1.8.0",
1616
"better-opn": "^2.0.0",
17+
"boxen": "^5.1.1",
1718
"chalk": "^4.1.2",
1819
"clipboardy": "^2.3.0",
1920
"common-tags": "^1.8.0",

packages/gatsby-cli/src/reporter/loggers/ink/cli.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ProgressBar } from "./components/progress-bar"
77
import { Message, IMessageProps } from "./components/messages"
88
import { Error as ErrorComponent } from "./components/error"
99
import Develop from "./components/develop"
10+
import PageTree from "./components/pageTree"
1011
import { IGatsbyCLIState, IActivity } from "../../redux/types"
1112
import { ActivityLogLevels } from "../../constants"
1213
import { IStructuredError } from "../../../structured-errors/types"
@@ -16,6 +17,7 @@ const showProgress = isTTY()
1617
interface ICLIProps {
1718
logs: IGatsbyCLIState
1819
showStatusBar: boolean
20+
showPageTree: boolean
1921
}
2022

2123
interface ICLIState {
@@ -48,6 +50,7 @@ class CLI extends React.Component<ICLIProps, ICLIState> {
4850
const {
4951
logs: { messages, activities },
5052
showStatusBar,
53+
showPageTree,
5154
} = this.props
5255

5356
const { hasError, error } = this.state
@@ -99,6 +102,7 @@ class CLI extends React.Component<ICLIProps, ICLIState> {
99102
)
100103
}
101104
</Static>
105+
{showPageTree && <PageTree />}
102106

103107
{spinners.map(activity => (
104108
<Spinner key={activity.id} {...activity} />
@@ -114,6 +118,7 @@ class CLI extends React.Component<ICLIProps, ICLIState> {
114118
/>
115119
))}
116120
</Box>
121+
117122
{showStatusBar && <Develop />}
118123
</Box>
119124
)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import React, { ReactElement, useContext } from "react"
2+
import { Box, Text, BoxProps, Spacer } from "ink"
3+
import path from "path"
4+
import StoreStateContext from "../context"
5+
import {
6+
generatePageTree,
7+
IPageTreeLine,
8+
IComponentWithPageModes,
9+
} from "../../../../util/generate-page-tree"
10+
11+
interface IPageTreeProps {
12+
components: Map<string, IComponentWithPageModes>
13+
root: string
14+
}
15+
16+
const Description: React.FC<BoxProps> = function Description(props) {
17+
return (
18+
<Box>
19+
<Box
20+
{...props}
21+
flexDirection="column"
22+
borderStyle="round"
23+
padding={1}
24+
marginLeft={2}
25+
marginRight={2}
26+
>
27+
<Box paddingLeft={2}>
28+
<Text>(SSG) Generated at build time</Text>
29+
</Box>
30+
<Text>
31+
D (DSG) Defered static generation - page generated at runtime
32+
</Text>
33+
<Text>∞ (SSR) Server-side renders at runtime (uses getServerDate)</Text>
34+
<Text>λ (Function) Gatsby function</Text>
35+
</Box>
36+
<Spacer />
37+
</Box>
38+
)
39+
}
40+
41+
const ComponentTree: React.FC<{
42+
file: string
43+
isFirst: boolean
44+
isLast: boolean
45+
pages: IComponentWithPageModes
46+
}> = function ComponentTree({ file, isFirst, isLast, pages }) {
47+
let topLevelIcon = `├`
48+
if (isFirst) {
49+
topLevelIcon = `┌`
50+
}
51+
if (isLast) {
52+
topLevelIcon = `└`
53+
}
54+
55+
const sortedPages: Array<IPageTreeLine> = generatePageTree(pages)
56+
57+
return (
58+
<Box flexDirection="column">
59+
<Box>
60+
<Box paddingRight={1}>
61+
<Text>{topLevelIcon}</Text>
62+
</Box>
63+
<Text wrap="truncate-middle">{file}</Text>
64+
</Box>
65+
{sortedPages.map((page, index) => (
66+
<Box key={page.text}>
67+
<Text>{isLast ? ` ` : `│`}</Text>
68+
<Box paddingLeft={1} paddingRight={1}>
69+
<Text>{index === sortedPages.length - 1 ? `└` : `├`}</Text>
70+
</Box>
71+
<Box>
72+
<Text>
73+
{page.symbol} {page.text}
74+
</Text>
75+
</Box>
76+
</Box>
77+
))}
78+
</Box>
79+
)
80+
}
81+
82+
const PageTree: React.FC<IPageTreeProps> = function PageTree({
83+
components,
84+
root,
85+
}) {
86+
const componentList: Array<ReactElement> = []
87+
let i = 0
88+
for (const [componentPath, pages] of components) {
89+
componentList.push(
90+
<ComponentTree
91+
isFirst={i === 0}
92+
isLast={i === components.size - 1}
93+
key={componentPath}
94+
file={path.posix.relative(root, componentPath)}
95+
pages={pages}
96+
/>
97+
)
98+
99+
i++
100+
}
101+
102+
return (
103+
<Box flexDirection="column" marginTop={2}>
104+
<Box paddingBottom={1}>
105+
<Text underline>Pages</Text>
106+
</Box>
107+
{componentList}
108+
<Description marginTop={1} marginBottom={1} />
109+
</Box>
110+
)
111+
}
112+
113+
const ConnectedPageTree: React.FC = function ConnectedPageTree() {
114+
const state = useContext(StoreStateContext)
115+
116+
const componentWithPages = new Map<string, IComponentWithPageModes>()
117+
for (const { componentPath, pages } of state.components.values()) {
118+
const pagesByMode = {
119+
SSG: new Set<string>(),
120+
DSG: new Set<string>(),
121+
SSR: new Set<string>(),
122+
FN: new Set<string>(),
123+
}
124+
pages.forEach(pagePath => {
125+
const gatsbyPage = state.pages.get(pagePath)
126+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
127+
pagesByMode[gatsbyPage!.mode].add(pagePath)
128+
})
129+
130+
componentWithPages.set(componentPath, pagesByMode)
131+
}
132+
133+
for (const {
134+
originalAbsoluteFilePath,
135+
functionRoute,
136+
} of state.functions.values()) {
137+
componentWithPages.set(originalAbsoluteFilePath, {
138+
SSG: new Set<string>(),
139+
DSG: new Set<string>(),
140+
SSR: new Set<string>(),
141+
FN: new Set<string>([`/api/${functionRoute}`]),
142+
})
143+
}
144+
145+
return (
146+
<PageTree components={componentWithPages} root={state.program.directory} />
147+
)
148+
}
149+
150+
export default ConnectedPageTree

packages/gatsby-cli/src/reporter/loggers/ink/context.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState, useLayoutEffect, createContext } from "react"
22
import { getStore, onLogAction } from "../../redux"
3+
// TODO remove and copy types
34
import { IGatsbyState } from "gatsby/src/redux/types"
45

56
// These weird castings we are doing in this file is because the way gatsby-cli works is that it starts with it's own store

packages/gatsby-cli/src/reporter/loggers/ink/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@ const ConnectedCLI: React.FC = (): React.ReactElement => {
88
const showStatusBar =
99
state.program?._?.[0] === `develop` &&
1010
state.program?.status === `BOOTSTRAP_FINISHED`
11+
const showPageTree =
12+
state.program?._?.[0] === `build` &&
13+
state.logs.messages.find(message => message?.text?.includes(`onPostBuild`))
1114

12-
return <CLI showStatusBar={Boolean(showStatusBar)} logs={state.logs} />
15+
return (
16+
<CLI
17+
showStatusBar={Boolean(showStatusBar)}
18+
showPageTree={Boolean(showPageTree)}
19+
logs={state.logs}
20+
/>
21+
)
1322
}
1423

1524
export function initializeINKLogger(): void {

packages/gatsby-cli/src/reporter/loggers/yurnalist/index.ts

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { onLogAction } from "../../redux"
1+
import path from "path"
2+
import { getStore, onLogAction } from "../../redux"
23
import {
34
Actions,
45
LogLevels,
@@ -9,7 +10,14 @@ import {
910
import { createReporter } from "yurnalist"
1011
import ProgressBar from "progress"
1112
import chalk from "chalk"
13+
import boxen from "boxen"
1214
import { IUpdateActivity } from "../../redux/types"
15+
import {
16+
generatePageTree,
17+
IComponentWithPageModes,
18+
} from "../../../util/generate-page-tree"
19+
// TODO remove and copy types
20+
import { IGatsbyState } from "gatsby/src/redux/types"
1321

1422
interface IYurnalistActivities {
1523
[activityId: string]: {
@@ -20,6 +28,100 @@ interface IYurnalistActivities {
2028
}
2129
}
2230

31+
function generatePageTreeToConsole(yurnalist: any): void {
32+
const state = getStore().getState() as IGatsbyState
33+
34+
// TODO use program
35+
const root = state.program.directory
36+
const componentWithPages = new Map<string, IComponentWithPageModes>()
37+
for (const { componentPath, pages } of state.components.values()) {
38+
const pagesByMode = {
39+
SSG: new Set<string>(),
40+
DSG: new Set<string>(),
41+
SSR: new Set<string>(),
42+
FN: new Set<string>(),
43+
}
44+
pages.forEach(pagePath => {
45+
const gatsbyPage = state.pages.get(pagePath)
46+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
47+
pagesByMode[gatsbyPage!.mode].add(pagePath)
48+
})
49+
50+
componentWithPages.set(
51+
path.posix.relative(root, componentPath),
52+
pagesByMode
53+
)
54+
}
55+
56+
for (const {
57+
originalAbsoluteFilePath,
58+
functionRoute,
59+
} of state.functions.values()) {
60+
componentWithPages.set(
61+
path.posix.relative(root, originalAbsoluteFilePath),
62+
{
63+
SSG: new Set<string>(),
64+
DSG: new Set<string>(),
65+
SSR: new Set<string>(),
66+
FN: new Set<string>([`/api/${functionRoute}`]),
67+
}
68+
)
69+
}
70+
71+
yurnalist.log(`\n${chalk.underline(`Pages`)}\n`)
72+
73+
let i = 0
74+
for (const [componentPath, pages] of componentWithPages) {
75+
const isLast = i === componentWithPages.size - 1
76+
let topLevelIcon = `├`
77+
if (i === 0) {
78+
topLevelIcon = `┌`
79+
}
80+
if (isLast) {
81+
topLevelIcon = `└`
82+
}
83+
const componentTree = [`${topLevelIcon} ${componentPath}`]
84+
85+
const sortedPages = generatePageTree(pages)
86+
sortedPages.map((page, index) => {
87+
componentTree.push(
88+
[
89+
isLast ? ` ` : `│`,
90+
` ${index === sortedPages.length - 1 ? `└` : `├`} `,
91+
`${page.symbol} ${page.text}`,
92+
].join(``)
93+
)
94+
})
95+
96+
yurnalist.log(componentTree.join(`\n`))
97+
98+
i++
99+
}
100+
101+
yurnalist.log(``)
102+
yurnalist.log(
103+
boxen(
104+
[
105+
` (SSG) Generated at build time`,
106+
`D (DSG) Defered static generation - page generated at runtime`,
107+
`∞ (SSR) Server-side renders at runtime (uses getServerDate)`,
108+
`λ (Function) Gatsby function`,
109+
].join(`\n`),
110+
{
111+
padding: 1,
112+
margin: {
113+
left: 2,
114+
right: 2,
115+
top: 0,
116+
bottom: 0,
117+
},
118+
// @ts-ignore - bad type in boxen
119+
borderStyle: `round`,
120+
}
121+
)
122+
)
123+
}
124+
23125
export function initializeYurnalistLogger(): void {
24126
const activities: IYurnalistActivities = {}
25127
const yurnalist = createReporter({ emoji: true, verbose: true })
@@ -56,6 +158,11 @@ export function initializeYurnalistLogger(): void {
56158
}
57159
yurnalistMethod(message)
58160
}
161+
162+
if (action.payload.text?.includes(`onPostBuild`)) {
163+
generatePageTreeToConsole(yurnalist)
164+
}
165+
59166
break
60167
}
61168
case Actions.StartActivity: {

0 commit comments

Comments
 (0)