diff --git a/CHANGELOG.md b/CHANGELOG.md index c15fafb7..1a450fc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [8.19.1](https://github.com/netlify/edge-bundler/compare/v8.19.0...v8.19.1) (2023-09-06) + + +### Bug Fixes + +* hide stack trace on syntax errors ([#464](https://github.com/netlify/edge-bundler/issues/464)) ([9261b8c](https://github.com/netlify/edge-bundler/commit/9261b8c13a07970be0e177aa6f1c4358ac862110)) +* pin bootstrap version used in config extraction ([#469](https://github.com/netlify/edge-bundler/issues/469)) ([19d142d](https://github.com/netlify/edge-bundler/commit/19d142dcb17953e4c598626709662a2ddb6cf506)) +* remap `netlify:edge` specifier ([#467](https://github.com/netlify/edge-bundler/issues/467)) ([9728d1a](https://github.com/netlify/edge-bundler/commit/9728d1a067cd791226eae2110e03897d2e0ac97b)) + ## [8.19.0](https://github.com/netlify/edge-bundler/compare/v8.18.0...v8.19.0) (2023-08-28) diff --git a/deno/bundle.ts b/deno/bundle.ts index 6df88f90..64e3f49f 100644 --- a/deno/bundle.ts +++ b/deno/bundle.ts @@ -3,4 +3,12 @@ import { writeStage2 } from './lib/stage2.ts' const [payload] = Deno.args const { basePath, destPath, externals, functions, importMapData, vendorDirectory } = JSON.parse(payload) -await writeStage2({ basePath, destPath, externals, functions, importMapData, vendorDirectory }) +try { + await writeStage2({ basePath, destPath, externals, functions, importMapData, vendorDirectory }) +} catch (error) { + if (error instanceof Error && error.message.includes("The module's source code could not be parsed")) { + delete error.stack + } + + throw error +} diff --git a/deno/config.ts b/deno/config.ts index 33f85d8e..9377184e 100644 --- a/deno/config.ts +++ b/deno/config.ts @@ -1,7 +1,9 @@ -const [functionURL, collectorURL, bootstrapURL, rawExitCodes] = Deno.args +// this needs to be updated whenever there's a change to globalThis.Netlify in bootstrap +import { Netlify } from "https://64e8753eae24930008fac6d9--edge.netlify.app/bootstrap/index-combined.ts" + +const [functionURL, collectorURL, rawExitCodes] = Deno.args const exitCodes = JSON.parse(rawExitCodes) -const { Netlify } = await import(bootstrapURL) globalThis.Netlify = Netlify let func diff --git a/node/bundle_error.ts b/node/bundle_error.ts index 8b9a6099..a9de79c9 100644 --- a/node/bundle_error.ts +++ b/node/bundle_error.ts @@ -1,3 +1,5 @@ +import type { ExecaError } from 'execa' + interface BundleErrorOptions { format: string } @@ -30,6 +32,11 @@ class BundleError extends Error { */ const wrapBundleError = (input: unknown, options?: BundleErrorOptions) => { if (input instanceof Error) { + if (input.message.includes("The module's source code could not be parsed")) { + // eslint-disable-next-line no-param-reassign + input.message = (input as ExecaError).stderr + } + return new BundleError(input, options) } diff --git a/node/bundler.test.ts b/node/bundler.test.ts index 39bfc922..34e99499 100644 --- a/node/bundler.test.ts +++ b/node/bundler.test.ts @@ -87,7 +87,8 @@ test('Uses the vendored eszip module instead of fetching it from deno.land', asy }) test('Adds a custom error property to user errors during bundling', async () => { - expect.assertions(2) + process.env.NO_COLOR = 'true' + expect.assertions(3) const { basePath, cleanup, distPath } = await useFixture('invalid_functions') const sourceDirectory = join(basePath, 'functions') @@ -102,6 +103,16 @@ test('Adds a custom error property to user errors during bundling', async () => await bundle([sourceDirectory], distPath, declarations, { basePath }) } catch (error) { expect(error).toBeInstanceOf(BundleError) + const [messageBeforeStack] = (error as BundleError).message.split('at (file://') + expect(messageBeforeStack).toMatchInlineSnapshot(` + "error: Uncaught (in promise) Error: The module's source code could not be parsed: Unexpected eof at file:///root/functions/func1.ts:1:27 + + export default async () => + ~ + const ret = new Error(getStringFromWasm0(arg0, arg1)); + ^ + " + `) expect((error as BundleError).customErrorInfo).toEqual({ location: { format: 'eszip', diff --git a/node/bundler.ts b/node/bundler.ts index efe93766..b771d42a 100644 --- a/node/bundler.ts +++ b/node/bundler.ts @@ -43,7 +43,6 @@ export const bundle = async ( tomlDeclarations: Declaration[] = [], { basePath: inputBasePath, - bootstrapURL = 'https://edge.netlify.com/bootstrap/index-combined.ts', cacheDirectory, configPath, debug, @@ -125,10 +124,10 @@ export const bundle = async ( // Retrieving a configuration object for each function. // Run `getFunctionConfig` in parallel as it is a non-trivial operation and spins up deno const internalConfigPromises = internalFunctions.map( - async (func) => [func.name, await getFunctionConfig({ func, importMap, deno, log: logger, bootstrapURL })] as const, + async (func) => [func.name, await getFunctionConfig({ func, importMap, deno, log: logger })] as const, ) const userConfigPromises = userFunctions.map( - async (func) => [func.name, await getFunctionConfig({ func, importMap, deno, log: logger, bootstrapURL })] as const, + async (func) => [func.name, await getFunctionConfig({ func, importMap, deno, log: logger })] as const, ) // Creating a hash of function names to configuration objects. diff --git a/node/config.test.ts b/node/config.test.ts index 1bec3625..b5cb1e71 100644 --- a/node/config.test.ts +++ b/node/config.test.ts @@ -14,8 +14,6 @@ import { FunctionConfig, getFunctionConfig } from './config.js' import type { Declaration } from './declaration.js' import { ImportMap } from './import_map.js' -const bootstrapURL = 'https://edge.netlify.com/bootstrap/index-combined.ts' - const importMapFile = { baseURL: new URL('file:///some/path/import-map.json'), imports: { @@ -146,7 +144,6 @@ describe('`getFunctionConfig` extracts configuration properties from function fi importMap: new ImportMap([importMapFile]), deno, log: logger, - bootstrapURL, }) if (func.error) { @@ -284,7 +281,6 @@ test('Passes validation if default export exists and is a function', async () => importMap: new ImportMap([importMapFile]), deno, log: logger, - bootstrapURL, }), ).resolves.not.toThrow() @@ -321,7 +317,6 @@ test('Fails validation if default export is not function', async () => { importMap: new ImportMap([importMapFile]), deno, log: logger, - bootstrapURL, }) await expect(config).rejects.toThrowError(invalidDefaultExportErr(path)) @@ -358,7 +353,6 @@ test('Fails validation if default export is not present', async () => { importMap: new ImportMap([importMapFile]), deno, log: logger, - bootstrapURL, }) await expect(config).rejects.toThrowError(invalidDefaultExportErr(path)) diff --git a/node/config.ts b/node/config.ts index d3361400..3d4b8ab2 100644 --- a/node/config.ts +++ b/node/config.ts @@ -59,13 +59,11 @@ export const getFunctionConfig = async ({ func, importMap, deno, - bootstrapURL, log, }: { func: EdgeFunction importMap: ImportMap deno: DenoBridge - bootstrapURL: string log: Logger }) => { // The extractor is a Deno script that will import the function and run its @@ -94,7 +92,6 @@ export const getFunctionConfig = async ({ extractorPath, pathToFileURL(func.path).href, pathToFileURL(collector.path).href, - bootstrapURL, JSON.stringify(ConfigExitCode), ], { rejectOnExitCode: false }, diff --git a/node/import_map.test.ts b/node/import_map.test.ts index 6a6581be..153465d9 100644 --- a/node/import_map.test.ts +++ b/node/import_map.test.ts @@ -26,7 +26,8 @@ test('Handles import maps with full URLs without specifying a base URL', () => { const map = new ImportMap([inputFile1, inputFile2]) const { imports } = map.getContents() - expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts') + expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts?v=legacy') + expect(imports['@netlify/edge-functions']).toBe('https://edge.netlify.com/v1/index.ts') expect(imports['alias:jamstack']).toBe('https://jamstack.org/') expect(imports['alias:pets']).toBe('https://petsofnetlify.com/') }) @@ -44,7 +45,8 @@ test('Resolves relative paths to absolute paths if a base path is not provided', const { imports } = map.getContents() const expectedPath = join(cwd(), 'my-cool-site', 'heart', 'pets') - expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts') + expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts?v=legacy') + expect(imports['@netlify/edge-functions']).toBe('https://edge.netlify.com/v1/index.ts') expect(imports['alias:pets']).toBe(`${pathToFileURL(expectedPath).toString()}/`) }) @@ -81,7 +83,7 @@ describe('Returns the fully resolved import map', () => { specifier2: 'file:///some/full/path/file2.js', specifier1: 'file:///some/full/path/file.js', '@netlify/edge-functions': 'https://edge.netlify.com/v1/index.ts', - 'netlify:edge': 'https://edge.netlify.com/v1/index.ts', + 'netlify:edge': 'https://edge.netlify.com/v1/index.ts?v=legacy', }) expect(scopes).toStrictEqual({ @@ -106,7 +108,7 @@ describe('Returns the fully resolved import map', () => { specifier2: 'file:///root/full/path/file2.js', specifier1: 'file:///root/full/path/file.js', '@netlify/edge-functions': 'https://edge.netlify.com/v1/index.ts', - 'netlify:edge': 'https://edge.netlify.com/v1/index.ts', + 'netlify:edge': 'https://edge.netlify.com/v1/index.ts?v=legacy', }) expect(scopes).toStrictEqual({ @@ -154,6 +156,7 @@ test('Writes import map file to disk', async () => { await file.cleanup() - expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts') + expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts?v=legacy') + expect(imports['@netlify/edge-functions']).toBe('https://edge.netlify.com/v1/index.ts') expect(imports['alias:pets']).toBe(pathToFileURL(expectedPath).toString()) }) diff --git a/node/import_map.ts b/node/import_map.ts index 903ce3e3..1ba1c2e4 100644 --- a/node/import_map.ts +++ b/node/import_map.ts @@ -10,7 +10,7 @@ import { isFileNotFoundError } from './utils/error.js' const INTERNAL_IMPORTS = { '@netlify/edge-functions': 'https://edge.netlify.com/v1/index.ts', - 'netlify:edge': 'https://edge.netlify.com/v1/index.ts', + 'netlify:edge': 'https://edge.netlify.com/v1/index.ts?v=legacy', } type Imports = Record diff --git a/node/server/server.ts b/node/server/server.ts index 78792270..de36ce88 100644 --- a/node/server/server.ts +++ b/node/server/server.ts @@ -88,7 +88,7 @@ const prepareServer = ({ if (options.getFunctionsConfig) { functionsConfig = await Promise.all( - functions.map((func) => getFunctionConfig({ func, importMap, deno, bootstrapURL, log: logger })), + functions.map((func) => getFunctionConfig({ func, importMap, deno, log: logger })), ) } diff --git a/package-lock.json b/package-lock.json index 7263b9ec..1e1804fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@netlify/edge-bundler", - "version": "8.19.0", + "version": "8.19.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@netlify/edge-bundler", - "version": "8.19.0", + "version": "8.19.1", "license": "MIT", "dependencies": { "@import-maps/resolve": "^1.0.1", diff --git a/package.json b/package.json index 0be9026f..7769fde1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@netlify/edge-bundler", - "version": "8.19.0", + "version": "8.19.1", "description": "Intelligently prepare Netlify Edge Functions for deployment", "type": "module", "main": "./dist/node/index.js",