Skip to content

Commit 1c393a4

Browse files
Merge pull request #56 from ServerlessLife/cdk-invalid-path
fix: CDK error Cannot find entry file
2 parents ea9a183 + c7170b8 commit 1c393a4

File tree

14 files changed

+253
-277
lines changed

14 files changed

+253
-277
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ If you have a new feature idea, please create and issue.
324324

325325
(alphabetical)
326326

327+
- [Roger Chi](https://rogerchi.com/)
327328
- ⭐ Your name here for notable code or documentation contributions or sample projects submitted with a bug report that resulted in tool improvement.
328329

329330
## Disclaimer

src/frameworks/cdkFramework.ts

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -212,26 +212,83 @@ export class CdkFramework implements IFramework {
212212
config: LldConfigBase,
213213
) {
214214
const entryFile = await this.getCdkEntryFile(cdkConfigPath);
215-
// Define a plugin to prepend custom code to .ts or .tsx files
215+
let isESM = false;
216+
const packageJsonPath = await findPackageJson(entryFile);
217+
218+
if (packageJsonPath) {
219+
try {
220+
const packageJson = JSON.parse(
221+
await fs.readFile(packageJsonPath, { encoding: 'utf-8' }),
222+
);
223+
if (packageJson.type === 'module') {
224+
isESM = true;
225+
Logger.verbose(`[CDK] Using ESM format`);
226+
}
227+
} catch (err: any) {
228+
Logger.error(
229+
`Error reading CDK package.json (${packageJsonPath}): ${err.message}`,
230+
err,
231+
);
232+
}
233+
}
234+
235+
const rootDir = process.cwd();
236+
237+
// Plugin that:
238+
// - Fixes __dirname issues
239+
// - Injects code to get the file path of the Lambda function and CDK hierarchy
216240
const injectCodePlugin: esbuild.Plugin = {
217241
name: 'injectCode',
218242
setup(build: esbuild.PluginBuild) {
219243
build.onLoad({ filter: /.*/ }, async (args: esbuild.OnLoadArgs) => {
220-
const absolutePath = path.resolve(args.path);
244+
// fix __dirname issues
245+
const isWindows = /^win/.test(process.platform);
246+
const esc = (p: string) => (isWindows ? p.replace(/\\/g, '/') : p);
247+
248+
const variables = `
249+
const __fileloc = {
250+
filename: "${esc(args.path)}",
251+
dirname: "${esc(path.dirname(args.path))}",
252+
relativefilename: "${esc(path.relative(rootDir, args.path))}",
253+
relativedirname: "${esc(
254+
path.relative(rootDir, path.dirname(args.path)),
255+
)}",
256+
import: { meta: { url: "file://${esc(args.path)}" } }
257+
};
258+
`;
259+
260+
let fileContent = new TextDecoder().decode(
261+
await fs.readFile(args.path),
262+
);
221263

222-
let source = await fs.readFile(absolutePath, 'utf8');
264+
// remove shebang
265+
if (fileContent.startsWith('#!')) {
266+
const firstNewLine = fileContent.indexOf('\n');
267+
fileContent = fileContent.slice(firstNewLine + 1);
268+
}
269+
270+
let contents: string;
271+
if (args.path.endsWith('.ts') || args.path.endsWith('.js')) {
272+
// add the variables at the top of the file, that contains the file location
273+
contents = `${variables}\n${fileContent}`;
274+
} else {
275+
contents = fileContent;
276+
}
223277

278+
const loader = args.path.split('.').pop() as esbuild.Loader;
279+
280+
// Inject code to get the file path of the Lambda function and CDK hierarchy
224281
if (args.path.includes('aws-cdk-lib/aws-lambda/lib/function.')) {
225282
const codeToFind =
226283
'try{jsiiDeprecationWarnings().aws_cdk_lib_aws_lambda_FunctionProps(props)}';
227284

228-
if (!source.includes(codeToFind)) {
285+
if (!contents.includes(codeToFind)) {
229286
throw new Error(`Can not find code to inject in ${args.path}`);
230287
}
231288

232289
// Inject code to get the file path of the Lambda function and CDK hierarchy
233290
// path to match it with the Lambda function. Store data in the global variable.
234-
source = source.replace(
291+
contents = contents.replace(
235292
codeToFind,
236293
`;
237294
global.lambdas = global.lambdas ?? [];
@@ -264,55 +321,28 @@ export class CdkFramework implements IFramework {
264321
) {
265322
const codeToFind = 'super(scope,id),this.requestDestinationArn=!1;';
266323

267-
if (!source.includes(codeToFind)) {
324+
if (!contents.includes(codeToFind)) {
268325
throw new Error(`Can not find code to inject in ${args.path}`);
269326
}
270327

271328
// Inject code to prevent deploying the assets
272-
source = source.replace(codeToFind, codeToFind + `return;`);
329+
contents = contents.replace(codeToFind, codeToFind + `return;`);
273330
}
274331

275332
return {
276-
contents: source,
277-
loader: 'default',
333+
contents,
334+
loader,
278335
};
279336
});
280337
},
281338
};
282339

283-
let isESM = false;
284-
// get packgage.json
285-
const packageJsonPath = await findPackageJson(entryFile);
286-
287-
if (packageJsonPath) {
288-
try {
289-
const packageJson = JSON.parse(
290-
await fs.readFile(packageJsonPath, { encoding: 'utf-8' }),
291-
);
292-
if (packageJson.type === 'module') {
293-
isESM = true;
294-
Logger.verbose(`[CDK] Using ESM format`);
295-
}
296-
} catch (err: any) {
297-
Logger.error(
298-
`Error reading CDK package.json (${packageJsonPath}): ${err.message}`,
299-
err,
300-
);
301-
}
302-
}
303-
304340
const compileOutput = path.join(
305341
getProjectDirname(),
306342
outputFolder,
307343
`compiledCdk.${isESM ? 'mjs' : 'cjs'}`,
308344
);
309345

310-
const dirname = path.join(
311-
...([getProjectDirname(), config.subfolder, 'x'].filter(
312-
(p) => p,
313-
) as string[]),
314-
);
315-
316346
try {
317347
// Build CDK code
318348
await esbuild.build({
@@ -331,7 +361,6 @@ export class CdkFramework implements IFramework {
331361
banner: {
332362
js: [
333363
`import { createRequire as topLevelCreateRequire } from 'module';`,
334-
`import.meta.url = 'file:///${dirname}/cdkFrameworkWorker.mjs';`,
335364
`global.require = global.require ?? topLevelCreateRequire(import.meta.url);`,
336365
`import { fileURLToPath as topLevelFileUrlToPath, URL as topLevelURL } from "url"`,
337366
`global.__dirname = global.__dirname ?? topLevelFileUrlToPath(new topLevelURL(".", import.meta.url))`,
@@ -341,10 +370,15 @@ export class CdkFramework implements IFramework {
341370
: {
342371
format: 'cjs',
343372
target: 'node18',
344-
banner: {
345-
js: [`__dirname = '${dirname}';`].join('\n'),
346-
},
347373
}),
374+
define: {
375+
// replace __dirname,... with the a variable that contains the file location
376+
__filename: '__fileloc.filename',
377+
__dirname: '__fileloc.dirname',
378+
__relativefilename: '__fileloc.relativefilename',
379+
__relativedirname: '__fileloc.relativedirname',
380+
'import.meta.url': '__fileloc.import.meta.url',
381+
},
348382
});
349383
} catch (error: any) {
350384
throw new Error(`Error building CDK code: ${error.message}`, {

src/frameworks/cdkFrameworkWorker.mjs

Lines changed: 1 addition & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
// @ts-nocheck
2-
import { createRequire as topLevelCreateRequire } from 'module';
3-
const require = topLevelCreateRequire(import.meta.url);
4-
import path from 'path';
5-
62
import { workerData, parentPort } from 'node:worker_threads';
7-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
8-
import fs from 'fs/promises'; // do not delete this line
9-
103
import { Logger } from '../logger.mjs';
114

125
Logger.setVerbose(workerData.verbose);
@@ -21,7 +14,6 @@ parentPort.on('message', async (data) => {
2114
Logger.verbose(`[Worker ${workerData.workerId}] Received message`, data);
2215

2316
// execute code to get the data into global.lambdas
24-
await fixCdkPaths(workerData.awsCdkLibPath);
2517
await import(data.compileOutput);
2618

2719
if (!global.lambdas || global.lambdas?.length === 0) {
@@ -43,7 +35,7 @@ parentPort.on('message', async (data) => {
4335
}));
4436

4537
Logger.verbose(
46-
`[CDK] [Worker] Sending found lambdas`,
38+
`[CDK] [Worker] Sending found Lambdas`,
4739
JSON.stringify(lambdas, null, 2),
4840
);
4941
parentPort.postMessage(lambdas);
@@ -53,91 +45,6 @@ parentPort.on('message', async (data) => {
5345
}
5446
});
5547

56-
/**
57-
* Some paths are not resolved correctly in the CDK code, so we need to fix them
58-
*/
59-
async function fixCdkPaths(awsCdkLibPath) {
60-
// leave this lines for manual debugging
61-
//const awsCdkLibPath = path.resolve("node_modules/aws-cdk-lib");
62-
//const path = require("path");
63-
64-
Logger.verbose(`[CDK] [Worker] aws-cdk-lib PATH ${awsCdkLibPath}`);
65-
66-
const pathsFix = {
67-
'custom-resource-handlers/': `${awsCdkLibPath}/custom-resource-handlers/`,
68-
'aws-custom-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/custom-resources/aws-custom-resource-handler`,
69-
'auto-delete-objects-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3/auto-delete-objects-handler`,
70-
'notifications-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3/notifications-resource-handler`,
71-
'drop-spam-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ses/drop-spam-handler`,
72-
'aws-stepfunctions-tasks/role-policy-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-handler`,
73-
'eval-nodejs-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-handler`,
74-
'cross-account-zone-delegation-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-route53/cross-account-zone-delegation-handler`,
75-
'delete-existing-record-set-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-route53/delete-existing-record-set-handler`,
76-
'aws-api-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-events-targets/aws-api-handler`,
77-
'log-retention-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-logs/log-retention-handler`,
78-
'cluster-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-eks/cluster-resource-handler`,
79-
'auto-delete-images-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ecr/auto-delete-images-handler`,
80-
'bucket-deployment-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-handler`,
81-
'nodejs-entrypoint-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/core/nodejs-entrypoint-handler`,
82-
'restrict-default-security-group-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ec2/restrict-default-security-group-handler`,
83-
'dns-validated-certificate-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-certificatemanager/dns-validated-certificate-handler`,
84-
'auto-delete-underlying-resources-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-synthetics/auto-delete-underlying-resources-handler`,
85-
'replica-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-dynamodb/replica-handler`,
86-
'oidc-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-iam/oidc-handler`,
87-
};
88-
89-
// Create a proxy to intercept calls to the path module so we can fix paths
90-
const pathProxy = new Proxy(path, {
91-
get(target, prop) {
92-
if (typeof target[prop] === 'function') {
93-
return function (...args) {
94-
if (prop === 'resolve') {
95-
let resolvedPath = target[prop].apply(target, args);
96-
97-
for (const [key, value] of Object.entries(pathsFix)) {
98-
if (resolvedPath.includes(key)) {
99-
// replace the beginning of the path with the value
100-
const i = resolvedPath.indexOf(key);
101-
const newResolvedPath = `${value}${resolvedPath.substring(i + key.length)}`;
102-
Logger.verbose(
103-
`[CDK] [Worker] Fixing path ${resolvedPath} -> ${newResolvedPath}`,
104-
);
105-
resolvedPath = newResolvedPath;
106-
}
107-
}
108-
109-
return resolvedPath;
110-
}
111-
if (prop === 'join') {
112-
let resolvedPath = target[prop].apply(target, args);
113-
114-
for (const [key, value] of Object.entries(pathsFix)) {
115-
if (resolvedPath.includes(key)) {
116-
// replace the beginning of the path with the value
117-
const i = resolvedPath.indexOf(key);
118-
const newResolvedPath = `${value}${resolvedPath.substring(i + key.length)}`;
119-
Logger.verbose(
120-
`[CDK] [Worker] Fixing path ${resolvedPath} -> ${newResolvedPath}`,
121-
);
122-
resolvedPath = newResolvedPath;
123-
}
124-
}
125-
126-
return resolvedPath;
127-
}
128-
return target[prop].apply(target, args);
129-
};
130-
}
131-
return target[prop];
132-
},
133-
});
134-
135-
// Override the path module in the require cache
136-
require.cache[require.resolve('path')] = {
137-
exports: pathProxy,
138-
};
139-
}
140-
14148
process.on('unhandledRejection', (error) => {
14249
Logger.error(`[CDK] [Worker] Unhandled Rejection`, error);
14350
});

src/nodeWorkerRunner.mjs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ Logger.verbose(
1313

1414
parentPort.on('message', async (data) => {
1515
Logger.verbose(`[Worker ${workerData.workerId}] Received message`, data);
16-
const mod = await import(workerData.artifactFile);
17-
const fn = mod[workerData.handler];
16+
try {
17+
const mod = await import(workerData.artifactFile);
18+
const fn = mod[workerData.handler];
1819

19-
if (!fn) {
20-
throw new Error(
21-
`Handler '${workerData.handler}' not found for function '${workerData.functionId}'`,
22-
);
23-
}
20+
if (!fn) {
21+
throw new Error(
22+
`Handler '${workerData.handler}' not found for function '${workerData.functionId}'`,
23+
);
24+
}
2425

25-
try {
2626
const context = {
2727
...data.context,
2828
getRemainingTimeInMillis: () => 2147483647, // Max 32-bit signed integer

test/cdk-basic.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ async function getFunctionName(folder: string, functionName: string) {
153153
const cdkOutputs = JSON.parse(
154154
await fs.readFile(path.join(folder, 'cdk-outputs.json'), 'utf-8'),
155155
);
156-
const lambdaName = cdkOutputs['test-lld-cdk-basic'][functionName];
156+
let lambdaName = cdkOutputs['test-lld-cdk-basic'][functionName];
157+
158+
if (!lambdaName) {
159+
lambdaName = cdkOutputs['test-lld-cdk-basic2'][functionName];
160+
}
161+
162+
if (!lambdaName) {
163+
throw new Error(
164+
`Lambda name not found for ${functionName} in cdk-outputs.json`,
165+
);
166+
}
167+
157168
return lambdaName;
158169
}

test/cdk-basic/bin/cdk-basic.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,16 @@
22
import 'source-map-support/register';
33
import * as cdk from 'aws-cdk-lib';
44
import { CdkbasicStack } from '../lib/cdk-basic-stack';
5+
import { CdkbasicStack2 } from '../lib/subfolder/cdk-basic-stack2';
56

67
const app = new cdk.App();
78

89
const environment = 'test';
910

1011
new CdkbasicStack(app, 'CdkbasicStack', {
1112
stackName: `${environment}-lld-cdk-basic`,
12-
/* If you don't specify 'env', this stack will be environment-agnostic.
13-
* Account/Region-dependent features and context lookups will not work,
14-
* but a single synthesized template can be deployed anywhere. */
15-
16-
/* Uncomment the next line to specialize this stack for the AWS Account
17-
* and Region that are implied by the current CLI configuration. */
18-
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
19-
20-
/* Uncomment the next line if you know exactly what Account and Region you
21-
* want to deploy the stack to. */
22-
// env: { account: '123456789012', region: 'us-east-1' },
13+
});
2314

24-
/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
15+
new CdkbasicStack2(app, 'CdkbasicStack2', {
16+
stackName: `${environment}-lld-cdk-basic2`,
2517
});

0 commit comments

Comments
 (0)