Skip to content

Commit 7ac7221

Browse files
fix(aws-lambda-nodejs): pre compilation with tsc is not being run (#18062)
Revamped the logic of running `tsc`, previously we were running `tsc` once for the whole project and due to this logic, `tsc` was not running as on the first run as `createBundlingCommand` first prepare command to execute on Docker and then for local bundling and on the first run we already updated the flag to not run `tsc` now. Closes [#18002](#18002) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 30e2233 commit 7ac7221

File tree

4 files changed

+93
-85
lines changed

4 files changed

+93
-85
lines changed

Diff for: packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts

+8-22
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as cdk from '@aws-cdk/core';
55
import { PackageInstallation } from './package-installation';
66
import { PackageManager } from './package-manager';
77
import { BundlingOptions, OutputFormat, SourceMapMode } from './types';
8-
import { exec, extractDependencies, findUp } from './util';
8+
import { exec, extractDependencies, findUp, getTsconfigCompilerOptions } from './util';
99

1010
const ESBUILD_MAJOR_VERSION = '0';
1111

@@ -68,13 +68,8 @@ export class Bundling implements cdk.BundlingOptions {
6868
this.tscInstallation = undefined;
6969
}
7070

71-
public static clearTscCompilationCache(): void {
72-
this.tscCompiled = false;
73-
}
74-
7571
private static esbuildInstallation?: PackageInstallation;
7672
private static tscInstallation?: PackageInstallation;
77-
private static tscCompiled = false
7873

7974
// Core bundling options
8075
public readonly image: cdk.DockerImage;
@@ -156,26 +151,17 @@ export class Bundling implements cdk.BundlingOptions {
156151

157152
private createBundlingCommand(options: BundlingCommandOptions): string {
158153
const pathJoin = osPathJoin(options.osPlatform);
159-
let tscCommand: string = '';
160-
let relativeEntryPath = this.relativeEntryPath;
154+
let relativeEntryPath = pathJoin(options.inputDir, this.relativeEntryPath);
155+
let tscCommand = '';
161156

162157
if (this.props.preCompilation) {
163-
164-
let tsconfig = this.relativeTsconfigPath;
158+
const tsconfig = this.props.tsconfig ?? findUp('tsconfig.json', path.dirname(this.props.entry));
165159
if (!tsconfig) {
166-
const findConfig = findUp('tsconfig.json', path.dirname(this.props.entry));
167-
if (!findConfig) {
168-
throw new Error('Cannot find a tsconfig.json, please specify the prop: tsconfig');
169-
}
170-
tsconfig = path.relative(this.projectRoot, findConfig);
160+
throw new Error('Cannot find a `tsconfig.json` but `preCompilation` is set to `true`, please specify it via `tsconfig`');
171161
}
172-
162+
const compilerOptions = getTsconfigCompilerOptions(tsconfig);
163+
tscCommand = `${options.tscRunner} "${relativeEntryPath}" ${compilerOptions}`;
173164
relativeEntryPath = relativeEntryPath.replace(/\.ts(x?)$/, '.js$1');
174-
if (!Bundling.tscCompiled) {
175-
// Intentionally Setting rootDir and outDir, so that the compiled js file always end up next ts file.
176-
tscCommand = `${options.tscRunner} --project ${pathJoin(options.inputDir, tsconfig)} --rootDir ./ --outDir ./`;
177-
Bundling.tscCompiled = true;
178-
}
179165
}
180166

181167
const loaders = Object.entries(this.props.loader ?? {});
@@ -193,7 +179,7 @@ export class Bundling implements cdk.BundlingOptions {
193179
const outFile = this.props.format === OutputFormat.ESM ? 'index.mjs' : 'index.js';
194180
const esbuildCommand: string[] = [
195181
options.esbuildRunner,
196-
'--bundle', `"${pathJoin(options.inputDir, relativeEntryPath)}"`,
182+
'--bundle', `"${relativeEntryPath}"`,
197183
`--target=${this.props.target ?? toTarget(this.props.runtime)}`,
198184
'--platform=node',
199185
...this.props.format ? [`--format=${this.props.format}`] : [],

Diff for: packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts

+62
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,65 @@ export function extractDependencies(pkgPath: string, modules: string[]): { [key:
144144

145145
return dependencies;
146146
}
147+
148+
export function getTsconfigCompilerOptions(tsconfigPath: string): string {
149+
const compilerOptions = extractTsConfig(tsconfigPath);
150+
const excludedCompilerOptions = [
151+
'composite',
152+
'tsBuildInfoFile',
153+
];
154+
155+
const options: Record<string, any> = {
156+
...compilerOptions,
157+
// Overrides
158+
incremental: false,
159+
// Intentionally Setting rootDir and outDir, so that the compiled js file always end up next to .ts file.
160+
rootDir: './',
161+
outDir: './',
162+
};
163+
164+
let compilerOptionsString = '';
165+
Object.keys(options).forEach((key: string) => {
166+
167+
if (excludedCompilerOptions.includes(key)) {
168+
return;
169+
}
170+
171+
const value = options[key];
172+
const option = '--' + key;
173+
const type = typeof value;
174+
175+
if (type === 'boolean') {
176+
if (value) {
177+
compilerOptionsString += option + ' ';
178+
}
179+
} else if (type === 'string') {
180+
compilerOptionsString += option + ' ' + value + ' ';
181+
} else if (type === 'object') {
182+
if (Array.isArray(value)) {
183+
compilerOptionsString += option + ' ' + value.join(',') + ' ';
184+
}
185+
} else {
186+
throw new Error(`Missing support for compilerOption: [${key}]: { ${type}, ${value}} \n`);
187+
}
188+
});
189+
190+
return compilerOptionsString.trim();
191+
}
192+
193+
194+
function extractTsConfig(tsconfigPath: string, previousCompilerOptions?: Record<string, any>): Record<string, any> | undefined {
195+
// eslint-disable-next-line @typescript-eslint/no-require-imports
196+
const { extends: extendedConfig, compilerOptions } = require(tsconfigPath);
197+
const updatedCompilerOptions = {
198+
...(previousCompilerOptions ?? {}),
199+
...compilerOptions,
200+
};
201+
if (extendedConfig) {
202+
return extractTsConfig(
203+
path.resolve(tsconfigPath.replace(/[^\/]+$/, ''), extendedConfig),
204+
updatedCompilerOptions,
205+
);
206+
}
207+
return updatedCompilerOptions;
208+
}

Diff for: packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts

+7-62
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ beforeEach(() => {
1616
jest.resetAllMocks();
1717
jest.restoreAllMocks();
1818
Bundling.clearEsbuildInstallationCache();
19-
Bundling.clearTscInstallationCache();
2019

2120
jest.spyOn(Code, 'fromAsset');
2221

@@ -574,90 +573,36 @@ test('esbuild bundling with projectRoot and externals and dependencies', () => {
574573
});
575574

576575
test('esbuild bundling with pre compilations', () => {
577-
Bundling.bundle({
578-
entry,
579-
projectRoot,
580-
depsLockFilePath,
581-
runtime: Runtime.NODEJS_14_X,
582-
forceDockerBundling: true,
583-
tsconfig,
584-
preCompilation: true,
585-
architecture: Architecture.X86_64,
586-
});
587-
588-
// Correctly bundles with esbuild
589-
expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(depsLockFilePath), {
590-
assetHashType: AssetHashType.OUTPUT,
591-
bundling: expect.objectContaining({
592-
command: [
593-
'bash', '-c',
594-
[
595-
'tsc --project /asset-input/lib/custom-tsconfig.ts --rootDir ./ --outDir ./ &&',
596-
'esbuild --bundle \"/asset-input/lib/handler.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"',
597-
'--external:aws-sdk --tsconfig=/asset-input/lib/custom-tsconfig.ts',
598-
].join(' '),
599-
],
600-
}),
601-
});
602-
603-
Bundling.bundle({
604-
entry,
605-
projectRoot,
606-
depsLockFilePath,
607-
runtime: Runtime.NODEJS_14_X,
608-
forceDockerBundling: true,
609-
tsconfig,
610-
preCompilation: true,
611-
architecture: Architecture.X86_64,
612-
});
613-
614-
// Correctly bundles with esbuild
615-
expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(depsLockFilePath), {
616-
assetHashType: AssetHashType.OUTPUT,
617-
bundling: expect.objectContaining({
618-
command: [
619-
'bash', '-c',
620-
[
621-
'esbuild --bundle \"/asset-input/lib/handler.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"',
622-
'--external:aws-sdk --tsconfig=/asset-input/lib/custom-tsconfig.ts',
623-
].join(' '),
624-
],
625-
}),
626-
});
627-
628-
});
629-
630-
test('esbuild bundling with pre compilations with undefined tsconfig ( Should find in root directory )', () => {
631-
Bundling.clearTscCompilationCache();
632576
const packageLock = path.join(__dirname, '..', 'package-lock.json');
633577

634578
Bundling.bundle({
635579
entry: __filename.replace('.js', '.ts'),
636580
projectRoot: path.dirname(packageLock),
637581
depsLockFilePath: packageLock,
638582
runtime: Runtime.NODEJS_14_X,
639-
forceDockerBundling: true,
640583
preCompilation: true,
584+
forceDockerBundling: true,
641585
architecture: Architecture.X86_64,
642586
});
643587

588+
const compilerOptions = util.getTsconfigCompilerOptions(path.join(__dirname, '..', 'tsconfig.json'));
589+
644590
// Correctly bundles with esbuild
645591
expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(packageLock), {
646592
assetHashType: AssetHashType.OUTPUT,
647593
bundling: expect.objectContaining({
648594
command: [
649595
'bash', '-c',
650596
[
651-
'tsc --project /asset-input/tsconfig.json --rootDir ./ --outDir ./ &&',
652-
'esbuild --bundle \"/asset-input/test/bundling.test.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"',
653-
'--external:aws-sdk',
597+
`tsc \"/asset-input/test/bundling.test.ts\" ${compilerOptions} &&`,
598+
'esbuild --bundle \"/asset-input/test/bundling.test.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\" --external:aws-sdk',
654599
].join(' '),
655600
],
656601
}),
657602
});
658603
});
659604

660-
test('esbuild bundling with pre compilations and undefined tsconfig ( Should throw) ', () => {
605+
test('throws with pre compilation and not found tsconfig', () => {
661606
expect(() => {
662607
Bundling.bundle({
663608
entry,
@@ -668,7 +613,7 @@ test('esbuild bundling with pre compilations and undefined tsconfig ( Should thr
668613
preCompilation: true,
669614
architecture: Architecture.X86_64,
670615
});
671-
}).toThrow('Cannot find a tsconfig.json, please specify the prop: tsconfig');
616+
}).toThrow('Cannot find a `tsconfig.json` but `preCompilation` is set to `true`, please specify it via `tsconfig`');
672617

673618
});
674619

Diff for: packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as child_process from 'child_process';
22
import * as fs from 'fs';
33
import * as path from 'path';
4-
import { callsites, exec, extractDependencies, findUp, findUpMultiple } from '../lib/util';
4+
import { callsites, exec, extractDependencies, findUp, findUpMultiple, getTsconfigCompilerOptions } from '../lib/util';
55

66
beforeEach(() => {
77
jest.clearAllMocks();
@@ -179,3 +179,18 @@ describe('extractDependencies', () => {
179179
fs.unlinkSync(pkgPath);
180180
});
181181
});
182+
183+
describe('getTsconfigCompilerOptions', () => {
184+
test('should extract compiler options and returns as string', () => {
185+
const tsconfig = path.join(__dirname, '..', 'tsconfig.json');
186+
const compilerOptions = getTsconfigCompilerOptions(tsconfig);
187+
expect(compilerOptions).toEqual([
188+
'--alwaysStrict --charset utf8 --declaration --experimentalDecorators',
189+
'--inlineSourceMap --inlineSources --lib es2019 --module CommonJS',
190+
'--newLine lf --noEmitOnError --noFallthroughCasesInSwitch --noImplicitAny',
191+
'--noImplicitReturns --noImplicitThis --noUnusedLocals --noUnusedParameters',
192+
'--resolveJsonModule --strict --strictNullChecks --strictPropertyInitialization',
193+
'--target ES2019 --rootDir ./ --outDir ./',
194+
].join(' '));
195+
});
196+
});

0 commit comments

Comments
 (0)