Skip to content

Commit

Permalink
fix: correctly annotate transpilation errors in nft bundler (#1086)
Browse files Browse the repository at this point in the history
Add `customErrorInfo` to errors happening during transpilation of esm with esbuild in the nft bundler

Co-authored-by: Eduardo Bouças <mail@eduardoboucas.com>
  • Loading branch information
danez and eduardoboucas committed May 18, 2022
1 parent 61e5b72 commit 28a85fb
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 14 deletions.
8 changes: 6 additions & 2 deletions src/runtimes/node/bundlers/nft/es_modules.ts
Expand Up @@ -56,6 +56,7 @@ export const processESM = async ({
fsCache,
mainFile,
reasons,
name,
}: {
basePath: string | undefined
config: FunctionConfig
Expand All @@ -64,6 +65,7 @@ export const processESM = async ({
fsCache: FsCache
mainFile: string
reasons: NodeFileTraceReasons
name: string
}): Promise<{ rewrites?: Map<string, string>; moduleFormat: ModuleFormat }> => {
const entrypointIsESM = isEntrypointESM({ basePath, esmPaths, mainFile })

Expand All @@ -82,7 +84,7 @@ export const processESM = async ({
}
}

const rewrites = await transpileESM({ basePath, config, esmPaths, fsCache, reasons })
const rewrites = await transpileESM({ basePath, config, esmPaths, fsCache, reasons, name })

return {
moduleFormat: 'cjs',
Expand Down Expand Up @@ -140,12 +142,14 @@ const transpileESM = async ({
esmPaths,
fsCache,
reasons,
name,
}: {
basePath: string | undefined
config: FunctionConfig
esmPaths: Set<string>
fsCache: FsCache
reasons: NodeFileTraceReasons
name: string
}) => {
const cache: Map<string, boolean> = new Map()
const pathsToTranspile = [...esmPaths].filter((path) => shouldTranspile(path, cache, esmPaths, reasons))
Expand All @@ -166,7 +170,7 @@ const transpileESM = async ({
await Promise.all(
pathsToTranspile.map(async (path) => {
const absolutePath = resolvePath(path, basePath)
const transpiled = await transpile(absolutePath, config)
const transpiled = await transpile(absolutePath, config, name)

rewrites.set(absolutePath, transpiled)
}),
Expand Down
5 changes: 5 additions & 0 deletions src/runtimes/node/bundlers/nft/index.ts
Expand Up @@ -25,6 +25,7 @@ const bundle: BundleFunction = async ({
config,
featureFlags,
mainFile,
name,
pluginsModulesPath,
repositoryRoot = basePath,
}) => {
Expand All @@ -43,6 +44,7 @@ const bundle: BundleFunction = async ({
featureFlags,
mainFile,
pluginsModulesPath,
name,
})
const filteredIncludedPaths = filterExcludedPaths([...dependencyPaths, ...includedFilePaths], excludedPaths)
const dirnames = filteredIncludedPaths.map((filePath) => normalize(dirname(filePath))).sort()
Expand Down Expand Up @@ -73,12 +75,14 @@ const traceFilesAndTranspile = async function ({
featureFlags,
mainFile,
pluginsModulesPath,
name,
}: {
basePath?: string
config: FunctionConfig
featureFlags: FeatureFlags
mainFile: string
pluginsModulesPath?: string
name: string
}) {
const fsCache: FsCache = {}
const {
Expand Down Expand Up @@ -129,6 +133,7 @@ const traceFilesAndTranspile = async function ({
fsCache,
mainFile,
reasons,
name,
})

return {
Expand Down
39 changes: 27 additions & 12 deletions src/runtimes/node/bundlers/nft/transpile.ts
@@ -1,22 +1,37 @@
import { build } from '@netlify/esbuild'

import type { FunctionConfig } from '../../../../config.js'
import type { RuntimeName } from '../../../runtime.js'
import { getBundlerTarget } from '../esbuild/bundler_target.js'
import type { NodeBundlerName } from '../index.js'

export const transpile = async (path: string, config: FunctionConfig) => {
export const transpile = async (path: string, config: FunctionConfig, functionName: string) => {
// The version of ECMAScript to use as the build target. This will determine
// whether certain features are transpiled down or left untransformed.
const nodeTarget = getBundlerTarget(config.nodeVersion)
const transpiled = await build({
bundle: false,
entryPoints: [path],
format: 'cjs',
logLevel: 'error',
platform: 'node',
sourcemap: Boolean(config.nodeSourcemap),
target: [nodeTarget],
write: false,
})

return transpiled.outputFiles[0].text
try {
const transpiled = await build({
bundle: false,
entryPoints: [path],
format: 'cjs',
logLevel: 'error',
platform: 'node',
sourcemap: Boolean(config.nodeSourcemap),
target: [nodeTarget],
write: false,
})

return transpiled.outputFiles[0].text
} catch (error) {
const bundler: NodeBundlerName = 'nft'
const runtime: RuntimeName = 'js'

error.customErrorInfo = {
type: 'functionsBundling',
location: { bundler, functionName, runtime },
}

throw error
}
}
5 changes: 5 additions & 0 deletions tests/fixtures/node-esm-top-level-await-error/function.js
@@ -0,0 +1,5 @@
const func = async () => {}

await func()

export { func } //makes nft detect this file as esm
17 changes: 17 additions & 0 deletions tests/main.js
Expand Up @@ -1897,6 +1897,23 @@ testMany(
},
)

test('Adds `type: "functionsBundling"` to user errors when transpiling esm in nft bundler', async (t) => {
try {
await zipNode(t, 'node-esm-top-level-await-error', {
opts: { config: { '*': { nodeBundler: 'nft' } } },
})

t.fail('Bundling should have thrown')
} catch (error) {
const { customErrorInfo } = error

t.is(customErrorInfo.type, 'functionsBundling')
t.is(customErrorInfo.location.bundler, 'nft')
t.is(customErrorInfo.location.functionName, 'function')
t.is(customErrorInfo.location.runtime, 'js')
}
})

test('Returns a list of all modules with dynamic imports in a `nodeModulesWithDynamicImports` property', async (t) => {
const fixtureName = 'node-module-dynamic-import'
const { files } = await zipNode(t, fixtureName, {
Expand Down

1 comment on commit 28a85fb

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏱ Benchmark results

largeDepsEsbuild: 6.3s

largeDepsNft: 29.1s

largeDepsZisi: 45.1s

Please sign in to comment.