-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsummary.ts
More file actions
127 lines (107 loc) · 3.53 KB
/
summary.ts
File metadata and controls
127 lines (107 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import type { Endpoint, Namespace, Route } from '@seamapi/blueprint'
import type { PathMetadata } from '../path-metadata.js'
export interface ApiSummaryLayoutContext {
nodes: Node[]
}
interface Node {
title: string
path: string
nodes: Node[]
}
interface Context {
routes: Route[]
namespaces: Namespace[]
endpoints: Endpoint[]
pathMetadata: PathMetadata
}
// Access Grants are the default and recommended way to grant access, so the
// granting resources are pinned to the top of the API reference nav. The
// low-level legacy granting resources sink to the bottom. Everything else
// stays alphabetical in between.
const firstResourcePaths = [
'/access_grants',
'/access_methods',
'/spaces',
'/user_identities',
'/instant_keys',
]
const lastResourcePaths = ['/access_codes', '/acs']
const topLevelRank = (path: string): number => {
const firstIdx = firstResourcePaths.indexOf(path)
if (firstIdx !== -1) return firstIdx
const lastIdx = lastResourcePaths.indexOf(path)
if (lastIdx !== -1) return firstResourcePaths.length + 1 + lastIdx
return firstResourcePaths.length
}
export function setSummaryLayoutContext(
file: Partial<ApiSummaryLayoutContext>,
context: Context,
): void {
const paths = getPaths(null, context)
const nodes = getNodes(paths, context)
file.nodes = nodes.sort(
(n1, n2) =>
topLevelRank(`/${n1.path.replace(/\/README\.md$/, '')}`) -
topLevelRank(`/${n2.path.replace(/\/README\.md$/, '')}`),
)
}
const getNodes = (paths: string[], context: Context): Node[] => {
const nodes = paths.map((path) => getNode(path, context))
return nodes.sort((n1, n2) => {
const res = n1.title.localeCompare(n2.title)
if (n1.nodes.length > 0 && n2.nodes.length === 0) return 1
if (n1.nodes.length === 0 && n2.nodes.length > 0) return -1
return res
})
}
const getNode = (path: string, context: Context): Node => {
const { endpoints, routes, namespaces, pathMetadata } = context
const endpoint = endpoints.find((e) => e.path === path)
const route = routes.find((e) => e.path === path)
const namespace = namespaces.find((e) => e.path === path)
if (endpoint != null) {
return {
title: endpoint.title,
path: `${endpoint.path.slice(1)}.md`,
nodes: [],
}
}
if (route != null) {
const routeMetadata = pathMetadata[route.path]
if (routeMetadata == null) {
throw new Error(`Route metadata for ${route.path} not found`)
}
const paths = getPaths(route.path, context)
return {
title: routeMetadata.title,
path: `${route.path.slice(1)}/README.md`,
nodes: getNodes(paths, context),
}
}
if (namespace != null) {
const namespaceMetadata = pathMetadata[namespace.path]
if (namespaceMetadata == null) {
throw new Error(`Namespace metadata for ${namespace.path} not found`)
}
const paths = getPaths(namespace.path, context)
return {
title: namespaceMetadata.title,
path: `${namespace.path.slice(1)}/README.md`,
nodes: getNodes(paths, context),
}
}
throw new Error(`Could not construct node for ${path}`)
}
const getPaths = (path: string | null, context: Context): string[] => {
const getSubpaths = (
items: Array<{ path: string; parentPath: string | null }>,
): string[] => {
return items
.filter(({ parentPath }) => parentPath === path)
.map(({ path }) => path)
}
const routes = getSubpaths(context.routes)
const endpoints = getSubpaths(context.endpoints)
const namespaces = getSubpaths(context.namespaces)
return [...namespaces, ...endpoints, ...routes]
}