0

I'm currently converting a serverless AWS lambda microservices API over to typescript. I'm hoping I can maintain the existing js files while adding more ts files as we move on. I'm having some trouble configuring webpack and typescript and getting this error at runtime when calling one of my lambdas

× Unhandled exception in handler 'getInstitutionInfo'.
× Runtime.UserCodeSyntaxError: SyntaxError: Cannot use import statement outside a module
      at _loadUserApp (C:\git\my-project\node_modules\serverless-offline\src\lambda\handler-runner\in-process-runner\aws-lambda-ric\UserFunction.js:307:15)
      at async module.exports.load (C:\git\my-project\node_modules\serverless-offline\src\lambda\handler-runner\in-process-runner\aws-lambda-ric\UserFunction.js:341:21)
      at async InProcessRunner.run (file:///C:/git/my-project/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/InProcessRunner.js:41:21)
      at async MessagePort.<anonymous> (file:///C:/git/my-project/node_modules/serverless-offline/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js:24:14)
× SyntaxError: Cannot use import statement outside a module

Seems to be complaining about one of the packages I'm using rather than my code. This is my tsconfig.json

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "lib": [
      "es2020"
    ],
    "module": "commonjs",
    "target": "es2020",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "sourceMap": true,
    "allowJs": true,
    "outDir": ".erb/dll"
  }
}

and my webpack config

const path = require('path');
const slsw = require('serverless-webpack');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  // `mode` will be set to `production` and comes with included optimizations
  // when building to be run on AWS or similar.
  // https://webpack.js.org/configuration/mode/
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  // to determine what source maps to use per dev or prod
  // https://webpack.js.org/configuration/devtool/
  devtool: slsw.lib.webpack.isLocal ? 'source-map' : 'cheap-source-map',

  // the provided argument will be an object referencing functions as defined
  // in your `serverless.yml` .
  // https://webpack.js.org/concepts/entry-points/
  entry: slsw.lib.entries,
  target: 'node',
  resolve: {
    // What file extensions we want Webpack to care about, and in what order
    // https://webpack.js.org/configuration/resolve/#resolveextensions
    extensions: ['.cjs', '.mjs', '.js', '.ts'],
    // `yarn add -D tsconfig-paths-webpack-plugin` if you need path aliases
    // plugins: [new TsconfigPathsPlugin()],
  },
  // Where the bundled files will be output. Not strictly necessary with
  // Serverless Webpack.
  // https://webpack.js.org/configuration/output/
  output: {
    libraryTarget: 'commonjs2',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
  },
  // Anything that will be available to the bundled code in the runtime
  // environment and does not need to be included in any of the bundles.
  //
  // In AWS Lambda, the `aws-sdk` is available and we almost certainly want to
  // exclude it from our bundle(s). Similarly, because it's a Node lambda,
  // Node's native modules will also be available.
  externals: ['aws-sdk', nodeExternals()],
  module: {
    // Instruct Webpack to use the `ts-loader` for any TypeScript files, else it
    // won't know what to do with them.
    rules: [
      {
        test: /\.[jt]s?$/,
        loader: 'ts-loader',
        exclude: [
          [
            /node_modules/,
            path.resolve(__dirname, '.webpack'),
            path.resolve(__dirname, '.serverless'),
          ],
        ],
        // And here we have options for ts-loader
        // https://www.npmjs.com/package/ts-loader#options
        options: {
          // Disable type checking, this will lead to improved build times
          transpileOnly: true,
          // Enable file caching, can be quite useful when running offline
          experimentalFileCaching: true,
        },
      },
    ],
  },
  // We still want type checking, just without the burden on build performance,
  // so we use a plugin to take care of it on another thread.
  plugins: [new ForkTsCheckerWebpackPlugin()],
};
Real World
  • 1,593
  • 1
  • 21
  • 46
  • Does your lambda run as esm or cjs? Your TS config suggests you want cjs and the error implies your project does not have type module applied. Are you able to switch to esm, i.e. use a node runtime that supports it? Otherwise you will need something like babel to transpile that dependency to use cjs. – morganney May 19 '23 at 13:54
  • @morganney Might be a stupid question, but how do I find that out? I presume its whatever the code is compiled to, which is commonjs. There is no "type" set in package.json and the node version its deployed at is 18 – Real World May 19 '23 at 14:48
  • Node 18 supports esm. I’d suggest migrating your app to have an esm build target. Otherwise you’re adding more build tools to support an older module system. You can also see if that dependency has a version that is published as cjs and use that instead. – morganney May 19 '23 at 17:36

1 Answers1

0

The call stack was a red herring as it wasn't showing the call stack of where the error actually occurred. The issue was with an npm package we had created using esm imports but was being imported into the project that's using commonjs. The fix was to rebuild the package with babel to change it to commonjs.

Real World
  • 1,593
  • 1
  • 21
  • 46