Skip to content

(aws-lambda-nodejs): .mjs file extension and import.meta not supported #13274

@blake-regalia

Description

@blake-regalia

Although Lambda now supports Node v14:

  1. .mjs entry files are rejected by a regex pattern in aws-lambda-nodejs
  2. esbuild is transpiling the scripts to cjs format (commonjs) but BundlingOptions provides no means to specify format: "esm", and consequently esbuild polyfills import.meta which breaks all of its uses in the scripts

Reproduction Steps

lib/my-stack.mjs:

export class MyStack extends Stack {
    constructor(scope, id, props) {
        super(scope, id, props);

        // ...

        new NodejsFunction(this, 'Example', {
            runtime: Runtime.NODEJS_14_X,
            entry: 'src/entry-file.mjs',
            bundling: {
                target: 'es2020',
                // format: 'esm',   <-- should be able to pass this option here
            },
        };

        // ...
    }
}

src/entry-file.mjs:

export async function Example() {
    return import.meta.url;
}

What did you expect to happen?

  1. entry-file.mjs to be allowed to be used as an entry file.
  2. import.meta to be defined and Example() to return a string.

What actually happened?

  1. Error: Only JavaScript or TypeScript entry files are supported.
  2. Example() returns undefined since import.meta is polyfilled with an empty plain object.

Environment

  • CDK CLI Version : n/a
  • Framework Version:
  • Node.js Version: 14.13.0
  • OS : n/a
  • Language (Version):

Other

  1. For the .mjs extension:

    if (!/\.(jsx?|tsx?)$/.test(entry)) {

  2. Allow passing in the format option to esbuild:

    const esbuildCommand: string = [
    npx, 'esbuild',
    '--bundle', pathJoin(inputDir, this.relativeEntryPath),
    `--target=${this.props.target ?? toTarget(this.props.runtime)}`,
    '--platform=node',
    `--outfile=${pathJoin(outputDir, 'index.js')}`,
    ...this.props.minify ? ['--minify'] : [],
    ...this.props.sourceMap ? ['--sourcemap'] : [],
    ...this.externals.map(external => `--external:${external}`),
    ...loaders.map(([ext, name]) => `--loader:${ext}=${name}`),
    ...defines.map(([key, value]) => `--define:${key}=${value}`),
    ...this.props.logLevel ? [`--log-level=${this.props.logLevel}`] : [],
    ...this.props.keepNames ? ['--keep-names'] : [],
    ...this.relativeTsconfigPath ? [`--tsconfig=${pathJoin(inputDir, this.relativeTsconfigPath)}`] : [],
    ...this.props.metafile ? [`--metafile=${pathJoin(outputDir, 'index.meta.json')}`] : [],
    ...this.props.banner ? [`--banner='${this.props.banner}'`] : [],
    ...this.props.footer ? [`--footer='${this.props.footer}'`] : [],
    ].join(' ');


This is 🐛 Bug Report

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions