Repository: netlify/zip-it-and-ship-it
Pull request #404 (2021-04-05 10:47:47)

feat: add errorInfo property to esbuild bundling errors
**- Summary**

This PR adds an `errorInfo` property to any errors resulting from bundling with esbuild, so that they're flagged as user errors in our build system.

Closes https://github.com/netlify/zip-it-and-ship-it/issues/384.

**- Test plan**

Added a test.

**- Description for the changelog**

feat: add errorInfo property to esbuild bundling errors

**- A picture of a cute animal (not mandatory but encouraged)**

![p091prnq](https://user-images.githubusercontent.com/4162329/113566474-a6f45c80-9604-11eb-9a89-6e168aaa4806.jpg)



Patch:
diff --git a/src/runtimes/node/bundler.js b/src/runtimes/node/bundler.js
index 822e263..acfb71a 100644
--- a/src/runtimes/node/bundler.js
+++ b/src/runtimes/node/bundler.js
@@ -14,6 +14,7 @@ const bundleJsFile = async function ({
   destFolder,
   externalModules = [],
   ignoredModules = [],
+  name,
   srcFile,
 }) {
   // De-duping external and ignored modules.
@@ -27,17 +28,6 @@ const bundleJsFile = async function ({
 
   // eslint-disable-next-line node/no-sync
   const buildFunction = supportsAsyncAPI ? esbuild.build : esbuild.buildSync
-  const data = await buildFunction({
-    bundle: true,
-    entryPoints: [srcFile],
-    external,
-    logLevel: 'warning',
-    outfile: bundlePath,
-    nodePaths: additionalModulePaths,
-    platform: 'node',
-    resolveExtensions: ['.js', '.jsx', '.mjs', '.cjs', '.json'],
-    target: ['es2017'],
-  })
   const cleanTempFiles = async () => {
     try {
       await pUnlink(bundlePath)
@@ -46,7 +36,25 @@ const bundleJsFile = async function ({
     }
   }
 
-  return { bundlePath, cleanTempFiles, data }
+  try {
+    const data = await buildFunction({
+      bundle: true,
+      entryPoints: [srcFile],
+      external,
+      logLevel: 'warning',
+      outfile: bundlePath,
+      nodePaths: additionalModulePaths,
+      platform: 'node',
+      resolveExtensions: ['.js', '.jsx', '.mjs', '.cjs', '.json'],
+      target: ['es2017'],
+    })
+
+    return { bundlePath, cleanTempFiles, data }
+  } catch (error) {
+    error.customErrorInfo = { type: 'functionsBundling', location: { functionName: name } }
+
+    throw error
+  }
 }
 
 module.exports = { bundleJsFile }
diff --git a/src/runtimes/node/index.js b/src/runtimes/node/index.js
index 49b6175..e5b5362 100644
--- a/src/runtimes/node/index.js
+++ b/src/runtimes/node/index.js
@@ -19,6 +19,7 @@ const zipFunction = async function ({
   extension,
   filename,
   mainFile,
+  name,
   pluginsModulesPath,
   srcDir,
   srcPath,
@@ -58,6 +59,7 @@ const zipFunction = async function ({
     extension,
     filename,
     mainFile,
+    name,
     pluginsModulesPath,
     srcDir,
     srcPath,
diff --git a/src/runtimes/node/zip_esbuild.js b/src/runtimes/node/zip_esbuild.js
index fe76ae8..ed41be1 100644
--- a/src/runtimes/node/zip_esbuild.js
+++ b/src/runtimes/node/zip_esbuild.js
@@ -33,6 +33,7 @@ const zipEsbuild = async ({
   extension,
   filename,
   mainFile,
+  name,
   pluginsModulesPath,
   srcDir,
   srcPath,
@@ -56,6 +57,7 @@ const zipEsbuild = async ({
     destFolder,
     externalModules,
     ignoredModules,
+    name,
     srcDir,
     srcFile: mainFile,
   })
diff --git a/src/zip.js b/src/zip.js
index dd328b7..00bba78 100644
--- a/src/zip.js
+++ b/src/zip.js
@@ -41,6 +41,7 @@ const zipFunctions = async function (
         filename: func.filename,
         ignoredNodeModules,
         mainFile: func.mainFile,
+        name: func.name,
         pluginsModulesPath,
         runtime: func.runtime,
         srcDir: func.srcDir,


Test patch:
diff --git a/tests/main.js b/tests/main.js
index b272e51..9a1ede8 100644
--- a/tests/main.js
+++ b/tests/main.js
@@ -820,6 +820,18 @@ testBundlers(
   },
 )
 
+test('Adds `type: "functionsBundling"` to esbuild bundling errors', async (t) => {
+  try {
+    await zipNode(t, 'node-module-native', {
+      opts: { config: { '*': { nodeBundler: ESBUILD } } },
+    })
+
+    t.fail('Function did not throw')
+  } catch (error) {
+    t.deepEqual(error.customErrorInfo, { type: 'functionsBundling', location: { functionName: 'function' } })
+  }
+})
+
 test('Uses the default Node bundler if no configuration object is supplied', async (t) => {
   const { files, tmpDir } = await zipNode(t, 'local-node-module')
   const requires = await getRequires({ filePath: resolve(tmpDir, 'src/function.js') })