diff --git a/src/code.ts b/src/code.ts index c56b5eb8..acae5863 100644 --- a/src/code.ts +++ b/src/code.ts @@ -10,7 +10,7 @@ import { } from './asset'; import { EntryPoints } from './bundler'; import { BuildOptions } from './esbuild-types'; -import { defaultPlatformProps } from './private/utils'; +import { defaultPlatformProps, uniqueAssetId } from './private/utils'; export { CodeConfig } from 'aws-cdk-lib/aws-lambda'; export interface JavaScriptCodeProps extends AssetBaseProps {}; @@ -29,7 +29,7 @@ export class EsbuildCode< protected getAsset(scope: Construct): EsbuildAsset { return new EsbuildAsset( scope, - this.constructor.name, + uniqueAssetId(scope, this.constructor.name), this.props, ); } @@ -137,7 +137,7 @@ export class JavaScriptCode extends EsbuildCode { protected getAsset(scope: Construct): EsbuildAsset { return new JSAsset( scope, - this.constructor.name, + uniqueAssetId(scope, this.constructor.name), this.props, ); } @@ -186,7 +186,7 @@ export class TypeScriptCode extends EsbuildCode { protected getAsset(scope: Construct): EsbuildAsset { return new TSAsset( scope, - this.constructor.name, + uniqueAssetId(scope, this.constructor.name), this.props, ); } diff --git a/src/private/utils.ts b/src/private/utils.ts index 1d2d121c..2291fdb5 100644 --- a/src/private/utils.ts +++ b/src/private/utils.ts @@ -1,3 +1,4 @@ +import { IConstruct } from 'constructs'; import { BuildOptions, Platform, TransformOptions } from '../esbuild-types'; export function isEsbuildError(error: unknown): boolean { @@ -22,3 +23,16 @@ export function defaultPlatformProps(options?: BuildOptions | TransformOptions): return {}; } + +const assetIds = new WeakMap(); +export const uniqueAssetId = (scope: IConstruct, name: string) => { + const nextId = (assetIds.get(scope) ?? 0) + 1; + assetIds.set(scope, nextId); + + // Only one asset per scope, skip the id + if (nextId === 1) { + return name; + } + + return `${name}${nextId}`; +}; diff --git a/src/source.ts b/src/source.ts index 9389264f..757c44e5 100644 --- a/src/source.ts +++ b/src/source.ts @@ -8,6 +8,8 @@ import { Construct } from 'constructs'; import { AssetBaseProps, AssetProps, JavaScriptAsset, TypeScriptAsset } from './asset'; import { EntryPoints } from './bundler'; import { BuildOptions } from './esbuild-types'; +import { uniqueAssetId } from './private/utils'; + export interface JavaScriptSourceProps extends AssetBaseProps{}; export interface TypeScriptSourceProps extends AssetBaseProps{}; @@ -75,7 +77,7 @@ abstract class Source< if (!this.asset) { this.asset = new this.assetClass( scope, - this.constructor.name, + uniqueAssetId(scope, this.constructor.name), this.props, ); } else if (Stack.of(this.asset) !== Stack.of(scope)) { diff --git a/test/code.test.ts b/test/code.test.ts index 1d52c4ef..2dd97847 100644 --- a/test/code.test.ts +++ b/test/code.test.ts @@ -202,3 +202,21 @@ describe('Amazon CloudWatch Synthetics', () => { }); }); }); + + +describe('multiple Code in the same scope', () => { + it('does not throw', () => { + const stack = new Stack(); + + const codeOne = new TypeScriptCode('fixtures/handlers/ts-handler.ts', { + buildOptions: { absWorkingDir: resolve(__dirname) }, + }); + + const codeTwo = new TypeScriptCode('fixtures/handlers/ts-handler.ts', { + buildOptions: { absWorkingDir: resolve(__dirname) }, + }); + + codeOne.bind(stack); + codeTwo.bind(stack); + }); +}); diff --git a/test/source.test.ts b/test/source.test.ts index 7b195aeb..4e38b341 100644 --- a/test/source.test.ts +++ b/test/source.test.ts @@ -176,4 +176,36 @@ describe('source', () => { expect(source.props.buildOptions.platform).toBe('browser'); }); }); + + describe('with multiple sources in the same scope', () => { + it('does not throw', () => { + const stack = new Stack(); + + const sourceOne = new TypeScriptSource('fixtures/handlers/ts-handler.ts', { + buildOptions: { + absWorkingDir: resolve(__dirname), + }, + }); + const sourceTwo = new TypeScriptSource('fixtures/handlers/ts-handler.ts', { + buildOptions: { + absWorkingDir: resolve(__dirname), + }, + }); + + const destinationBucket = new Bucket(stack, 'WebsiteBucket', { + autoDeleteObjects: true, + publicReadAccess: true, + removalPolicy: RemovalPolicy.DESTROY, + websiteIndexDocument: 'index.html', + }); + + new BucketDeployment(stack, 'MultipleAssets', { + destinationBucket, + sources: [ + sourceOne, + sourceTwo, + ], + }); + }); + }); });