Skip to content

Commit

Permalink
feat: log failures when loading import map files (#267)
Browse files Browse the repository at this point in the history
* chore: add test

* chore: cache ESZIP lib

* feat: log import map load failures
  • Loading branch information
eduardoboucas committed Dec 19, 2022
1 parent dcbd7f7 commit 138690b
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
with:
# Should match the `DENO_VERSION_RANGE` constant in `node/bridge.ts`.
deno-version: v1.22.0
- name: Setup Deno dependencies
run: deno cache https://deno.land/x/eszip@v0.28.0/eszip.ts
- name: Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
Expand Down
32 changes: 29 additions & 3 deletions node/bundler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import process from 'process'

import { deleteAsync } from 'del'
import tmp from 'tmp-promise'
import { test, expect } from 'vitest'
import { test, expect, vi } from 'vitest'

import { importMapSpecifier } from '../shared/consts.js'
import { runESZIP, useFixture } from '../test/util.js'

import { BundleError } from './bundle_error.js'
import { bundle, BundleOptions } from './bundler.js'
import { isNodeError } from './utils/error.js'
import { isFileNotFoundError } from './utils/error.js'
import { validateManifest } from './validation/manifest/index.js'

test('Produces an ESZIP bundle', async () => {
Expand Down Expand Up @@ -264,7 +264,7 @@ test('Ignores any user-defined `deno.json` files', async () => {
`The file at '${denoConfigPath} would be overwritten by this test. Please move the file to a different location and try again.'`,
)
} catch (error) {
if (isNodeError(error) && error.code !== 'ENOENT') {
if (!isFileNotFoundError(error)) {
throw error
}
}
Expand Down Expand Up @@ -342,3 +342,29 @@ test('Loads declarations and import maps from the deploy configuration', async (

await cleanup()
})

test("Ignores entries in `importMapPaths` that don't point to an existing import map file", async () => {
const systemLogger = vi.fn()
const { basePath, cleanup, distPath } = await useFixture('with_import_maps')
const sourceDirectory = join(basePath, 'functions')
const importMapPath = join(distPath, 'some-file-that-does-not-exist.json')
const declarations = [
{
function: 'func1',
path: '/func1',
},
]
const result = await bundle([sourceDirectory], distPath, declarations, {
basePath,
configPath: join(sourceDirectory, 'config.json'),
importMapPaths: [importMapPath],
systemLogger,
})
const generatedFiles = await fs.readdir(distPath)

expect(result.functions.length).toBe(1)
expect(generatedFiles.length).toBe(2)
expect(systemLogger).toHaveBeenCalledWith(`Did not find an import map file at '${importMapPath}'.`)

await cleanup()
})
2 changes: 1 addition & 1 deletion node/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const bundle = async (
const externals = deployConfig.layers.map((layer) => layer.name)
const importMap = new ImportMap()

await importMap.addFiles([deployConfig?.importMap, ...importMapPaths])
await importMap.addFiles([deployConfig?.importMap, ...importMapPaths], logger)

const functions = await findFunctions(sourceDirectories)
const functionBundle = await bundleESZIP({
Expand Down
4 changes: 2 additions & 2 deletions node/deploy_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { dirname, resolve } from 'path'
import type { Declaration } from './declaration.js'
import type { Layer } from './layer.js'
import type { Logger } from './logger.js'
import { isNodeError } from './utils/error.js'
import { isFileNotFoundError } from './utils/error.js'

/* eslint-disable camelcase */
interface DeployConfigFile {
Expand Down Expand Up @@ -35,7 +35,7 @@ export const load = async (path: string | undefined, logger: Logger): Promise<De

return parse(config, path)
} catch (error) {
if (isNodeError(error) && error.code !== 'ENOENT') {
if (!isFileNotFoundError(error)) {
logger.system('Error while parsing internal edge functions manifest:', error)
}
}
Expand Down
21 changes: 14 additions & 7 deletions node/import_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { fileURLToPath, pathToFileURL } from 'url'

import { parse } from '@import-maps/resolve'

import { Logger } from './logger.js'
import { isFileNotFoundError } from './utils/error.js'

const INTERNAL_IMPORTS = {
'netlify:edge': 'https://edge.netlify.com/v1/index.ts',
}
Expand Down Expand Up @@ -108,8 +111,8 @@ class ImportMap {
this.sources.push(source)
}

async addFile(path: string) {
const source = await readFile(path)
async addFile(path: string, logger: Logger) {
const source = await readFile(path, logger)

if (Object.keys(source.imports).length === 0) {
return
Expand All @@ -118,13 +121,13 @@ class ImportMap {
return this.add(source)
}

async addFiles(paths: (string | undefined)[]) {
async addFiles(paths: (string | undefined)[], logger: Logger) {
for (const path of paths) {
if (path === undefined) {
return
}

await this.addFile(path)
await this.addFile(path, logger)
}
}

Expand Down Expand Up @@ -171,7 +174,7 @@ class ImportMap {
}
}

const readFile = async (path: string): Promise<ImportMapSource> => {
const readFile = async (path: string, logger: Logger): Promise<ImportMapSource> => {
const baseURL = pathToFileURL(path)

try {
Expand All @@ -182,8 +185,12 @@ const readFile = async (path: string): Promise<ImportMapSource> => {
...importMap,
baseURL,
}
} catch {
// no-op
} catch (error) {
if (isFileNotFoundError(error)) {
logger.system(`Did not find an import map file at '${path}'.`)
} else {
logger.user(`Error while loading import map at '${path}':`, error)
}
}

return {
Expand Down
2 changes: 1 addition & 1 deletion node/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ const serve = async ({

const importMap = new ImportMap()

await importMap.addFiles(importMapPaths)
await importMap.addFiles(importMapPaths, logger)

const flags = ['--allow-all', '--unstable', `--import-map=${importMap.toDataURL()}`, '--no-config']

Expand Down
3 changes: 3 additions & 0 deletions node/utils/error.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isNodeError = (error: any): error is NodeJS.ErrnoException => error instanceof Error

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isFileNotFoundError = (error: any) => isNodeError(error) && error.code === 'ENOENT'
29 changes: 20 additions & 9 deletions test/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { promises as fs } from 'fs'
import { join, resolve } from 'path'
import { stderr, stdout } from 'process'
import { fileURLToPath, pathToFileURL } from 'url'

import { execa } from 'execa'
Expand Down Expand Up @@ -47,7 +48,19 @@ const runESZIP = async (eszipPath: string) => {
const tmpDir = await tmp.dir({ unsafeCleanup: true })

// Extract ESZIP into temporary directory.
await execa('deno', ['run', '--allow-all', 'https://deno.land/x/eszip@v0.28.0/eszip.ts', 'x', eszipPath, tmpDir.path])
const extractCommand = execa('deno', [
'run',
'--allow-all',
'https://deno.land/x/eszip@v0.28.0/eszip.ts',
'x',
eszipPath,
tmpDir.path,
])

extractCommand.stderr?.pipe(stderr)
extractCommand.stdout?.pipe(stdout)

await extractCommand

const virtualRootPath = join(tmpDir.path, 'source', 'root')
const stage2Path = join(virtualRootPath, '..', 'bootstrap-stage2')
Expand All @@ -63,17 +76,15 @@ const runESZIP = async (eszipPath: string) => {
await fs.rename(stage2Path, `${stage2Path}.js`)

// Run function that imports the extracted stage 2 and invokes each function.
const { stdout } = await execa('deno', [
'eval',
'--no-check',
'--import-map',
importMapPath,
inspectFunction(stage2Path),
])
const evalCommand = execa('deno', ['eval', '--no-check', '--import-map', importMapPath, inspectFunction(stage2Path)])

evalCommand.stderr?.pipe(stderr)

const result = await evalCommand

await tmpDir.cleanup()

return JSON.parse(stdout)
return JSON.parse(result.stdout)
}

export { fixturesDir, testLogger, runESZIP, useFixture }

0 comments on commit 138690b

Please sign in to comment.