Lint and format all code with same configs
Luke Karrys committed
Oct 20, 2023 at 15:11 UTC
8b3b27d769fc1491b6de7934e71328f7ea637503
20 files changed
+275
-315
.eslintrc.js
+3
-1
@@ -1,7 +1,8 @@
1
module.exports = {
2
root: true,
3
- ignorePatterns: ['cli/', '.cache/', 'public/'],
3
+ ignorePatterns: ['.cache/', 'public/'],
4
extends: [
5
+ '@npmcli',
6
'react-app',
7
// 'react-app/jest',
8
'eslint:recommended',
@@ -13,6 +14,7 @@ module.exports = {
14
'prettier',
15
],
16
rules: {
17
+ 'max-len': 'off',
18
'react/prop-types': 'off',
19
'primer-react/no-system-props': ['error', {includeUtilityComponents: true}],
20
},
.prettierignore
+7
-6
@@ -1,8 +1,9 @@
1
**/template-oss/*.json
2
**/template-oss/*.yml
3
-content/
4
-cli/
5
-.github/
6
-package-lock.json
7
-SECURITY.md
8
-.reuse/
3
+/content/
4
+/.github/
5
+/package-lock.json
6
+/SECURITY.md
7
+/.reuse/
8
+.nyc_output/
9
+coverage/
cli/.eslintrc.js
deleted
-17
@@ -1,17 +0,0 @@
1
-/* This file is automatically added by @npmcli/template-oss. Do not edit. */
2
-
3
-'use strict'
4
-
5
-const { readdirSync: readdir } = require('fs')
6
-
7
-const localConfigs = readdir(__dirname)
8
- .filter((file) => file.startsWith('.eslintrc.local.'))
9
- .map((file) => `./${file}`)
10
-
11
-module.exports = {
12
- root: true,
13
- extends: [
14
- '@npmcli',
15
- ...localConfigs,
16
- ],
17
-}
cli/bin/build.js
+5
-5
@@ -1,7 +1,7 @@
1
-const { resolve, relative, join } = require('path')
2
-const { spawnSync } = require('child_process')
1
+const {resolve, relative, join} = require('path')
2
+const {spawnSync} = require('child_process')
3
const build = require('../lib/build.js')
4
-const { nwo } = require('../lib/gh')
4
+const {nwo} = require('../lib/gh')
5
6
// check only build with the current versions instead of checking the registry
7
// and also fails if any changes are detected. this is used in CI to make sure
@@ -13,7 +13,7 @@ const contentPath = join(ROOT, 'content/cli')
13
const navPath = join(ROOT, 'content/nav.yml')
14
15
const checkContent = () => {
16
- const status = spawnSync('git', ['status', '--porcelain', contentPath], { encoding: 'utf-8' })
16
+ const status = spawnSync('git', ['status', '--porcelain', contentPath], {encoding: 'utf-8'})
17
if (status.stdout) {
18
const msg = [
19
`The following untracked changes to ${relative(process.cwd(), contentPath)} were found:`,
@@ -38,7 +38,7 @@ build({
38
}
39
return console.log('DONE')
40
})
41
- .catch((e) => {
41
+ .catch(e => {
42
console.error(e)
43
process.exit(1)
44
})
cli/lib/build.js
+48
-48
@@ -1,4 +1,4 @@
1
-const { posix } = require('path')
1
+const {posix} = require('path')
2
const fs = require('fs').promises
3
const yaml = require('yaml')
4
const semver = require('semver')
@@ -8,8 +8,8 @@ const log = require('./log')
8
9
const DOCS_PATH = 'cli'
10
11
-const updateNav = async (updates, { nav, path }) => {
12
- const variants = updates.map((release) => ({
11
+const updateNav = async (updates, {nav, path}) => {
12
+ const variants = updates.map(release => ({
13
title: release.title,
14
shortName: release.id,
15
url: release.url,
@@ -17,8 +17,7 @@ const updateNav = async (updates, { nav, path }) => {
17
children: release.nav,
18
}))
19
20
- const index = nav.contents.items
21
- .findIndex(n => posix.basename(n.get('url')) === DOCS_PATH)
20
+ const index = nav.contents.items.findIndex(n => posix.basename(n.get('url')) === DOCS_PATH)
21
const key = [index, 'variants']
22
const current = nav.getIn(key)
23
@@ -26,7 +25,7 @@ const updateNav = async (updates, { nav, path }) => {
25
nav.setIn(key, nav.createNode(variants))
26
} else {
27
for (const variant of variants) {
29
- const vIndex = current.items.findIndex((n) => n.get('url') === variant.url)
28
+ const vIndex = current.items.findIndex(n => n.get('url') === variant.url)
29
if (vIndex === -1) {
30
nav.addIn(key, nav.createNode(variant))
31
} else {
@@ -38,14 +37,16 @@ const updateNav = async (updates, { nav, path }) => {
37
return fs.writeFile(path, nav.toString(), 'utf-8')
38
}
39
41
-const getCurrentVersions = (nav) => {
40
+const getCurrentVersions = nav => {
41
// the only place the current versions are stored is in the nav
42
const currentSections = nav.find(s => s.url === `/${DOCS_PATH}`).variants
43
45
- const currentVersions = currentSections.map((v) => {
46
- const version = v.title?.match(/^Version\s(.*?)\s/)[1]
47
- return version
48
- }).sort(semver.compare)
44
+ const currentVersions = currentSections
45
+ .map(v => {
46
+ const version = v.title?.match(/^Version\s(.*?)\s/)[1]
47
+ return version
48
+ })
49
+ .sort(semver.compare)
50
51
return {
52
versions: currentVersions,
@@ -53,14 +54,7 @@ const getCurrentVersions = (nav) => {
54
}
55
}
56
56
-const main = async ({
57
- loglevel,
58
- releases: rawReleases,
59
- useCurrent,
60
- navPath,
61
- contentPath,
62
- prerelease,
63
-}) => {
57
+const main = async ({loglevel, releases: rawReleases, useCurrent, navPath, contentPath, prerelease}) => {
58
/* istanbul ignore next */
59
if (loglevel) {
60
log.on(loglevel)
@@ -72,32 +66,40 @@ const main = async ({
66
67
const pack = useCurrent
68
? getCurrentVersions(navData)
75
- : await pacote.packument('npm', { preferOnline: true }).then(p => ({
76
- versions: Object.keys(p.versions),
77
- latest: p['dist-tags'].latest,
78
- }))
79
-
80
- const releaseVersions = rawReleases.map(release => {
81
- const major = Number(release.id.replace(/^v/, ''))
82
- const range = `>=${major}.0.0-a <${major + 1}.0.0` // include all prereleases
83
- const version = semver.parse(semver.maxSatisfying(pack.versions, range))
84
-
85
- return version && {
86
- ...release,
87
- version: version.toString(),
88
- // the default release is always controlled by the latest dist-tag
89
- default: semver.eq(version, pack.latest),
90
- prerelease: version.prerelease.length > 0,
91
- }
92
- }).filter(Boolean)
93
-
94
- const latestRelease = releaseVersions.find(r => r.default) ??
69
+ : await pacote.packument('npm', {preferOnline: true}).then(p => ({
70
+ versions: Object.keys(p.versions),
71
+ latest: p['dist-tags'].latest,
72
+ }))
73
+
74
+ const releaseVersions = rawReleases
75
+ .map(release => {
76
+ const major = Number(release.id.replace(/^v/, ''))
77
+ const range = `>=${major}.0.0-a <${major + 1}.0.0` // include all prereleases
78
+ const version = semver.parse(semver.maxSatisfying(pack.versions, range))
79
+
80
+ return (
81
+ version && {
82
+ ...release,
83
+ version: version.toString(),
84
+ // the default release is always controlled by the latest dist-tag
85
+ default: semver.eq(version, pack.latest),
86
+ prerelease: version.prerelease.length > 0,
87
+ }
88
+ )
89
+ })
90
+ .filter(Boolean)
91
+
92
+ const latestRelease =
93
+ releaseVersions.find(r => r.default) ??
94
releaseVersions.slice(0).sort((a, b) => semver.compare(b.version, a.version))[0]
95
97
- const releases = releaseVersions.map((release) => {
98
- const type = release.default ? 'Latest Release'
99
- : release.prerelease ? 'Prerelease'
100
- : semver.gt(release.version, latestRelease.version) ? 'Current Release'
96
+ const releases = releaseVersions.map(release => {
97
+ const type = release.default
98
+ ? 'Latest Release'
99
+ : release.prerelease
100
+ ? 'Prerelease'
101
+ : semver.gt(release.version, latestRelease.version)
102
+ ? 'Current Release'
103
: 'Legacy Release'
104
105
return {
@@ -110,12 +112,10 @@ const main = async ({
112
})
113
114
const updates = await Promise.all(
113
- releases.map((r) =>
114
- extractRelease(r, { contentPath, baseNav: navData, prerelease })
115
- )
116
- ).then((r) => r.filter(Boolean))
115
+ releases.map(r => extractRelease(r, {contentPath, baseNav: navData, prerelease})),
116
+ ).then(r => r.filter(Boolean))
117
118
- await updateNav(updates, { nav: navDoc, path: navPath })
118
+ await updateNav(updates, {nav: navDoc, path: navPath})
119
}
120
121
module.exports = main
cli/lib/extract.js
+58
-56
@@ -1,6 +1,6 @@
1
const pacote = require('pacote')
2
const tar = require('tar')
3
-const { join, sep, dirname, posix } = require('path')
3
+const {join, sep, dirname, posix} = require('path')
4
const fs = require('fs/promises')
5
const yaml = require('yaml')
6
const Transform = require('./transform')
@@ -17,31 +17,33 @@ const whackAMoleReplace = (s, replacements) => {
17
return s
18
}
19
20
-const unpackTarball = async ({ release, cwd, dir }) => {
20
+const unpackTarball = async ({release, cwd, dir}) => {
21
const strip = 1
22
const result = []
23
const dirParts = dir.split(sep)
24
25
- log.verbose('tarball', release.resolved, { cwd, dir })
25
+ log.verbose('tarball', release.resolved, {cwd, dir})
26
27
- const format = (s) => {
27
+ const format = s => {
28
// a few specific replacements that cause problems for mdx
29
- return whackAMoleReplace(s, [
30
- '{npm-version} node/{node-version} {platform} {arch} workspaces/{workspaces} {ci}',
31
- 'Default: {prefix}/etc/npmrc',
32
- ])
33
- // we cant remove all emails since all except this one are in code blocks
34
- .replace(/(:: )<(i@izs\.me)>/g, '$1[$2](mailto:$2)')
35
- // the v6 version of the funding page has json not inside a code
36
- .replace(/(:\n\n)(\s{4}"funding": {)/g, '$1```json\n$2')
37
- .replace(/^(\s{4}]$)(\n\n)/gm, '$1\n```$2')
29
+ return (
30
+ whackAMoleReplace(s, [
31
+ '{npm-version} node/{node-version} {platform} {arch} workspaces/{workspaces} {ci}',
32
+ 'Default: {prefix}/etc/npmrc',
33
+ ])
34
+ // we cant remove all emails since all except this one are in code blocks
35
+ .replace(/(:: )<(i@izs\.me)>/g, '$1[$2](mailto:$2)')
36
+ // the v6 version of the funding page has json not inside a code
37
+ .replace(/(:\n\n)(\s{4}"funding": {)/g, '$1```json\n$2')
38
+ .replace(/^(\s{4}]$)(\n\n)/gm, '$1\n```$2')
39
+ )
40
}
41
42
const extract = () =>
43
tar.x({
44
cwd,
45
strip: dirParts.length + strip,
44
- transform: ({ path }) => {
46
+ transform: ({path}) => {
47
result.push(path)
48
log.verbose(release.id, path)
49
return new Transform({
@@ -50,28 +52,31 @@ const unpackTarball = async ({ release, cwd, dir }) => {
52
format,
53
})
54
},
53
- filter: (path) => {
55
+ filter: path => {
56
const pathParts = path.split(posix.sep)
57
const prefixParts = pathParts.slice(strip, dirParts.length + strip)
58
return join(...prefixParts) === join(...dirParts)
59
},
60
})
61
60
- await pacote.tarball.stream(`npm@${release.version}`, (stream) =>
61
- new Promise((res, rej) => {
62
- stream.on('end', res)
63
- stream.on('error', rej)
64
- stream.pipe(extract())
65
- }))
62
+ await pacote.tarball.stream(
63
+ `npm@${release.version}`,
64
+ stream =>
65
+ new Promise((res, rej) => {
66
+ stream.on('end', res)
67
+ stream.on('error', rej)
68
+ stream.pipe(extract())
69
+ }),
70
+ )
71
72
return result
73
}
74
70
-const getNav = async ({ path, release }) => {
71
- const nav = await gh.getFile({ ref: release.branch, path })
75
+const getNav = async ({path, release}) => {
76
+ const nav = await gh.getFile({ref: release.branch, path})
77
73
- const rewriteUrls = (nodes) =>
74
- nodes?.map((n) => {
78
+ const rewriteUrls = nodes =>
79
+ nodes?.map(n => {
80
n.url = release.url + n.url
81
n.children = rewriteUrls(n.children)
82
return n
@@ -83,9 +88,9 @@ const getNav = async ({ path, release }) => {
88
}
89
}
90
86
-const writeChangelog = async ({ release, nav, cwd, srcPath, contentPath }) => {
91
+const writeChangelog = async ({release, nav, cwd, srcPath, contentPath}) => {
92
const title = 'Changelog'
88
- const changelog = await gh.getFile({ ref: release.branch, path: srcPath })
93
+ const changelog = await gh.getFile({ref: release.branch, path: srcPath})
94
95
await fs.writeFile(
96
join(cwd, contentPath + '.md'),
@@ -96,23 +101,25 @@ const writeChangelog = async ({ release, nav, cwd, srcPath, contentPath }) => {
101
github_path: srcPath,
102
title,
103
},
99
- format: (s) => {
104
+ format: s => {
105
// some known content in changelogs that is problematic for mdx this is
106
// a bit of whack-a-mole but is necessary since markdown in the CLI is
107
// different from the mdx v2 we parse for the docs site
103
- return whackAMoleReplace(s, [
104
- ' support for node <=16.13 ',
105
- '<->',
106
- ' npm install <folder> ',
107
- ' --replace-registry-host=<npmjs|always|never> ',
108
- 'bundledDependencies -> bundleDependencies ',
109
- ' bump knownBroken to <12.5.0 ',
110
- ])
111
- // remove changelog h1 so it doesnt double render the title
112
- .replace(/^#\s+Changelog\s+$\n/gm, '')
108
+ return (
109
+ whackAMoleReplace(s, [
110
+ ' support for node <=16.13 ',
111
+ '<->',
112
+ ' npm install <folder> ',
113
+ ' --replace-registry-host=<npmjs|always|never> ',
114
+ 'bundledDependencies -> bundleDependencies ',
115
+ ' bump knownBroken to <12.5.0 ',
116
+ ])
117
+ // remove changelog h1 so it doesnt double render the title
118
+ .replace(/^#\s+Changelog\s+$\n/gm, '')
119
+ )
120
},
121
}),
115
- 'utf-8'
122
+ 'utf-8',
123
)
124
125
nav.children[nav.children.length - 1].children.push({
@@ -122,10 +129,7 @@ const writeChangelog = async ({ release, nav, cwd, srcPath, contentPath }) => {
129
})
130
}
131
125
-const unpackRelease = async (
126
- release,
127
- { contentPath, baseNav, prerelease = false }
128
-) => {
132
+const unpackRelease = async (release, {contentPath, baseNav, prerelease = false}) => {
133
if (release.prerelease && !prerelease) {
134
log.info(`Skipping ${release.id} due to prerelease ${release.version}`)
135
return
@@ -139,8 +143,7 @@ const unpackRelease = async (
143
const srcPath = join('docs', 'lib', 'content')
144
145
// this is the src dir for the docs that we link to for the edit links
142
- release.src = await gh.pathExists(release.branch, srcPath)
143
- ?? await gh.pathExists(release.branch, builtPath)
146
+ release.src = (await gh.pathExists(release.branch, srcPath)) ?? (await gh.pathExists(release.branch, builtPath))
147
148
/* istanbul ignore next */
149
if (!release.src) {
@@ -150,13 +153,12 @@ const unpackRelease = async (
153
const nav = await getNav({
154
release,
155
// the nav file can also be in a few different places
153
- path: await gh.pathExists(release.branch, join(srcPath, 'nav.yml'))
154
- ?? await gh.pathExists(release.branch, join('docs', 'nav.yml')),
156
+ path:
157
+ (await gh.pathExists(release.branch, join(srcPath, 'nav.yml'))) ??
158
+ (await gh.pathExists(release.branch, join('docs', 'nav.yml'))),
159
})
160
157
- await fs
158
- .rm(cwd, { force: true, recursive: true })
159
- .then(() => fs.mkdir(cwd, { recursive: true }))
161
+ await fs.rm(cwd, {force: true, recursive: true}).then(() => fs.mkdir(cwd, {recursive: true}))
162
163
const files = await unpackTarball({
164
release,
@@ -164,18 +166,18 @@ const unpackRelease = async (
166
dir: builtPath,
167
})
168
167
- const dirs = ['', ...new Set(files.map((f) => dirname(f)))]
169
+ const dirs = ['', ...new Set(files.map(f => dirname(f)))]
170
171
// The docs in the cli contains all the content pagess and the nav
172
// but no index pages. So this builts empty index pages for each
173
// directory with the correct frontmatter. The context is an mdx
174
// component which will end up showing the nav for this directory.
175
const indexes = await Promise.all(
174
- dirs.map(async (dir) => {
176
+ dirs.map(async dir => {
177
const path = join(dir, 'index.mdx')
178
const navSection = dir
177
- ? nav.children.find((c) => posix.basename(c.url) === dir)
178
- : baseNav.find((c) => posix.basename(c.url) === release.urlPrefix)
179
+ ? nav.children.find(c => posix.basename(c.url) === dir)
180
+ : baseNav.find(c => posix.basename(c.url) === release.urlPrefix)
181
182
await fs.writeFile(
183
join(cwd, path),
@@ -188,11 +190,11 @@ const unpackRelease = async (
190
shortName: navSection.shortName,
191
},
192
}),
191
- 'utf-8'
193
+ 'utf-8',
194
)
195
196
return path
195
- })
197
+ }),
198
)
199
200
await writeChangelog({
cli/lib/gh.js
+13
-13
@@ -1,26 +1,26 @@
1
-const { Octokit } = require('@octokit/rest')
2
-const { posix, sep } = require('path')
1
+const {Octokit} = require('@octokit/rest')
2
+const {posix, sep} = require('path')
3
4
if (!process.env.GITHUB_TOKEN) {
5
throw new Error('GITHUB_TOKEN env var is required to build CLI docs')
6
}
7
8
-const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
8
+const octokit = new Octokit({auth: process.env.GITHUB_TOKEN})
9
const owner = 'npm'
10
const repo = 'cli'
11
-const opts = { owner, repo }
11
+const opts = {owner, repo}
12
13
-const getFile = async ({ sha, ref, path }) => {
14
- const { data } = await (sha
13
+const getFile = async ({sha, ref, path}) => {
14
+ const {data} = await (sha
15
? octokit.git.getBlob({
16
- ...opts,
17
- file_sha: sha,
18
- })
16
+ ...opts,
17
+ file_sha: sha,
18
+ })
19
: octokit.repos.getContent({
20
- ...opts,
21
- ref,
22
- path: path.split(sep).join(posix.sep),
23
- }))
20
+ ...opts,
21
+ ref,
22
+ path: path.split(sep).join(posix.sep),
23
+ }))
24
return Buffer.from(data.content, data.encoding)
25
}
26
cli/lib/log.js
+10
-11
@@ -1,16 +1,15 @@
1
const log = require('proc-log')
2
const SYMBOL = Symbol('doc-log')
3
4
-module.exports = Object.fromEntries(
5
- Object.entries(log).map(([k, v]) => [k, (...a) => v(SYMBOL, ...a)])
6
-)
4
+module.exports = Object.fromEntries(Object.entries(log).map(([k, v]) => [k, (...a) => v(SYMBOL, ...a)]))
5
6
/* istanbul ignore next */
9
-module.exports.on = (loglevel) => process.on('log', (l, s, ...args) => {
10
- // If loglevel is verbose, show everything. Otherwise only show things
11
- // that are not verbose. The script only uses verbose and info, so this
12
- // approach works for now.
13
- if (s === SYMBOL && (loglevel === 'verbose' || l !== 'verbose')) {
14
- console.error(l, ...args)
15
- }
16
-})
7
+module.exports.on = loglevel =>
8
+ process.on('log', (l, s, ...args) => {
9
+ // If loglevel is verbose, show everything. Otherwise only show things
10
+ // that are not verbose. The script only uses verbose and info, so this
11
+ // approach works for now.
12
+ if (s === SYMBOL && (loglevel === 'verbose' || l !== 'verbose')) {
13
+ console.error(l, ...args)
14
+ }
15
+ })
cli/lib/redirects.js
+19
-50
@@ -1,4 +1,6 @@
1
-const { posix: { join } } = require('path')
1
+const {
2
+ posix: {join},
3
+} = require('path')
4
5
// An object to generate redirects for a given path.
6
// The key is a glob path that is matched by minimatch,
@@ -31,64 +33,31 @@ const { posix: { join } } = require('path')
33
34
module.exports = {
35
index: () => ['.'],
34
- '*/!(index)': ({ section, page }) => [
35
- join(section, page),
36
- join('/', section, page),
37
- ],
38
- '*/index': ({ section, page }) => [
39
- join(section),
40
- join('/', section),
41
- join(section, page),
42
- join('/', section, page),
43
- ],
36
+ '*/!(index)': ({section, page}) => [join(section, page), join('/', section, page)],
37
+ '*/index': ({section, page}) => [join(section), join('/', section), join(section, page), join('/', section, page)],
38
39
// any page with `-json` -> `.json`
46
- '**/*-json*': ({ section, page }) => [
47
- join(section, page.replace(/-json/g, '.json')),
48
- ],
40
+ '**/*-json*': ({section, page}) => [join(section, page.replace(/-json/g, '.json'))],
41
42
// commands
51
- 'commands/!(index)': ({ page }) => [
52
- page,
53
- ],
54
- 'commands/index': () => [
55
- '/cli-documentation/cli',
56
- ],
57
- 'commands/*': ({ page }) => [
58
- join('cli-commands', page),
59
- join('/', 'cli-commands', page),
60
- ],
61
- 'commands/npm-*': ({ section, page }) => [
62
- join(section, page.replace(/^npm-/, '')),
63
- ],
43
+ 'commands/!(index)': ({page}) => [page],
44
+ 'commands/index': () => ['/cli-documentation/cli'],
45
+ 'commands/*': ({page}) => [join('cli-commands', page), join('/', 'cli-commands', page)],
46
+ 'commands/npm-*': ({section, page}) => [join(section, page.replace(/^npm-/, ''))],
47
48
// configuring npm
66
- 'configuring-npm/*': ({ page }) => [
67
- join('files', page),
68
- join('/', 'files', page),
69
- ],
70
- 'configuring-npm/package-locks': ({ section, page, release }) => [
49
+ 'configuring-npm/*': ({page}) => [join('files', page), join('/', 'files', page)],
50
+ 'configuring-npm/package-locks': ({section, page, release}) => [
51
// A special case for a path that was deleted in v7, but we still
52
// want the old v6 default url to resolve.
73
- { path: join('/', section, page), default: release.id === 'v6' },
74
- { path: join(section, page), default: release.id === 'v6' },
53
+ {path: join('/', section, page), default: release.id === 'v6'},
54
+ {path: join(section, page), default: release.id === 'v6'},
55
],
56
57
// using npm
78
- 'using-npm/*': ({ page }) => [
79
- join('misc', page),
80
- join('/', 'misc', page),
81
- ],
82
- 'using-npm/removal': ({ section }) => [
83
- join(section, 'removing-npm'),
84
- ],
85
- 'using-npm/scope': ({ section }) => [
86
- join(section, 'npm-scope'),
87
- ],
88
- 'about-pgp-signatures-for-packages-in-the-public-registry': () => [
89
- '/about-registry-signatures',
90
- ],
91
- 'verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry': () => [
92
- '/verifying-registry-signatures',
93
- ],
58
+ 'using-npm/*': ({page}) => [join('misc', page), join('/', 'misc', page)],
59
+ 'using-npm/removal': ({section}) => [join(section, 'removing-npm')],
60
+ 'using-npm/scope': ({section}) => [join(section, 'npm-scope')],
61
+ 'about-pgp-signatures-for-packages-in-the-public-registry': () => ['/about-registry-signatures'],
62
+ 'verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry': () => ['/verifying-registry-signatures'],
63
}
cli/lib/transform.js
+21
-25
@@ -1,13 +1,13 @@
1
const parseFm = require('front-matter')
2
-const { Minipass } = require('minipass')
2
+const {Minipass} = require('minipass')
3
const yaml = require('yaml')
4
-const { minimatch } = require('minimatch')
5
-const { sep, join, posix, isAbsolute } = require('path')
4
+const {minimatch} = require('minimatch')
5
+const {sep, join, posix, isAbsolute} = require('path')
6
const gh = require('./gh')
7
const prettier = require('@prettier/sync')
8
const rawRedirects = require('./redirects')
9
10
-const getPathParts = (path) => {
10
+const getPathParts = path => {
11
const abs = isAbsolute(path)
12
const paths = path.replace(/\.mdx?$/, '').split(sep)
13
@@ -22,23 +22,20 @@ const getPathParts = (path) => {
22
}
23
}
24
25
-const getRedirects = ({ path, release }) => {
25
+const getRedirects = ({path, release}) => {
26
const redirects = [getPathParts(path)]
27
const [pagePath] = redirects
28
- const canonical = posix.join(
29
- release.url,
30
- pagePath.section,
31
- pagePath.page === 'index' ? '' : pagePath.page
32
- )
28
+ const canonical = posix.join(release.url, pagePath.section, pagePath.page === 'index' ? '' : pagePath.page)
29
30
for (const [k, v] of Object.entries(rawRedirects).reverse()) {
31
const pageRedirects = redirects.flatMap(redirect => {
32
if (minimatch(redirect.path, k)) {
37
- return v({ ...getPathParts(redirect.path), release }).map(p => ({
33
+ return v({...getPathParts(redirect.path), release}).map(p => ({
34
...redirect,
39
- ...typeof p === 'object' ? p : { path: p },
35
+ ...(typeof p === 'object' ? p : {path: p}),
36
}))
37
}
38
+ return []
39
})
40
redirects.push(...pageRedirects.filter(Boolean))
41
}
@@ -58,18 +55,17 @@ const getRedirects = ({ path, release }) => {
55
.filter(Boolean)
56
.filter(r => r !== canonical)
57
61
- return [...new Set(prefixRedirects)]
62
- .sort((a, b) => a.localeCompare(b, 'en'))
58
+ return [...new Set(prefixRedirects)].sort((a, b) => a.localeCompare(b, 'en'))
59
}
60
65
-const transform = (data, { release, path, frontmatter, format = s => s }) => {
66
- let { attributes, body } = parseFm(data.toString())
61
+const transform = (data, {release, path, frontmatter, format = s => s}) => {
62
+ let {attributes, body} = parseFm(data.toString())
63
64
/* istanbul ignore next */
65
if (!attributes.redirect_from) {
66
attributes.redirect_from = []
67
}
72
- attributes.redirect_from.push(...getRedirects({ path, release }))
68
+ attributes.redirect_from.push(...getRedirects({path, release}))
69
70
const ghFrontmatter = {
71
github_repo: gh.nwo,
@@ -91,7 +87,7 @@ const transform = (data, { release, path, frontmatter, format = s => s }) => {
87
const sortRedirects = (a, b) => a.localeCompare(b, 'en')
88
89
attributes = Object.fromEntries(
94
- Object.entries({ ...attributes, ...ghFrontmatter, ...frontmatter })
90
+ Object.entries({...attributes, ...ghFrontmatter, ...frontmatter})
91
.filter(([, v]) => (Array.isArray(v) ? v.length : true))
92
.map(([k, v]) => (Array.isArray(v) ? [k, v.sort(sortRedirects)] : [k, v]))
93
.sort(([a], [b]) => {
@@ -100,12 +96,12 @@ const transform = (data, { release, path, frontmatter, format = s => s }) => {
96
/* istanbul ignore next */
97
const bIndex = order.includes(b) ? order.indexOf(b) : order.length
98
return aIndex - bIndex
103
- })
99
+ }),
100
)
101
102
// first format with prettier, this helps so other replacements don't have to
103
// worry about newlines vs spaces
108
- body = prettier.format(body, { parser: 'markdown', proseWrap: 'never' })
104
+ body = prettier.format(body, {parser: 'markdown', proseWrap: 'never'})
105
// then do replacements for all cli makdown files
106
body = body
107
// some legacy versions of the docs did not get this replaced
@@ -115,7 +111,7 @@ const transform = (data, { release, path, frontmatter, format = s => s }) => {
111
// specific version
112
.replace(
113
/\[([^\]]+)\]\(\/((?:commands|configuring-npm|using-npm)\/[^)]+)\)/g,
118
- (_, p1, p2) => `[${p1}](${release.url}/${p2})`
114
+ (_, p1, p2) => `[${p1}](${release.url}/${p2})`,
115
)
116
// remove html comments which are not mdx compatible
117
.replace(/^<!--\s.*?\s-->$\n/gm, '')
@@ -137,18 +133,18 @@ class Transform extends Minipass {
133
134
static sync = transform
135
140
- constructor (transformOpts) {
141
- super({ encoding: 'utf-8' })
136
+ constructor(transformOpts) {
137
+ super({encoding: 'utf-8'})
138
this.#opts = transformOpts
139
}
140
145
- write (c) {
141
+ write(c) {
142
this.#data.push(c)
143
this.#length += c.length
144
return true
145
}
146
151
- end () {
147
+ end() {
148
super.write(transform(Buffer.concat(this.#data, this.#length), this.#opts))
149
return super.end()
150
}
cli/package.json
-2
@@ -31,8 +31,6 @@
31
"devDependencies": {
32
"@npmcli/eslint-config": "^4.0.2",
33
"@npmcli/template-oss": "4.19.0",
34
- "eslint-plugin-node": "^11.1.0",
35
- "eslint-plugin-promise": "^6.1.1",
34
"tap": "^16.3.9"
35
},
36
"author": "GitHub Inc.",
cli/scripts/template-oss/index.js
+1
-3
@@ -5,7 +5,5 @@ module.exports = {
5
'.github/workflows/update-cli.yml': 'update-cli.yml',
6
},
7
},
8
- allowPaths: [
9
- '/releases.json',
10
- ],
8
+ allowPaths: ['/releases.json'],
9
}
cli/test/index.js
+40
-41
@@ -1,17 +1,11 @@
1
const t = require('tap')
2
-const { resolve, join, posix } = require('path')
2
+const {resolve, join, posix} = require('path')
3
const fs = require('fs/promises')
4
const pacote = require('pacote')
5
const yaml = require('yaml')
6
const semver = require('semver')
7
8
-const navPath = resolve(
9
- __dirname,
10
- '..',
11
- '..',
12
- 'content',
13
- 'nav.yml'
14
-)
8
+const navPath = resolve(__dirname, '..', '..', 'content', 'nav.yml')
9
10
const getReleases = () => [
11
{
@@ -32,11 +26,7 @@ const getReleases = () => [
26
},
27
]
28
35
-const mockBuild = async (t, {
36
- releases = getReleases(),
37
- packument = {},
38
- testdir: testdirOpts,
39
-} = {}) => {
29
+const mockBuild = async (t, {releases = getReleases(), packument = {}, testdir: testdirOpts} = {}) => {
30
const rawNav = await fs.readFile(navPath, 'utf-8')
31
const nav = yaml.parse(rawNav)
32
@@ -59,6 +49,8 @@ const mockBuild = async (t, {
49
return '8.19.3'
50
case '9':
51
return '9.0.0'
52
+ default:
53
+ throw new Error(`Unknown packument version: ${JSON.stringify(r)}`)
54
}
55
})
56
}
@@ -67,10 +59,10 @@ const mockBuild = async (t, {
59
packument.latest = packument.versions[packument.versions.length - 1]
60
}
61
70
- const navSection = (ref) => {
62
+ const navSection = ref => {
63
const id = ref === 'latest' ? `v${semver.major(packument.latest)}` : posix.basename(ref)
72
- const { variants } = nav.find(c => c.url === '/cli')
73
- const { children } = variants.find(v => posix.basename(v.url) === id)
64
+ const {variants} = nav.find(c => c.url === '/cli')
65
+ const {children} = variants.find(v => posix.basename(v.url) === id)
66
return yaml.stringify(children).replace(new RegExp(`/cli/${id}/`, 'g'), '/')
67
}
68
@@ -89,9 +81,9 @@ const mockBuild = async (t, {
81
}
82
},
83
},
92
- '@prettier/sync': { format: s => s },
84
+ '@prettier/sync': {format: s => s},
85
'../lib/gh.js': {
94
- getFile: async ({ ref }) => navSection(ref),
86
+ getFile: async ({ref}) => navSection(ref),
87
pathExists: async (ref, p) => {
88
if (ref.includes('v6') && p.includes('docs/lib/content')) {
89
return null
@@ -105,55 +97,62 @@ const mockBuild = async (t, {
97
return {
98
testdir,
99
releases,
108
- build: (opts) => build({
109
- releases,
110
- contentPath: join(testdir, 'content'),
111
- navPath: join(testdir, 'nav.yml'),
112
- ...opts,
113
- }),
100
+ build: opts =>
101
+ build({
102
+ releases,
103
+ contentPath: join(testdir, 'content'),
104
+ navPath: join(testdir, 'nav.yml'),
105
+ ...opts,
106
+ }),
107
}
108
}
109
117
-t.test('basic', async (t) => {
118
- const { releases, build, testdir } = await mockBuild(t, {
110
+t.test('basic', async t => {
111
+ const {releases, build, testdir} = await mockBuild(t, {
112
testdir: {
113
'nav.yml': '- title: cli\n url: /cli',
114
},
115
})
116
117
await build()
125
- t.strictSame(await fs.readdir(join(testdir, 'content')), releases.map(r => r.id))
118
+ t.strictSame(
119
+ await fs.readdir(join(testdir, 'content')),
120
+ releases.map(r => r.id),
121
+ )
122
})
123
128
-t.test('prereleases', async (t) => {
129
- const { build, releases, testdir } = await mockBuild(t, {
130
- packument: { versions: ['6.14.18', '7.24.2', '8.19.3', '9.0.0-pre.2'], latest: '8.19.3' },
124
+t.test('prereleases', async t => {
125
+ const {build, releases, testdir} = await mockBuild(t, {
126
+ packument: {versions: ['6.14.18', '7.24.2', '8.19.3', '9.0.0-pre.2'], latest: '8.19.3'},
127
})
128
133
- await build({ prerelease: false })
129
+ await build({prerelease: false})
130
const expectedReleases = releases.map(r => r.id).filter(r => r !== 'v9')
131
t.strictSame(await fs.readdir(join(testdir, 'content')), expectedReleases)
132
137
- await build({ prerelease: true })
138
- t.strictSame(await fs.readdir(join(testdir, 'content')), releases.map(r => r.id))
133
+ await build({prerelease: true})
134
+ t.strictSame(
135
+ await fs.readdir(join(testdir, 'content')),
136
+ releases.map(r => r.id),
137
+ )
138
})
139
141
-t.test('earlier release is latest', async (t) => {
142
- const { build } = await mockBuild(t, {
143
- packument: { latest: '8.19.3' },
140
+t.test('earlier release is latest', async t => {
141
+ const {build} = await mockBuild(t, {
142
+ packument: {latest: '8.19.3'},
143
})
144
145
await build()
146
})
147
149
-t.test('can skip fetching latest', async (t) => {
150
- const { build } = await mockBuild(t)
148
+t.test('can skip fetching latest', async t => {
149
+ const {build} = await mockBuild(t)
150
152
- await build({ useCurrent: true })
151
+ await build({useCurrent: true})
152
})
153
155
-t.test('add variant to nav', async (t) => {
156
- const { build } = await mockBuild(t, {
154
+t.test('add variant to nav', async t => {
155
+ const {build} = await mockBuild(t, {
156
testdir: {
157
'nav.yml': '- title: cli\n url: /cli\n variants:\n - url: /cli/v0',
158
},
cli/test/transform.js
+31
-28
@@ -1,9 +1,9 @@
1
const t = require('tap')
2
const fm = require('front-matter')
3
4
-const transform = ({ id, path }) => {
4
+const transform = ({id, path}) => {
5
const Transform = t.mock('../lib/transform', {
6
- '../lib/gh.js': { nwo: 'npm/cli' },
6
+ '../lib/gh.js': {nwo: 'npm/cli'},
7
})
8
const transformed = Transform.sync('---\n---\n', {
9
release: {
@@ -19,8 +19,8 @@ const transform = ({ id, path }) => {
19
}
20
21
t.test('v6 default page', async t => {
22
- const v6 = transform({ id: 'v6', path: 'configuring-npm/package-locks' })
23
- const v7 = transform({ id: 'v7', path: 'configuring-npm/package-locks' })
22
+ const v6 = transform({id: 'v6', path: 'configuring-npm/package-locks'})
23
+ const v7 = transform({id: 'v7', path: 'configuring-npm/package-locks'})
24
25
t.strictSame(v6.redirect_from, [
26
'/cli/configuring-npm/package-locks',
@@ -30,15 +30,12 @@ t.test('v6 default page', async t => {
30
'/configuring-npm/package-locks',
31
'/files/package-locks',
32
])
33
- t.strictSame(v7.redirect_from, [
34
- '/cli/v7/configuring-npm/package-locks',
35
- '/cli/v7/files/package-locks',
36
- ])
33
+ t.strictSame(v7.redirect_from, ['/cli/v7/configuring-npm/package-locks', '/cli/v7/files/package-locks'])
34
})
35
36
t.test('command', async t => {
40
- const v7 = transform({ id: 'v7', path: 'commands/npm-bin' })
41
- const v8 = transform({ id: 'v8', path: 'commands/npm-bin' })
37
+ const v7 = transform({id: 'v7', path: 'commands/npm-bin'})
38
+ const v8 = transform({id: 'v8', path: 'commands/npm-bin'})
39
40
t.strictSame(v7.redirect_from, [
41
'/cli/v7/bin',
@@ -69,8 +66,8 @@ t.test('command', async t => {
66
})
67
68
t.test('package-json files', async t => {
72
- const v7 = transform({ id: 'v7', path: 'configuring-npm/package-json' })
73
- const v8 = transform({ id: 'v8', path: 'configuring-npm/package-json' })
69
+ const v7 = transform({id: 'v7', path: 'configuring-npm/package-json'})
70
+ const v8 = transform({id: 'v8', path: 'configuring-npm/package-json'})
71
72
t.strictSame(v7.redirect_from, [
73
'/cli/v7/configuring-npm/package-json',
@@ -95,20 +92,26 @@ t.test('package-json files', async t => {
92
})
93
94
t.test('registry signatures', async t => {
98
- t.strictSame(transform({
99
- id: 'v8',
100
- path: 'about-pgp-signatures-for-packages-in-the-public-registry',
101
- }).redirect_from, [
102
- '/about-registry-signatures',
103
- '/cli/about-pgp-signatures-for-packages-in-the-public-registry',
104
- '/cli/v8/about-pgp-signatures-for-packages-in-the-public-registry',
105
- ])
106
- t.strictSame(transform({
107
- id: 'v8',
108
- path: 'verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
109
- }).redirect_from, [
110
- '/cli/v8/verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
111
- '/cli/verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
112
- '/verifying-registry-signatures',
113
- ])
95
+ t.strictSame(
96
+ transform({
97
+ id: 'v8',
98
+ path: 'about-pgp-signatures-for-packages-in-the-public-registry',
99
+ }).redirect_from,
100
+ [
101
+ '/about-registry-signatures',
102
+ '/cli/about-pgp-signatures-for-packages-in-the-public-registry',
103
+ '/cli/v8/about-pgp-signatures-for-packages-in-the-public-registry',
104
+ ],
105
+ )
106
+ t.strictSame(
107
+ transform({
108
+ id: 'v8',
109
+ path: 'verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
110
+ }).redirect_from,
111
+ [
112
+ '/cli/v8/verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
113
+ '/cli/verifying-the-pgp-signature-for-a-package-from-the-npm-public-registry',
114
+ '/verifying-registry-signatures',
115
+ ],
116
+ )
117
})
gatsby-node.js
-1
@@ -289,6 +289,5 @@ const fetchContributors = async (path, fm, {reporter, octokit}) => {
289
}
290
} catch (err) {
291
reporter[CI ? 'panic' : 'error'](`Error fetching contributors for ${path}`, err)
292
- return
292
}
293
}
package-lock.json
+5
-3
@@ -42,11 +42,13 @@
42
"react-dom": "^18.2.0",
43
"react-focus-on": "^3.9.1",
44
"react-helmet": "^6.1.0",
45
- "styled-components": "^5.3.11"
45
+ "styled-components": "^5.3.11",
46
+ "styled-system": "^5.1.5"
47
},
48
"devDependencies": {
49
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
50
"@github/prettier-config": "^0.0.6",
51
+ "@npmcli/eslint-config": "^4.0.2",
52
"@npmcli/template-oss": "4.19.0",
53
"@testing-library/jest-dom": "^6.1.4",
54
"@testing-library/react": "^14.0.0",
@@ -56,7 +58,9 @@
58
"eslint-plugin-github": "^4.10.1",
59
"eslint-plugin-import": "^2.28.1",
60
"eslint-plugin-jsx-a11y": "^6.7.1",
61
+ "eslint-plugin-node": "^11.1.0",
62
"eslint-plugin-primer-react": "^4.0.3",
63
+ "eslint-plugin-promise": "^6.1.1",
64
"eslint-plugin-react": "^7.33.2",
65
"eslint-plugin-react-hooks": "^4.6.0",
66
"jest": "^29.7.0",
@@ -83,8 +87,6 @@
87
"devDependencies": {
88
"@npmcli/eslint-config": "^4.0.2",
89
"@npmcli/template-oss": "4.19.0",
86
- "eslint-plugin-node": "^11.1.0",
87
- "eslint-plugin-promise": "^6.1.1",
90
"tap": "^16.3.9"
91
},
92
"engines": {
package.json
+5
-1
@@ -57,11 +57,13 @@
57
"react-dom": "^18.2.0",
58
"react-focus-on": "^3.9.1",
59
"react-helmet": "^6.1.0",
60
- "styled-components": "^5.3.11"
60
+ "styled-components": "^5.3.11",
61
+ "styled-system": "^5.1.5"
62
},
63
"devDependencies": {
64
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
65
"@github/prettier-config": "^0.0.6",
66
+ "@npmcli/eslint-config": "^4.0.2",
67
"@npmcli/template-oss": "4.19.0",
68
"@testing-library/jest-dom": "^6.1.4",
69
"@testing-library/react": "^14.0.0",
@@ -71,7 +73,9 @@
73
"eslint-plugin-github": "^4.10.1",
74
"eslint-plugin-import": "^2.28.1",
75
"eslint-plugin-jsx-a11y": "^6.7.1",
76
+ "eslint-plugin-node": "^11.1.0",
77
"eslint-plugin-primer-react": "^4.0.3",
78
+ "eslint-plugin-promise": "^6.1.1",
79
"eslint-plugin-react": "^7.33.2",
80
"eslint-plugin-react-hooks": "^4.6.0",
81
"jest": "^29.7.0",
scripts/template-oss/index.js
+5
@@ -22,6 +22,11 @@ module.exports = {
22
'.github/settings.yml': false,
23
},
24
},
25
+ workspaceModule: {
26
+ add: {
27
+ '.eslintrc.js': false,
28
+ },
29
+ },
30
ciVersions: 'latest',
31
latestCiVersion: 18,
32
macCI: false,
src/mdx/code.js
+2
-2
@@ -54,7 +54,7 @@ function Code({className = '', children}) {
54
55
return (
56
<Highlight code={code} language={className.replace(/language-/, '') || 'bash'} theme={themes.github}>
57
- {({className, style, tokens, getLineProps, getTokenProps}) => (
57
+ {({className: highlightClassName, style, tokens, getLineProps, getTokenProps}) => (
58
<Box
59
sx={{
60
// Make <pre> adjust to the width of the container
@@ -91,7 +91,7 @@ function Code({className = '', children}) {
91
}}
92
/>
93
<Box sx={{m: 0, p: 3, overflowX: 'auto'}}>
94
- <Box as="pre" className={className} tabIndex={0} sx={{m: 0}}>
94
+ <Box as="pre" className={highlightClassName} tabIndex={0} sx={{m: 0}}>
95
{tokens.map((line, i) => (
96
<div key={i} {...getLineProps({line, key: i})}>
97
{line.map((token, key) => (
src/mdx/index.js
+2
-2
@@ -2,7 +2,7 @@ import React from 'react'
2
import {Box, Heading, themeGet, Text, Octicon} from '@primer/react'
3
import {withPrefix} from 'gatsby'
4
import styled from 'styled-components'
5
-import {variant} from 'styled-system'
5
+import {variant as styledVariant} from 'styled-system'
6
import {LinkIcon} from '@primer/octicons-react'
7
import textContent from 'react-addons-text-content'
8
import {FULL_HEADER_HEIGHT} from '../constants'
@@ -268,7 +268,7 @@ const StyledNote = styled.div`
268
margin-bottom: 0;
269
}
270
271
- ${variant({
271
+ ${styledVariant({
272
variants: {
273
info: {
274
borderColor: 'accent.muted',