Skip to content

Commit 2f7ca14

Browse files
authored
refactor to more generic code
2 parents 5792ec9 + 4f274d5 commit 2f7ca14

File tree

6 files changed

+166
-145
lines changed

6 files changed

+166
-145
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
branches: master
66

77
env:
8-
NODE_VERSION: 12.x
8+
NODE_VERSION: 16.x
99

1010
jobs:
1111
test:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.nyc_output
2+
.tap
23
node_modules
34
package-lock.json

common.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
'use strict'
2+
3+
const path = require('path')
4+
const semver = require('semver')
5+
6+
const ltsNames = {
7+
4: 'argon',
8+
6: 'boron',
9+
8: 'carbon',
10+
10: 'dubnium',
11+
12: 'erbium',
12+
14: 'fermium',
13+
16: 'gallium',
14+
18: 'hydrogen'
15+
}
16+
17+
class Linker {
18+
#links = new Map()
19+
#dirs = []
20+
#baseDir
21+
#docsDir
22+
constructor ({ baseDir, docsDir }) {
23+
this.#baseDir = baseDir
24+
this.#docsDir = docsDir
25+
}
26+
27+
async getLinks (allDirectories, readDir) {
28+
const allDirs = allDirectories
29+
.map((d) => path.basename(d))
30+
.map((d) => {
31+
try {
32+
return semver.parse(d)
33+
/* c8 ignore next 3 */
34+
} catch (e) {
35+
return null
36+
}
37+
})
38+
.filter(Boolean)
39+
40+
this.#makeDocsLinks(allDirs.map((d) => d.raw))
41+
42+
this.#dirs = allDirs.filter((d) => semver.satisfies(d, '~0.10 || ~0.12 || >= 1.0')).map((d) => d.raw)
43+
this.#dirs.sort((d1, d2) => semver.compare(d1, d2))
44+
45+
this.#link('0.10')
46+
this.#link(0.12)
47+
48+
for (let i = 1; ; i++) {
49+
if (!this.#link(i) && i >= 4) {
50+
break
51+
}
52+
}
53+
54+
const max = this.#link(null)
55+
const tbreg = new RegExp(`(\\w+)-${max}.tar.gz`)
56+
const latestDir = path.join(this.#baseDir, 'latest')
57+
58+
let tarball = (await readDir(this.#links.get(latestDir) || latestDir)).filter((f) => tbreg.test(f))
59+
60+
/* c8 ignore next 3 */
61+
if (tarball.length !== 1) {
62+
throw new Error('Could not find latest.tar.gz')
63+
}
64+
65+
tarball = tarball[0]
66+
const name = tarball.match(tbreg)[1]
67+
const dst = path.join(this.#baseDir, `${name}-latest.tar.gz`)
68+
this.#links.set(dst, path.join(this.#baseDir, 'latest', tarball))
69+
return this.#links
70+
}
71+
72+
#makeDocsLinks (versions) {
73+
if (!this.#docsDir) {
74+
return
75+
}
76+
77+
for (const version of versions) {
78+
const src = path.join(this.#baseDir, version, 'docs')
79+
const dst = path.join(this.#docsDir, version)
80+
this.#links.set(dst, src)
81+
}
82+
}
83+
84+
#link (version) {
85+
const line = version && `${version}.x`
86+
const range = version ? `${Number(version) < 1 ? '~' : '^'}${line}` : '*'
87+
const max = semver.maxSatisfying(this.#dirs, range)
88+
89+
if (!max) {
90+
return false
91+
}
92+
93+
const symlink = (name) => {
94+
const dst = path.join(this.#baseDir, name)
95+
const src = path.join(this.#baseDir, max)
96+
97+
this.#links.set(dst, src)
98+
99+
if (!this.#docsDir) {
100+
return
101+
}
102+
103+
const dsrc = path.join(this.#baseDir, max, 'docs')
104+
const ddst = path.join(this.#docsDir, name)
105+
this.#links.set(ddst, dsrc)
106+
}
107+
108+
if (line) {
109+
symlink(`latest-v${line}`)
110+
if (ltsNames[version]) {
111+
symlink(`latest-${ltsNames[version]}`)
112+
}
113+
} else {
114+
symlink('latest')
115+
}
116+
117+
return max
118+
}
119+
}
120+
121+
module.exports = {
122+
Linker
123+
}

latest-linker.js

Lines changed: 18 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -2,156 +2,34 @@
22

33
'use strict'
44

5-
const fs = require('fs')
5+
const fs = require('fs/promises')
66
const path = require('path')
7-
const semver = require('semver')
8-
const map = require('map-async')
9-
10-
const ltsNames = {
11-
4: 'argon',
12-
6: 'boron',
13-
8: 'carbon',
14-
10: 'dubnium',
15-
12: 'erbium',
16-
14: 'fermium',
17-
16: 'gallium',
18-
18: 'hydrogen'
19-
}
7+
const { Linker } = require('./common.js')
208

9+
/* c8 ignore next 3 */
2110
if (process.argv.length < 3) {
2211
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]')
2312
}
2413

2514
const dir = path.resolve(process.argv[2])
26-
const docsDir = process.argv[3] && path.resolve(process.argv[3])
27-
28-
if (!fs.statSync(dir).isDirectory()) {
29-
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]')
30-
}
15+
const docsDir = process.argv[3] && path.resolve(process.argv[3]);
3116

32-
if (docsDir && !fs.statSync(docsDir).isDirectory()) {
33-
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]')
34-
}
35-
36-
map(
37-
fs.readdirSync(dir).map((d) => path.join(dir, d)),
38-
(d, callback) => fs.stat(d, (_, stat) => callback(null, { d, stat })),
39-
afterMap
40-
)
41-
42-
function afterMap (err, allDirs) {
43-
if (err) {
44-
throw err
17+
(async function main () {
18+
/* c8 ignore next 3 */
19+
if (!(await fs.stat(dir)).isDirectory()) {
20+
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]')
4521
}
4622

47-
allDirs = allDirs.filter((d) => d.stat && d.stat.isDirectory())
48-
.map((d) => path.basename(d.d))
49-
.map((d) => {
50-
try {
51-
return semver.parse(d)
52-
} catch (e) {}
53-
})
54-
.filter(Boolean)
55-
56-
makeDocsLinks(allDirs.map((v) => v.raw))
57-
58-
const dirs = allDirs.filter((d) => semver.satisfies(d, '~0.10 || ~0.12 || >= 1.0'))
59-
.map((d) => d.raw)
60-
61-
dirs.sort((d1, d2) => semver.compare(d1, d2))
62-
63-
link('0.10', dirs)
64-
link(0.12, dirs)
65-
66-
for (let i = 1; ; i++) {
67-
if (!link(i, dirs) && i >= 4) {
68-
break
69-
}
23+
/* c8 ignore next 3 */
24+
if (docsDir && !(await fs.stat(docsDir)).isDirectory()) {
25+
throw new Error('Usage: latest-linker.js <downloads directory> [docs directory]')
7026
}
7127

72-
const max = link(null, dirs)
73-
const tbreg = new RegExp(`(\\w+)-${max}.tar.gz`)
74-
75-
let tarball = fs.readdirSync(path.join(dir, 'latest'))
76-
.filter((f) => tbreg.test(f))
77-
78-
if (tarball.length !== 1) {
79-
throw new Error('Could not find latest.tar.gz')
28+
const allDirs = (await fs.readdir(dir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
29+
const linker = new Linker({ baseDir: dir, docsDir })
30+
const links = await linker.getLinks(allDirs, fs.readdir)
31+
for (const [dest, src] of links) {
32+
await fs.unlink(dest).catch(() => {})
33+
await fs.symlink(src, dest)
8034
}
81-
82-
tarball = tarball[0]
83-
const name = tarball.match(tbreg)[1]
84-
const dst = path.join(dir, `${name}-latest.tar.gz`)
85-
try {
86-
fs.unlinkSync(dst)
87-
} catch (e) {}
88-
fs.symlinkSync(path.join(dir, 'latest', tarball), dst)
89-
}
90-
91-
function makeDocsLinks (versions) {
92-
if (!docsDir) {
93-
return
94-
}
95-
96-
versions.forEach((version) => {
97-
const src = path.join(dir, version, 'docs')
98-
const dst = path.join(docsDir, version)
99-
100-
fs.stat(src, (err, stat) => {
101-
if (err || !stat.isDirectory()) {
102-
return
103-
}
104-
105-
fs.unlink(dst, () => {
106-
fs.symlink(src, dst, (err) => {
107-
if (err) {
108-
throw err
109-
}
110-
})
111-
})
112-
})
113-
})
114-
}
115-
116-
function link (version, dirs) {
117-
const line = version && `${version}.x`
118-
const range = version ? `${Number(version) < 1 ? '~' : '^'}${line}` : '*'
119-
const max = semver.maxSatisfying(dirs, range)
120-
121-
if (!max) {
122-
return false
123-
}
124-
125-
function symlink (name) {
126-
const dst = path.join(dir, name)
127-
const src = path.join(dir, max)
128-
129-
try {
130-
fs.unlinkSync(dst)
131-
} catch (e) {}
132-
fs.symlinkSync(src, dst)
133-
134-
if (!docsDir) {
135-
return
136-
}
137-
138-
const dsrc = path.join(dir, max, 'docs')
139-
const ddst = path.join(docsDir, name)
140-
141-
try {
142-
fs.unlinkSync(ddst)
143-
} catch (e) {}
144-
fs.symlinkSync(dsrc, ddst)
145-
}
146-
147-
if (line) {
148-
symlink(`latest-v${line}`)
149-
if (ltsNames[version]) {
150-
symlink(`latest-${ltsNames[version]}`)
151-
}
152-
} else {
153-
symlink('latest')
154-
}
155-
156-
return max
157-
}
35+
})()

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"main": "latest-linker.js",
66
"scripts": {
77
"lint": "standard",
8-
"test": "npm run lint && tap test.js"
8+
"test": "npm run lint && tap --allow-incomplete-coverage test.js"
99
},
1010
"repository": {
1111
"type": "git",
@@ -14,15 +14,14 @@
1414
"author": "Rod <[email protected]> (http://r.va.gg/)",
1515
"license": "MIT",
1616
"dependencies": {
17-
"map-async": "^0.1.1",
1817
"semver": "^7.3.2"
1918
},
2019
"bin": {
2120
"nodejs-latest-linker": "latest-linker.js"
2221
},
2322
"preferGlobal": true,
2423
"devDependencies": {
25-
"tap": "^14.10.8",
26-
"standard": "^14.3.4"
24+
"standard": "^17.1.0",
25+
"tap": "^18.4.0"
2726
}
2827
}

test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ const { execFileSync } = require('child_process')
44
const fs = require('fs')
55
const path = require('path')
66
const tap = require('tap')
7+
const { Linker } = require('./common.js')
8+
9+
tap.test('Linker', async t => {
10+
const linker = new Linker({ baseDir: 'base', docsDir: 'docs' })
11+
const links = await linker.getLinks(
12+
['v19.8.0/', 'v19.8.1/', 'v19.9.0/', 'v20.7.0/', 'v20.8.0/'],
13+
async () => ['docs/', 'win-x64/', 'node-v20.8.0-aix-ppc64.tar.gz', 'node-v20.8.0-arm64.msi', 'node-v20.8.0-headers.tar.gz', 'node-v20.8.0.tar.gz']
14+
)
15+
t.same(links, new Map([
16+
['docs/v19.8.0', 'base/v19.8.0/docs'],
17+
['docs/v19.8.1', 'base/v19.8.1/docs'],
18+
['docs/v19.9.0', 'base/v19.9.0/docs'],
19+
['docs/v20.7.0', 'base/v20.7.0/docs'],
20+
['docs/v20.8.0', 'base/v20.8.0/docs'],
21+
['base/latest', 'base/v20.8.0'],
22+
['docs/latest', 'base/v20.8.0/docs'],
23+
['base/node-latest.tar.gz', 'base/latest/node-v20.8.0.tar.gz']
24+
]))
25+
t.end()
26+
})
727

828
tap.test('basic test', t => {
929
const dir = t.testdir({

0 commit comments

Comments
 (0)