-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Expand file tree
/
Copy pathtransform.js
More file actions
164 lines (139 loc) · 4.82 KB
/
Copy pathtransform.js
File metadata and controls
164 lines (139 loc) · 4.82 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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
const parseFm = require('front-matter')
const {Minipass} = require('minipass')
const yaml = require('yaml')
const {minimatch} = require('minimatch')
const {sep, join, posix, isAbsolute} = require('path')
const gh = require('./gh')
const prettier = require('@prettier/sync')
const rawRedirects = require('./redirects')
const prettierFormat = str =>
prettier.format(str, {
parser: 'markdown',
proseWrap: 'never',
})
const getPathParts = path => {
const abs = isAbsolute(path)
const paths = path.replace(/\.mdx?$/, '').split(sep)
const pathId = posix.join(abs ? '/' : '', ...paths)
const page = posix.basename(pathId)
const section = posix.dirname(pathId)
return {
path: pathId,
page,
section: section === '.' ? '' : section,
}
}
const getRedirects = ({path, release}) => {
const redirects = [getPathParts(path)]
const [pagePath] = redirects
const canonical = posix.join(release.url, pagePath.section, pagePath.page === 'index' ? '' : pagePath.page)
for (const [k, v] of Object.entries(rawRedirects).reverse()) {
const pageRedirects = redirects.flatMap(redirect => {
if (minimatch(redirect.path, k)) {
return v({...getPathParts(redirect.path), release}).map(p => ({
...redirect,
...(typeof p === 'object' ? p : {path: p}),
}))
}
return []
})
redirects.push(...pageRedirects.filter(Boolean))
}
const prefixRedirects = redirects
.flatMap(redirect => {
const useDefault = redirect.default || release.default
if (isAbsolute(redirect.path)) {
return useDefault ? redirect.path : null
} else {
return release.urlPrefixes.flatMap(p => [
posix.join('/', p, release.id, redirect.path),
useDefault ? posix.join('/', p, redirect.path) : null,
])
}
})
.filter(Boolean)
.filter(r => r !== canonical)
return [...new Set(prefixRedirects)].sort((a, b) => a.localeCompare(b, 'en'))
}
const transform = (data, {release, path, frontmatter, format = s => s}) => {
let {attributes, body} = parseFm(data.toString())
/* istanbul ignore next */
if (!attributes.redirect_from) {
attributes.redirect_from = []
}
attributes.redirect_from.push(...getRedirects({path, release}))
const ghFrontmatter = {
github_repo: gh.nwo,
github_branch: release.branch,
github_path: join(release.src, path).split(sep).join(posix.sep),
}
const order = [
'title',
'shortName',
'section',
'description',
'github_repo',
'github_branch',
'github_path',
'redirect_from',
]
const sortRedirects = (a, b) => a.localeCompare(b, 'en')
attributes = Object.fromEntries(
Object.entries({...attributes, ...ghFrontmatter, ...frontmatter})
.filter(([, v]) => (Array.isArray(v) ? v.length : true))
.map(([k, v]) => (Array.isArray(v) ? [k, v.sort(sortRedirects)] : [k, v]))
.sort(([a], [b]) => {
/* istanbul ignore next */
const aIndex = order.includes(a) ? order.indexOf(a) : order.length
/* istanbul ignore next */
const bIndex = order.includes(b) ? order.indexOf(b) : order.length
return aIndex - bIndex
}),
)
// first format with prettier, this helps so other replacements don't have to
// worry about newlines vs spaces
body = prettierFormat(body)
// then do replacements for all cli makdown files
body = body
// some legacy versions of the docs did not get this replaced
// in the source so we need to replace it here
.replace(/@VERSION@/g, release.version)
// also replace all internal markdown links with links to this
// specific version
.replace(
/\[([^\]]+)\]\(\/((?:commands|configuring-npm|using-npm)\/[^)]+)\)/g,
(_, p1, p2) => `[${p1}](${release.url}/${p2})`,
)
// remove html comments which are not mdx compatible
.replace(/^<!--\s.*?\s-->$\n/gm, '')
// replace markdown autolinks with full markdown links, also for mdx
.replace(/<(http)(.*?)\\?>/g, '[$1$2]($1$2)')
// then do any transformer specific replacements
body = format(body)
// then do any transformer specific replacements
body = format(body)
// prettier on the final assembled output so the committed file passes prettier --check
return prettierFormat(`---\n${yaml.stringify(attributes).trim()}\n---\n\n${body}`)
}
// copied from minipass-collect. collects all chunks into a single
// buffer which is then transformed before a final write event
class Transform extends Minipass {
#data = []
#length = 0
#opts = {}
static sync = transform
constructor(transformOpts) {
super({encoding: 'utf-8'})
this.#opts = transformOpts
}
write(c) {
this.#data.push(c)
this.#length += c.length
return true
}
end() {
super.write(transform(Buffer.concat(this.#data, this.#length), this.#opts))
return super.end()
}
}
module.exports = Transform