Skip to content

Commit

Permalink
feat: refresh types on Deno CLI cache (#66)
Browse files Browse the repository at this point in the history
* feat: refresh types on Deno CLI cache

* fix: use cache command

* chore: move rules to eslintrc

* chore: remove eslint comments

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
eduardoboucas and kodiakhq[bot] committed Jul 1, 2022
1 parent 2c57aaa commit 534ea80
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module.exports = {
sourceType: 'module',
},
rules: {
complexity: 'off',
'max-statements': 'off',
'node/no-missing-import': 'off',
},
overrides: [
Expand Down
16 changes: 8 additions & 8 deletions src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,6 @@ class DenoBridge {
return this.currentDownload
}

private log(...data: unknown[]) {
if (!this.debug) {
return
}

console.log(...data)
}

private static runWithBinary(binaryPath: string, args: string[], pipeOutput?: boolean) {
const runDeno = execa(binaryPath, args)

Expand Down Expand Up @@ -185,6 +177,14 @@ class DenoBridge {
return { global: false, path: downloadedPath }
}

log(...data: unknown[]) {
if (!this.debug) {
return
}

console.log(...data)
}

// Runs the Deno CLI in the background and returns a reference to the child
// process, awaiting its execution.
async run(args: string[], { pipeOutput }: RunOptions = {}) {
Expand Down
3 changes: 3 additions & 0 deletions src/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { bundle as bundleESZIP } from './formats/eszip.js'
import { bundle as bundleJS } from './formats/javascript.js'
import { ImportMap, ImportMapFile } from './import_map.js'
import { writeManifest } from './manifest.js'
import { ensureLatestTypes } from './types.js'

interface BundleOptions {
cacheDirectory?: string
Expand Down Expand Up @@ -98,6 +99,8 @@ const bundle = async (
})
const basePath = getBasePath(sourceDirectories)

await ensureLatestTypes(deno)

// The name of the bundle will be the hash of its contents, which we can't
// compute until we run the bundle process. For now, we'll use a random ID
// to create the bundle artifacts and rename them later.
Expand Down
5 changes: 4 additions & 1 deletion src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DenoBridge, OnAfterDownloadHook, OnBeforeDownloadHook, ProcessRef } fro
import type { EdgeFunction } from '../edge_function.js'
import { generateStage2 } from '../formats/javascript.js'
import { ImportMap, ImportMapFile } from '../import_map.js'
import { ensureLatestTypes } from '../types.js'

import { killProcess, waitForServer } from './util.js'

Expand Down Expand Up @@ -94,7 +95,6 @@ interface ServeOptions {
port: number
}

// eslint-disable-next-line complexity, max-statements
const serve = async ({
certificatePath,
debug,
Expand All @@ -120,6 +120,9 @@ const serve = async ({
// Wait for the binary to be downloaded if needed.
await deno.getBinaryPath()

// Downloading latest types if needed.
await ensureLatestTypes(deno)

// Creating an ImportMap instance with any import maps supplied by the user,
// if any.
const importMap = new ImportMap(importMaps)
Expand Down
77 changes: 77 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { promises as fs } from 'fs'
import { join } from 'path'

import fetch from 'node-fetch'

import type { DenoBridge } from './bridge.js'

const TYPES_URL = 'https://edge.netlify.com'

const ensureLatestTypes = async (deno: DenoBridge, customTypesURL?: string) => {
const typesURL = customTypesURL ?? TYPES_URL

let [localVersion, remoteVersion] = [await getLocalVersion(deno), '']

try {
remoteVersion = await getRemoteVersion(typesURL)
} catch (error) {
deno.log('Could not check latest version of types:', error)

return
}

if (localVersion === remoteVersion) {
deno.log('Local version of types is up-to-date:', localVersion)

return
}

deno.log('Local version of types is outdated, updating:', localVersion)

try {
await deno.run(['cache', '-r', typesURL])
} catch (error) {
deno.log('Could not download latest types:', error)

return
}

try {
await writeVersionFile(deno, remoteVersion)
} catch {
// no-op
}
}

const getLocalVersion = async (deno: DenoBridge) => {
const versionFilePath = join(deno.cacheDirectory, 'types-version.txt')

try {
const version = await fs.readFile(versionFilePath, 'utf8')

return version
} catch {
// no-op
}
}

const getRemoteVersion = async (typesURL: string) => {
const versionURL = new URL('/version.txt', typesURL)
const res = await fetch(versionURL.toString())

if (res.status !== 200) {
throw new Error('Unexpected status code from version endpoint')
}

const version = await res.text()

return version
}

const writeVersionFile = async (deno: DenoBridge, version: string) => {
const versionFilePath = join(deno.cacheDirectory, 'types-version.txt')

await fs.writeFile(versionFilePath, version)
}

export { ensureLatestTypes }
82 changes: 82 additions & 0 deletions test/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { promises as fs } from 'fs'
import { join } from 'path'

import test from 'ava'
import nock from 'nock'
import { stub } from 'sinon'
import tmp from 'tmp-promise'

import { DenoBridge } from '../src/bridge.js'
import { ensureLatestTypes } from '../src/types.js'

test('`ensureLatestTypes` updates the Deno CLI cache if the local version of types is outdated', async (t) => {
const mockURL = 'https://edge.netlify'
const mockVersion = '123456789'
const latestVersionMock = nock(mockURL).get('/version.txt').reply(200, mockVersion)

const tmpDir = await tmp.dir()
const deno = new DenoBridge({
cacheDirectory: tmpDir.path,
})

const mock = stub(deno, 'run').resolves()

await ensureLatestTypes(deno, mockURL)

const versionFile = await fs.readFile(join(tmpDir.path, 'types-version.txt'), 'utf8')

t.true(latestVersionMock.isDone())
t.is(mock.callCount, 1)
t.deepEqual(mock.firstCall.firstArg, ['cache', '-r', mockURL])
t.is(versionFile, mockVersion)

mock.restore()

await fs.rmdir(tmpDir.path, { recursive: true })
})

test('`ensureLatestTypes` does not update the Deno CLI cache if the local version of types is up-to-date', async (t) => {
const mockURL = 'https://edge.netlify'
const mockVersion = '987654321'

const tmpDir = await tmp.dir()
const versionFilePath = join(tmpDir.path, 'types-version.txt')

await fs.writeFile(versionFilePath, mockVersion)

const latestVersionMock = nock(mockURL).get('/version.txt').reply(200, mockVersion)
const deno = new DenoBridge({
cacheDirectory: tmpDir.path,
})
const mock = stub(deno, 'run').resolves()

await ensureLatestTypes(deno, mockURL)

t.true(latestVersionMock.isDone())
t.is(mock.callCount, 0)

mock.restore()

await fs.rmdir(tmpDir.path, { recursive: true })
})

test('`ensureLatestTypes` does not throw if the types URL is not available', async (t) => {
const mockURL = 'https://edge.netlify'
const latestVersionMock = nock(mockURL).get('/version.txt').reply(500)

const tmpDir = await tmp.dir()
const deno = new DenoBridge({
cacheDirectory: tmpDir.path,
})

const mock = stub(deno, 'run').resolves()

await ensureLatestTypes(deno, mockURL)

t.true(latestVersionMock.isDone())
t.is(mock.callCount, 0)

mock.restore()

await fs.rmdir(tmpDir.path, { recursive: true })
})

0 comments on commit 534ea80

Please sign in to comment.