Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Aug 7, 2019
1 parent e146799 commit 7ef308e
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 99 deletions.
100 changes: 1 addition & 99 deletions src/nve/main.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
#!/usr/bin/env node
import { argv, exit } from 'process'
import { readFile, writeFile } from 'fs'
import { promisify } from 'util'

import pathExists from 'path-exists'
import { clean as cleanRange, maxSatisfying, ltr } from 'semver'

import { validateInput } from './validate.js'
import { CACHE_DIR } from './cache.js'
import { getVersion } from './versions.js'
import { runNode } from './run.js'
import { cleanupOnError } from './cleanup.js'
import { fetchUrl } from './fetch.js'

const pReadFile = promisify(readFile)
const pWriteFile = promisify(writeFile)

// CLI that forwards its arguments to another node instance of a specific
// version range. The version range is specified as the first argument.
Expand All @@ -39,92 +29,4 @@ const nve = async function(versionRange, args = []) {
return { exitCode, signal }
}

// Retrieve the Node version matching a specific `versionRange`
const getVersion = async function(versionRange) {
const versions = await getVersions(versionRange)

const versionA = maxSatisfying(versions, versionRange)

if (versionA === null) {
throw new Error(`Invalid Node version: ${versionRange}`)
}

return versionA
}

// Retrieve all available Node versions
const getVersions = async function(versionRange) {
const cachedVersions = await getCachedVersions(versionRange)

if (cachedVersions !== undefined) {
return cachedVersions
}

const versions = await fetchVersions()

await cleanupOnError(() => cacheVersions(versions), VERSIONS_CACHE)

return versions
}

// Fetch all available Node versions by making a HTTP request to Node website
// Versions are already sorted from newest to oldest
const fetchVersions = async function() {
const response = await fetchUrl(INDEX_URL)
const index = await response.json()
const versions = index.map(getVersionField)
return versions
}

const INDEX_URL = 'https://nodejs.org/dist/index.json'

const getVersionField = function({ version }) {
return version.slice(1)
}

// We cache the HTTP request. The cache needs to be invalidated sometimes since
// new Node versions are made available every week. We only invalidate it when
// the requested `versionRange` targets the latest Node version.
// The cache is persisted to `./node_modules/.cache/nve/versions.json`.
// Also we also cache it in-memory so it's performed only once per process.
const getCachedVersions = async function(versionRange) {
if (currentCachedVersions !== undefined) {
return currentCachedVersions
}

if (!(await pathExists(VERSIONS_CACHE))) {
return
}

const versions = await pReadFile(VERSIONS_CACHE, 'utf8')
const versionsA = JSON.parse(versions)

if (isLatestVersion(versionRange, versionsA)) {
return
}

return versionsA
}

// If latest is 12.8.0, `versionRange` `12.8` should invalid cache, but not
// `12.8.0`. `13` should also invalidate it.
const isLatestVersion = function(versionRange, versions) {
const matchesLatest =
cleanRange(versionRange) === null &&
maxSatisfying(versions, versionRange) === versions[0]
return matchesLatest || ltr(versions[0], versionRange)
}

// Persist the cached versions
const cacheVersions = async function(versions) {
await pWriteFile(VERSIONS_CACHE, JSON.stringify(versions, null, 2))
// eslint-disable-next-line fp/no-mutation
currentCachedVersions = versions
}

const VERSIONS_CACHE = `${CACHE_DIR}/versions.json`

// eslint-disable-next-line fp/no-let, init-declarations
let currentCachedVersions

runCli()
100 changes: 100 additions & 0 deletions src/nve/versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { readFile, writeFile } from 'fs'
import { promisify } from 'util'

import pathExists from 'path-exists'
import { clean as cleanRange, maxSatisfying, ltr } from 'semver'

import { CACHE_DIR } from './cache.js'
import { cleanupOnError } from './cleanup.js'
import { fetchUrl } from './fetch.js'

const pReadFile = promisify(readFile)
const pWriteFile = promisify(writeFile)

// Retrieve the Node version matching a specific `versionRange`
export const getVersion = async function(versionRange) {
const versions = await getVersions(versionRange)

const versionA = maxSatisfying(versions, versionRange)

if (versionA === null) {
throw new Error(`Invalid Node version: ${versionRange}`)
}

return versionA
}

// Retrieve all available Node versions
const getVersions = async function(versionRange) {
const cachedVersions = await getCachedVersions(versionRange)

if (cachedVersions !== undefined) {
return cachedVersions
}

const versions = await fetchVersions()

await cleanupOnError(() => cacheVersions(versions), VERSIONS_CACHE)

return versions
}

// Fetch all available Node versions by making a HTTP request to Node website
// Versions are already sorted from newest to oldest
const fetchVersions = async function() {
const response = await fetchUrl(INDEX_URL)
const index = await response.json()
const versions = index.map(getVersionField)
return versions
}

const INDEX_URL = 'https://nodejs.org/dist/index.json'

const getVersionField = function({ version }) {
return version.slice(1)
}

// We cache the HTTP request. The cache needs to be invalidated sometimes since
// new Node versions are made available every week. We only invalidate it when
// the requested `versionRange` targets the latest Node version.
// The cache is persisted to `./node_modules/.cache/nve/versions.json`.
// Also we also cache it in-memory so it's performed only once per process.
const getCachedVersions = async function(versionRange) {
if (currentCachedVersions !== undefined) {
return currentCachedVersions
}

if (!(await pathExists(VERSIONS_CACHE))) {
return
}

const versions = await pReadFile(VERSIONS_CACHE, 'utf8')
const versionsA = JSON.parse(versions)

if (isLatestVersion(versionRange, versionsA)) {
return
}

return versionsA
}

// If latest is 12.8.0, `versionRange` `12.8` should invalid cache, but not
// `12.8.0`. `13` should also invalidate it.
const isLatestVersion = function(versionRange, versions) {
const matchesLatest =
cleanRange(versionRange) === null &&
maxSatisfying(versions, versionRange) === versions[0]
return matchesLatest || ltr(versions[0], versionRange)
}

// Persist the cached versions
const cacheVersions = async function(versions) {
await pWriteFile(VERSIONS_CACHE, JSON.stringify(versions, null, 2))
// eslint-disable-next-line fp/no-mutation
currentCachedVersions = versions
}

const VERSIONS_CACHE = `${CACHE_DIR}/versions.json`

// eslint-disable-next-line fp/no-let, init-declarations
let currentCachedVersions

0 comments on commit 7ef308e

Please sign in to comment.