Tips for creating TypeScript Google Cloud Functions using webpack

I'm currently facing a challenge while coding a Google Cloud Function using TypeScript. The concept involves having handler functions defined for various Cloud Functions in separate files within the source repository, along with some code that is shared among all handlers. After compiling my sources with tsc, I realized the need to webpack them in order to generate a single index.js file that can be loaded by Cloud Functions. Consequently, I require webpack to merge all handlers into one file.

Below is my configuration file tsconfig.json:

{
  "compilerOptions": {
    "incremental": true,
    "target": "es2016",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node"
  },
  "include": ["src/**/*.ts"]
}

Shown below is the code for a handler from src/TheHandler.ts:

import { EventFunction } from "@google-cloud/functions-framework";

export const TheHandler: EventFunction = (
  message,
  context
) => {
  console.log(message);
  console.log(context);
};

The above code compiles to JavaScript without encountering any issues.

However, upon feeding it into webpack, using the following webpack.config.js:

const path = require("path");
const fs = require("fs");
const nodeExternals = require("webpack-node-externals");

const files = fs
  .readdirSync("src")
  .filter((item) => item.match(/\.ts$/))
  .map((file) => `./src/${file}`);

module.exports = {
  mode: "production",
  entry: files,
  externalsPresets: { node: true },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".ts"],
  },
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};

The result is an empty dist/bundle.js, as confirmed by the webpack output:

asset bundle.js 0 bytes [compared for emit] [minimized] (name: main)
./src/TheHandler.ts 612 bytes [built] [code generated]
webpack 5.50.0 compiled successfully in 1937 ms

Could this issue be related to the module format or webpack attempting to bundle everything before ts-loader compiles src/TheHandler.ts? Or could it be something else entirely?

Answer №1

In order to make it work, I included the line

libraryTarget: "commonjs2"
inside the output section of my webpack.config.js.

Answer №2

In your exploration, you discovered that setting the libraryTarget parameter is crucial to create a webpack bundle that can be imported by external modules outside of the webpack build (such as cloud functions).

If you're looking for a smoother experience and improved developer productivity with reliable Hot Module Replacement (HMR), you might consider using webpack-cloud-functions (which happens to be developed by me). This tool simplifies much of the webpack configuration required for working with cloud functions, including handling the libraryTarget.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Should we consider packaging the npm dependencies code along with our code as a best practice?

What is the best way to handle npm dependencies in our code bundle? If it's preferable to include the npm dependency code in our bundle, does it make sense to add it as a separate module or package? If not, how can I prevent bundling my dependencie ...

Unable to modify the active property of the specified object as it is read-only

Presented here is the interface: export interface ProductCommand extends ProductDetailsCommand { } This is the ProductDetailsCommand interface: export interface ProductDetailsCommand { id: string; active: boolean; archive: boolean; title: ...

Best location to define numerous dialog components

Currently, I have 8 custom Modals that are called in various places within my app. These modals are currently located inside the app.component.html as shown below: <agc class="app-content" [rows]="'auto 1fr'" [height]=" ...

Utilizing 'nestjs/jwt' for generating tokens with a unique secret tailored to each individual user

I'm currently in the process of generating a user token based on the user's secret during login. However, instead of utilizing a secret from the environment variables, my goal is to use a secret that is associated with a user object stored within ...

Creating an extended class in Typescript with a unique method that overrides another method with different argument types

I need to change the argument types of a method by overriding it: class Base { public myMethod(myString: string): undefined { return; } } class Child extends Base { public myMethod(myNumber: number): undefined { return super.m ...

npm script for quickly generating React components

Can a script be written in package.json to generate a new folder with a name provided as an argument 'MyComponent' (for instance) and also create four files with the same name: MyComponent.js MyComponent.css MyComponent.test.js README.md "s ...

What is the proper way to specify the type for the `clean-element` higher-order-component in React?

Error message: 'typeof TextareaAutosize' argument cannot be assigned to a type 'Component<{}, {}, any>'. Error: Property 'setState' is not found in 'typeof TextareaAutosize'. Here is the code snippet causin ...

What is the best way to resolve the unusual resolution issue that arises when switching from Next.js 12 to 13

Previously, I created a website using nextjs 12 and now I am looking to rebuild it entirely with nextjs 13. During the upgrade process, I encountered some strange issues. For example, the index page functions properly on my local build but not on Vercel. ...

Learn how to dynamically import external modules or plugins using the import() functionality of TypeScript 2.4 within the production script generated by Angular CLI

Utilizing the typescript 2.4 [import()][1] feature has proven to be effective for dynamically loading modules. Testing this functionality has shown positive results, especially when importing modules and components located within the same directory. Howev ...

I'm curious about the Next.js type that corresponds to the Redirect object

It's possible to set up redirection in Next.js by configuring it like this: module.exports = { async redirects() { return [ { source: '/about', destination: '/', permanent: true, }, ] ...

What could be causing my TSC to constantly crash whenever I try to utilize MUI props?

Currently in the process of converting a JavaScript project using Next.js and Material UI to TypeScript. This is a snippet of code from one of my components. Whenever I include Props as an intersection type along with MUI's BoxProps, the TypeScript c ...

How to customize Material UI Autocomplete options background color

Is there a way to change the background color of the dropdown options in my Material UI Autocomplete component? I've checked out some resources, but they only explain how to use the renderOption prop to modify the option text itself, resulting in a a ...

Observable<void> fails to trigger the subscriber

I am currently facing a challenge with implementing a unit test for an Observable in order to signal the completion of a process. While there is no asynchronous code in the logout function yet, I plan to include it once the full logic is implemented. The m ...

Is it feasible to bring in a Typescript file into an active ts-node REPL session?

I want to experiment with some Typescript code that I have written. Currently, I usually run ts-node my-file-name.ts to test it out. But I am interested in making this process more interactive, similar to the Python REPL where you can import modules and ...

Why is it necessary to include a dollar sign before interpolation in Angular?

While diving into a tutorial, I stumbled upon a piece of code that piqued my curiosity. I grasped the concept that appending a dollar sign as a suffix indicates observability, but I wonder why the dollar sign was also prefixed to this.log(`updated hero i ...

The content security policy is preventing a connection to the signalr hub

Currently, I am developing an application using electron that incorporates React and Typescript. One of the features I am integrating is a SignalR hub for chat functionality. However, when attempting to connect to my SignalR server, I encounter the followi ...

Using TypeScript and controllerAs with $rootScope

I am currently developing an application using Angular 1 and Typescript. Here is the code snippet for my Login Controller: module TheHub { /** * Controller for the login page. */ export class LoginController { static $inject = [ ...

What steps are needed to configure ESLint to exclusively analyze .ts files?

I need ESLint to exclusively focus on processing .ts files and not .js files. In order to achieve that, I made a .eslintignore file and included the following lines: *.js **/*.js Nevertheless, it appears that ESLint is disregarding this file. Is there so ...

Jest may come across test suites, but it discreetly disregards the individual tests

Having encountered an issue with Jest testing in a Nuxt/Vue v2 project, I found that after making some changes, the tests were no longer running. The unit tests were either passing or failing initially, but suddenly stopped running altogether. ----------|- ...

Using Typescript to specify the parameter type of a function as a generic function

After creating a function called compose, it looks like this: const composeTyped = <T, U, R>(f: (x: T) => U, g: (y: U) => R) => (x: T) => g(f(x)); It appears to me that both functions f and g fall under the type fGeneric, which is define ...