Skip to content

Commit 215c355

Browse files
fix
1 parent 572aedb commit 215c355

File tree

3 files changed

+88
-64
lines changed

3 files changed

+88
-64
lines changed

src/cloudFormation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ async function getCloudFormationResources(
139139
async function getLambdasInStack(
140140
stackName: string,
141141
awsConfiguration: AwsConfiguration,
142+
stackLogicalId?: string,
142143
): Promise<
143144
Array<{
144145
lambdaName: string;
@@ -164,7 +165,7 @@ async function getLambdasInStack(
164165
return {
165166
lambdaName: resource.PhysicalResourceId!,
166167
logicalId: resource.LogicalResourceId!,
167-
stackName: stackName,
168+
stackName: stackLogicalId ?? stackName,
168169
};
169170
}) ?? [];
170171

@@ -175,6 +176,7 @@ async function getLambdasInStack(
175176
const lambdasInNestedStack = await getLambdasInStack(
176177
nestedStack.PhysicalResourceId,
177178
awsConfiguration,
179+
nestedStack.LogicalResourceId,
178180
);
179181

180182
return lambdasInNestedStack;

src/frameworks/samFramework.ts

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { CloudFormation } from '../cloudFormation.js';
1010
import { AwsConfiguration } from '../types/awsConfiguration.js';
1111
import { LldConfigBase } from '../types/lldConfig.js';
1212
import { Logger } from '../logger.js';
13+
import type { Format } from 'esbuild';
1314

1415
/**
1516
* Support for AWS SAM framework
@@ -102,19 +103,10 @@ export class SamFramework implements IFramework {
102103
throw new Error(`Stack name not found in ${samConfigFile}`);
103104
}
104105

105-
const samTemplateContent = await fs.readFile(
106-
path.resolve(samTemplateFile),
107-
'utf-8',
108-
);
109-
const template = yaml.parse(samTemplateContent);
110-
111-
const lambdas: any[] = [];
112-
113106
// Recursively parse templates to find all Lambda functions, including those in nested stacks
114-
await this.parseLambdasFromTemplate(
115-
template,
116-
path.dirname(path.resolve(samTemplateFile)),
117-
lambdas,
107+
const lambdas = await this.parseLambdasFromTemplate(
108+
samTemplateFile,
109+
stackName,
118110
);
119111

120112
const lambdasDiscovered: LambdaResource[] = [];
@@ -141,37 +133,32 @@ export class SamFramework implements IFramework {
141133

142134
// get tags for each Lambda
143135
for (const func of lambdas) {
144-
const handlerFull = path.join(
145-
func.Properties.CodeUri ?? '',
146-
func.Properties.Handler,
147-
);
136+
const handlerFull = path.join(func.codeUri ?? '', func.handler);
148137
const handlerParts = handlerFull.split('.');
149138
const handler = handlerParts[1];
150139

151140
const functionName = lambdasInStack.find(
152-
(lambda) => lambda.logicalId === func.Name,
141+
(lambda) =>
142+
lambda.logicalId === func.name && lambda.stackName === func.stackName,
153143
)?.lambdaName;
154144

155145
if (!functionName) {
156-
throw new Error(`Function name not found for function: ${func.Name}`);
146+
throw new Error(`Function name not found for function: ${func.name}`);
157147
}
158148

159149
let esBuildOptions: EsBuildOptions | undefined = undefined;
160150

161151
let codePath: string | undefined;
162-
if (func.Metadata?.BuildMethod?.toLowerCase() === 'esbuild') {
163-
if (func.Metadata?.BuildProperties?.EntryPoints?.length > 0) {
164-
codePath = path.join(
165-
func.Properties.CodeUri ?? '',
166-
func.Metadata?.BuildProperties?.EntryPoints[0],
167-
);
152+
if (func.buildMethod?.toLowerCase() === 'esbuild') {
153+
if (func.entryPoints && func.entryPoints.length > 0) {
154+
codePath = path.join(func.codeUri ?? '', func.entryPoints[0]);
168155
}
169156

170157
esBuildOptions = {
171-
external: func.Metadata?.BuildProperties?.External,
172-
minify: func.Metadata?.BuildProperties?.Minify,
173-
format: func.Metadata?.BuildProperties?.Format,
174-
target: func.Metadata?.BuildProperties?.Target,
158+
external: func.external,
159+
minify: func.minify,
160+
format: func.format,
161+
target: func.target,
175162
};
176163
}
177164

@@ -228,62 +215,95 @@ export class SamFramework implements IFramework {
228215

229216
/**
230217
* Recursively parse templates to find all Lambda functions, including nested stacks
231-
* @param template The parsed CloudFormation/SAM template
232-
* @param templateDir The directory containing the template file
233-
* @param lambdas The array to collect Lambda functions into
218+
* @param templatePath The path to the CloudFormation/SAM template file
219+
* @param stackName The name of the stack this template belongs to (for nested stacks)
234220
*/
235221
private async parseLambdasFromTemplate(
236-
template: any,
237-
templateDir: string,
238-
lambdas: any[],
239-
): Promise<void> {
222+
templatePath: string,
223+
stackName: string,
224+
): Promise<ParsedLambda[]> {
225+
const resolvedTemplatePath = path.resolve(templatePath);
226+
const templateDir = path.dirname(resolvedTemplatePath);
227+
228+
let template: any;
229+
try {
230+
const templateContent = await fs.readFile(resolvedTemplatePath, 'utf-8');
231+
template = yaml.parse(templateContent);
232+
} catch (err: any) {
233+
Logger.warn(
234+
`[SAM] Could not read or parse template at ${templatePath}: ${err.message}`,
235+
);
236+
return [];
237+
}
238+
240239
if (!template.Resources) {
241-
return;
240+
return [];
242241
}
243242

243+
const lambdas: ParsedLambda[] = [];
244+
244245
for (const resourceName in template.Resources) {
245246
const resource = template.Resources[resourceName];
246247

247248
// Check if it's a Lambda function
248249
if (resource.Type === 'AWS::Serverless::Function') {
249250
lambdas.push({
250-
Name: resourceName,
251-
...resource,
251+
templatePath,
252+
name: resourceName,
253+
codeUri: resource.Properties?.CodeUri,
254+
handler: resource.Properties?.Handler,
255+
buildMethod: resource.Metadata?.BuildMethod,
256+
entryPoints: resource.Metadata?.BuildProperties?.EntryPoints,
257+
external: resource.Metadata?.BuildProperties?.External,
258+
minify: resource.Metadata?.BuildProperties?.Minify,
259+
format: resource.Metadata?.BuildProperties?.Format as
260+
| Format
261+
| undefined,
262+
target: resource.Metadata?.BuildProperties?.Target,
263+
stackName,
252264
});
253265
}
254266
// Check if it's a nested stack
255267
else if (
256268
resource.Type === 'AWS::Serverless::Application' ||
257269
resource.Type === 'AWS::CloudFormation::Stack'
258270
) {
259-
const nestedTemplateLocation = resource.Properties?.Location;
271+
const nestedTemplateLocation =
272+
resource.Properties?.Location ?? resource.Properties?.TemplateURL;
260273
if (nestedTemplateLocation) {
261-
try {
262-
const nestedTemplatePath = path.resolve(
263-
templateDir,
264-
nestedTemplateLocation,
265-
);
266-
const nestedTemplateContent = await fs.readFile(
267-
nestedTemplatePath,
268-
'utf-8',
269-
);
270-
const nestedTemplate = yaml.parse(nestedTemplateContent);
271-
272-
// Recursively parse the nested template
273-
await this.parseLambdasFromTemplate(
274-
nestedTemplate,
275-
path.dirname(nestedTemplatePath),
276-
lambdas,
277-
);
278-
} catch (err: any) {
279-
Logger.warn(
280-
`[SAM] Could not parse nested template at ${nestedTemplateLocation}: ${err.message}`,
281-
);
282-
}
274+
const nestedTemplatePath = path.resolve(
275+
templateDir,
276+
nestedTemplateLocation,
277+
);
278+
279+
// Recursively parse the nested template
280+
const nestedLambdas = await this.parseLambdasFromTemplate(
281+
nestedTemplatePath,
282+
resourceName,
283+
);
284+
lambdas.push(...nestedLambdas);
283285
}
284286
}
285287
}
288+
289+
Logger.verbose(JSON.stringify(lambdas, null, 2));
290+
291+
return lambdas;
286292
}
287293
}
288294

289295
export const samFramework = new SamFramework();
296+
297+
type ParsedLambda = {
298+
templatePath: string;
299+
name: string;
300+
codeUri?: string;
301+
handler: string;
302+
buildMethod?: string;
303+
entryPoints?: string[];
304+
external?: string[];
305+
minify?: boolean;
306+
format?: Format;
307+
target?: string;
308+
stackName: string;
309+
};

test/sam-basic/nested-stack.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ Transform:
66
- AWS::Serverless-2016-10-31
77

88
Resources:
9+
# Using AWS::CloudFormation::Stack instead of AWS::Serverless::Application
10+
# to test nested stack support with native CloudFormation resource type
911
NestedNestedStack:
10-
Type: AWS::Serverless::Application
12+
Type: AWS::CloudFormation::Stack
1113
Properties:
12-
Location: nested-nested-stack.yaml
14+
TemplateURL: nested-nested-stack.yaml
1315

1416
testTsCommonJs:
1517
Type: AWS::Serverless::Function

0 commit comments

Comments
 (0)