aws-lambda-nodejs: package.json type field is not preserved with external node_modules
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
Hello 👋
I am using https://knexjs.org/ to apply database migrations via Lambda. This requires some modules to be loaded externally due to dynamic import which is working well with CommonJS.
However when porting the code to ES Modules (and using `OutputFormat.ESM`) I am seeing some module loading issues due to the `type` key not being set in the generated `package.json` file. I traced this back to this line:
https://github.com/aws/aws-cdk/blob/ac3ffa5cf8d9a1b664ccd5fcbe889e437be3ca5d/packages/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.ts#L288
This is surprising as it implicitly changes the output format of the bundle _back to CommonJS_.
### Regression Issue
- [ ] Select this option if this issue appears to be a regression.
### Last Known Working CDK Version
_No response_
### Expected Behavior
The `type` property should be added to the `package.json` file that is generated when using the `nodeModules` option, but only when in the source `package.json` file.
### Current Behavior
No `type` property is added to the generated `package.json`.
### Reproduction Steps
```typescript
import * as cdk from 'aws-cdk-lib';
import { type Construct } from 'constructs';
import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as Lambda from 'aws-cdk-lib/aws-lambda';
export class InfrastructureStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const banner =
"const require = (await import('node:module')).createRequire(import.meta.url);const __filename = (await import('node:url')).fileURLToPath(import.meta.url);const __dirname = (await import('node:path')).dirname(__filename);";
new NodejsFunction(this, 'lambda', {
functionName: 'test-knex-esm',
entry: 'index.ts',
runtime: Lambda.Runtime.NODEJS_20_X,
bundling: {
banner,
format: OutputFormat.ESM,
mainFields: ['module', 'main'],
nodeModules: ['knex'],
}
})
}
}
```
`index.ts` can have any contents to replicate this issue.
### Possible Solution
The `type` property is also added to the generated `package.json` when using the `nodeModules` option and only if it is present in the source `package.json`. However it may be worth considering to be explicit here as per the Node.js recommendation (and setting the `type` based on the value of `OutputType`):
> Writing ES module syntax in "ambiguous" files incurs a performance cost, and therefore it is encouraged that authors be explicit wherever possible. In particular, package authors should always include the ["type"](https://nodejs.org/api/packages.html#type) field in their package.json files, even in packages where all sources are CommonJS. Being explicit about the type of the package will future-proof the package in case the default type of Node.js ever changes, and it will also make things easier for build tools and loaders to determine how the files in the package should be interpreted.
>
> https://nodejs.org/api/packages.html#determining-module-system
### Additional Information/Context
There is a workaround here which is to write the source code in `.mjs` or `.mts` explicitly and not rely on the `package.json` lookup to determine the default module system that `.js` uses.
### CDK CLI Version
v2.174.0
### Framework Version
v2.174.0
### Node.js Version
v20.18.0
### OS
Mac OS X
### Language
TypeScript
### Language Version
TypeScript 5.7.2
### Other information
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.