@bifravst/aws-cdk-lambda-helpers 1.10.59 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/LambdaSource.d.ts +1 -1
- package/dist/src/LambdaSource.js +1 -1
- package/dist/src/checksumOfFiles.js +5 -5
- package/dist/src/findDependencies.d.ts +6 -1
- package/dist/src/findDependencies.js +56 -15
- package/dist/src/findDependencies.spec.d.ts +1 -0
- package/dist/src/findDependencies.spec.js +16 -0
- package/dist/src/module-folder-named-like-handler-bug.spec.js +4 -4
- package/dist/src/packLambda.d.ts +8 -4
- package/dist/src/packLambda.js +14 -9
- package/dist/src/packLambdaFromPath.d.ts +17 -1
- package/dist/src/packLambdaFromPath.js +8 -12
- package/dist/src/packLayer.d.ts +3 -3
- package/dist/src/packLayer.js +3 -3
- package/package.json +2 -2
- package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/acme/lib.d.ts +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/acme/lib.js +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/lambda/acme.d.ts +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/lambda/acme.js +0 -2
- package/dist/src/test-data/module-folder-named-like-handler-bug/same-level/acme/lib.d.ts +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/same-level/acme/lib.js +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/same-level/acme.d.ts +0 -1
- package/dist/src/test-data/module-folder-named-like-handler-bug/same-level/acme.js +0 -2
@@ -3,5 +3,5 @@ import { Construct } from 'constructs';
|
|
3
3
|
import type { PackedLambda } from './packLambda.js';
|
4
4
|
export declare class LambdaSource extends Construct {
|
5
5
|
readonly code: Lambda.S3Code;
|
6
|
-
constructor(parent: Construct, packedLambda: Pick<PackedLambda, '
|
6
|
+
constructor(parent: Construct, packedLambda: Pick<PackedLambda, 'zipFilePath' | 'id' | 'hash'>);
|
7
7
|
}
|
package/dist/src/LambdaSource.js
CHANGED
@@ -5,7 +5,7 @@ export class LambdaSource extends Construct {
|
|
5
5
|
constructor(parent, packedLambda) {
|
6
6
|
super(parent, `${packedLambda.id}Source`);
|
7
7
|
const asset = new S3Assets.Asset(this, 'asset', {
|
8
|
-
path: packedLambda.
|
8
|
+
path: packedLambda.zipFilePath,
|
9
9
|
assetHash: packedLambda.hash,
|
10
10
|
assetHashType: AssetHashType.CUSTOM,
|
11
11
|
});
|
@@ -14,12 +14,12 @@ export const checkSumOfStrings = (strings) => {
|
|
14
14
|
return hash.digest('hex');
|
15
15
|
};
|
16
16
|
const hashCache = {};
|
17
|
-
const hashFile = async (
|
18
|
-
if (hashCache[
|
19
|
-
hashCache[
|
17
|
+
const hashFile = async (filePath) => {
|
18
|
+
if (hashCache[filePath] === undefined) {
|
19
|
+
hashCache[filePath] = await new Promise((resolve) => {
|
20
20
|
const hash = crypto.createHash('sha1');
|
21
21
|
hash.setEncoding('hex');
|
22
|
-
const fileStream = fs.createReadStream(
|
22
|
+
const fileStream = fs.createReadStream(filePath);
|
23
23
|
fileStream.pipe(hash, { end: false });
|
24
24
|
fileStream.on('end', () => {
|
25
25
|
hash.end();
|
@@ -28,7 +28,7 @@ const hashFile = async (file) => {
|
|
28
28
|
});
|
29
29
|
});
|
30
30
|
}
|
31
|
-
return hashCache[
|
31
|
+
return hashCache[filePath];
|
32
32
|
};
|
33
33
|
/**
|
34
34
|
* Computes the checksum for the given files
|
@@ -1,4 +1,9 @@
|
|
1
1
|
/**
|
2
2
|
* Resolve project-level dependencies for the given file using TypeScript compiler API
|
3
3
|
*/
|
4
|
-
export declare const findDependencies: (
|
4
|
+
export declare const findDependencies: ({ sourceFilePath, tsConfigFilePath, imports: importsArg, visited: visitedArg, }: {
|
5
|
+
sourceFilePath: string;
|
6
|
+
tsConfigFilePath?: string;
|
7
|
+
imports?: string[];
|
8
|
+
visited?: string[];
|
9
|
+
}) => string[];
|
@@ -4,25 +4,26 @@ import ts, {} from 'typescript';
|
|
4
4
|
/**
|
5
5
|
* Resolve project-level dependencies for the given file using TypeScript compiler API
|
6
6
|
*/
|
7
|
-
export const findDependencies = (
|
8
|
-
|
7
|
+
export const findDependencies = ({ sourceFilePath, tsConfigFilePath, imports: importsArg, visited: visitedArg, }) => {
|
8
|
+
const visited = visitedArg ?? [];
|
9
|
+
const imports = importsArg ?? [];
|
10
|
+
if (visited.includes(sourceFilePath))
|
9
11
|
return imports;
|
10
|
-
const
|
12
|
+
const tsConfig = tsConfigFilePath !== undefined
|
13
|
+
? JSON.parse(readFileSync(tsConfigFilePath, 'utf-8').toString())
|
14
|
+
: undefined;
|
15
|
+
const fileNode = ts.createSourceFile(sourceFilePath, readFileSync(sourceFilePath, 'utf-8').toString(), ts.ScriptTarget.ES2022,
|
11
16
|
/*setParentNodes */ true);
|
12
17
|
const parseChild = (node) => {
|
13
18
|
if (node.kind !== ts.SyntaxKind.ImportDeclaration)
|
14
19
|
return;
|
15
20
|
const moduleSpecifier = node.moduleSpecifier.text;
|
16
|
-
const file =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
// Example: import { Network, notifyClients } from './notifyClients.js'
|
23
|
-
// The source file for that is actually in './notifyClients.ts'
|
24
|
-
.replace(/\.js$/, '.ts')
|
25
|
-
: moduleSpecifier;
|
21
|
+
const file = resolve({
|
22
|
+
moduleSpecifier,
|
23
|
+
sourceFilePath,
|
24
|
+
tsConfigFilePath,
|
25
|
+
tsConfig,
|
26
|
+
});
|
26
27
|
try {
|
27
28
|
const s = statSync(file);
|
28
29
|
if (!s.isDirectory())
|
@@ -34,9 +35,49 @@ export const findDependencies = (sourceFile, imports = [], visited = []) => {
|
|
34
35
|
}
|
35
36
|
};
|
36
37
|
ts.forEachChild(fileNode, parseChild);
|
37
|
-
visited.push(
|
38
|
+
visited.push(sourceFilePath);
|
38
39
|
for (const file of imports) {
|
39
|
-
findDependencies(
|
40
|
+
findDependencies({
|
41
|
+
sourceFilePath: file,
|
42
|
+
imports,
|
43
|
+
visited,
|
44
|
+
tsConfigFilePath,
|
45
|
+
});
|
40
46
|
}
|
41
47
|
return imports;
|
42
48
|
};
|
49
|
+
const resolve = ({ moduleSpecifier, sourceFilePath, tsConfigFilePath, tsConfig, }) => {
|
50
|
+
if (moduleSpecifier.startsWith('.'))
|
51
|
+
return (path
|
52
|
+
.resolve(path.parse(sourceFilePath).dir, moduleSpecifier)
|
53
|
+
// In ECMA Script modules, all imports from local files must have an extension.
|
54
|
+
// See https://nodejs.org/api/esm.html#mandatory-file-extensions
|
55
|
+
// So we need to replace the `.js` in the import specification to find the TypeScript source for the file.
|
56
|
+
// Example: import { Network, notifyClients } from './notifyClients.js'
|
57
|
+
// The source file for that is actually in './notifyClients.ts'
|
58
|
+
.replace(/\.js$/, '.ts'));
|
59
|
+
if (tsConfigFilePath !== undefined &&
|
60
|
+
tsConfig?.compilerOptions?.paths !== undefined) {
|
61
|
+
for (const [key, value] of Object.entries(tsConfig.compilerOptions.paths)) {
|
62
|
+
const [resolvedPath] = value;
|
63
|
+
if (resolvedPath === undefined)
|
64
|
+
continue;
|
65
|
+
// Exact match
|
66
|
+
if (moduleSpecifier === key) {
|
67
|
+
return path.join(path.parse(tsConfigFilePath).dir, tsConfig.compilerOptions.baseUrl, resolvedPath);
|
68
|
+
}
|
69
|
+
// Wildcard match
|
70
|
+
if (!key.includes('*'))
|
71
|
+
continue;
|
72
|
+
const rx = new RegExp(`^${key.replace('*', '(?<wildcard>.*)')}`);
|
73
|
+
const maybeMatch = rx.exec(moduleSpecifier);
|
74
|
+
if (maybeMatch?.groups?.wildcard === undefined)
|
75
|
+
continue;
|
76
|
+
return (path
|
77
|
+
.resolve(path.parse(tsConfigFilePath).dir, tsConfig.compilerOptions.baseUrl, resolvedPath.replace('*', maybeMatch.groups.wildcard))
|
78
|
+
// Same as above, replace `.js` with `.ts`
|
79
|
+
.replace(/\.js$/, '.ts'));
|
80
|
+
}
|
81
|
+
}
|
82
|
+
return moduleSpecifier;
|
83
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import assert from 'node:assert/strict';
|
2
|
+
import path from 'node:path';
|
3
|
+
import { describe, it } from 'node:test';
|
4
|
+
import { URL } from 'node:url';
|
5
|
+
import { findDependencies } from './findDependencies.js';
|
6
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
7
|
+
void describe('findDependencies()', () => {
|
8
|
+
void it('should honor tsconfig.json paths', () => {
|
9
|
+
const dependencies = findDependencies({
|
10
|
+
sourceFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'lambda.ts'),
|
11
|
+
tsConfigFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'tsconfig.json'),
|
12
|
+
});
|
13
|
+
assert.equal(dependencies.includes(path.join(__dirname, 'test-data', 'resolve-paths', 'foo', 'index.ts')), true, 'Should include the index.ts file');
|
14
|
+
assert.equal(dependencies.includes(path.join(__dirname, 'test-data', 'resolve-paths', 'foo', '2.ts')), true, 'Should include the module file');
|
15
|
+
});
|
16
|
+
});
|
@@ -9,11 +9,11 @@ const tmpDir = os.tmpdir();
|
|
9
9
|
void describe('packLambda()', () => {
|
10
10
|
// See https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/issues/93#issuecomment-2042201321
|
11
11
|
void it('should fail if it imports from a folder on the same level that has the same name as the handler module', async () => assert.rejects(async () => packLambda({
|
12
|
-
|
13
|
-
|
12
|
+
sourceFilePath: path.join(dirname(fileURLToPath(import.meta.url)), 'test-data', 'module-folder-named-like-handler-bug', 'same-level', 'acme.ts'),
|
13
|
+
zipFilePath: path.join(await fs.mkdtemp(`${tmpDir}${path.sep}`), 'acme.zip'),
|
14
14
|
}), ImportFromFolderNameError));
|
15
15
|
void it('should not fail if it a folder with the same name is on a different level', async () => assert.doesNotReject(async () => packLambda({
|
16
|
-
|
17
|
-
|
16
|
+
sourceFilePath: path.join(dirname(fileURLToPath(import.meta.url)), 'test-data', 'module-folder-named-like-handler-bug', 'different-level', 'lambda', 'acme.ts'),
|
17
|
+
zipFilePath: path.join(await fs.mkdtemp(`${tmpDir}${path.sep}`), 'acme.zip'),
|
18
18
|
})));
|
19
19
|
});
|
package/dist/src/packLambda.d.ts
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
export type PackedLambda = {
|
2
2
|
id: string;
|
3
|
-
|
3
|
+
zipFilePath: string;
|
4
4
|
handler: string;
|
5
5
|
hash: string;
|
6
6
|
};
|
7
7
|
/**
|
8
8
|
* In the bundle we only include code that's not in the layer.
|
9
9
|
*/
|
10
|
-
export declare const packLambda: ({
|
11
|
-
|
12
|
-
|
10
|
+
export declare const packLambda: ({ sourceFilePath, zipFilePath, tsConfigFilePath, debug, progress, }: {
|
11
|
+
sourceFilePath: string;
|
12
|
+
zipFilePath: string;
|
13
|
+
/**
|
14
|
+
* Pass the path to the tsconfig.json file if you want to use paths from the tsconfig.json file.
|
15
|
+
*/
|
16
|
+
tsConfigFilePath?: string;
|
13
17
|
debug?: (label: string, info: string) => void;
|
14
18
|
progress?: (label: string, info: string) => void;
|
15
19
|
}) => Promise<{
|
package/dist/src/packLambda.js
CHANGED
@@ -7,8 +7,8 @@ import yazl from 'yazl';
|
|
7
7
|
import { checkSumOfFiles } from './checksumOfFiles.js';
|
8
8
|
import { commonParent } from './commonParent.js';
|
9
9
|
import { findDependencies } from './findDependencies.js';
|
10
|
-
const removeCommonAncestor = (parentDir) => (
|
11
|
-
const p = parse(
|
10
|
+
const removeCommonAncestor = (parentDir) => (filePath) => {
|
11
|
+
const p = parse(filePath);
|
12
12
|
const jsFileName = [
|
13
13
|
p.dir.replace(parentDir.slice(0, parentDir.length - 1), ''),
|
14
14
|
`${p.name}.js`,
|
@@ -21,12 +21,15 @@ const removeCommonAncestor = (parentDir) => (file) => {
|
|
21
21
|
/**
|
22
22
|
* In the bundle we only include code that's not in the layer.
|
23
23
|
*/
|
24
|
-
export const packLambda = async ({
|
25
|
-
const deps = findDependencies(
|
26
|
-
|
24
|
+
export const packLambda = async ({ sourceFilePath, zipFilePath, tsConfigFilePath, debug, progress, }) => {
|
25
|
+
const deps = findDependencies({
|
26
|
+
sourceFilePath,
|
27
|
+
tsConfigFilePath,
|
28
|
+
});
|
29
|
+
const lambdaFiles = [sourceFilePath, ...deps];
|
27
30
|
const zipfile = new yazl.ZipFile();
|
28
31
|
const stripCommon = removeCommonAncestor(commonParent(lambdaFiles));
|
29
|
-
const handler = stripCommon(
|
32
|
+
const handler = stripCommon(sourceFilePath);
|
30
33
|
// Make sure that the handler does not import from a folder with the same name in the folder
|
31
34
|
const handlerInfo = path.parse(handler);
|
32
35
|
const handlerName = handlerInfo.name;
|
@@ -61,13 +64,15 @@ export const packLambda = async ({ sourceFile, zipFile, debug, progress, }) => {
|
|
61
64
|
}), 'utf-8'), 'package.json');
|
62
65
|
progress?.(`added`, 'package.json');
|
63
66
|
await new Promise((resolve) => {
|
64
|
-
zipfile.outputStream
|
67
|
+
zipfile.outputStream
|
68
|
+
.pipe(createWriteStream(zipFilePath))
|
69
|
+
.on('close', () => {
|
65
70
|
resolve();
|
66
71
|
});
|
67
72
|
zipfile.end();
|
68
73
|
});
|
69
|
-
progress?.(`written`,
|
70
|
-
return { handler: stripCommon(
|
74
|
+
progress?.(`written`, zipFilePath);
|
75
|
+
return { handler: stripCommon(sourceFilePath), hash };
|
71
76
|
};
|
72
77
|
/**
|
73
78
|
* @see https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/issues/93#issuecomment-2042201321
|
@@ -1,2 +1,18 @@
|
|
1
1
|
import { type PackedLambda } from './packLambda.js';
|
2
|
-
export declare const packLambdaFromPath: (id
|
2
|
+
export declare const packLambdaFromPath: ({ id, sourceFilePath, handlerFunction: handlerFunctionArg, baseDir: baseDirArg, distDir: distDirArg, tsConfigFilePath, }: {
|
3
|
+
id: string;
|
4
|
+
sourceFilePath: string;
|
5
|
+
handlerFunction?: string;
|
6
|
+
/**
|
7
|
+
* @default process.cwd()
|
8
|
+
*/
|
9
|
+
baseDir?: string;
|
10
|
+
/**
|
11
|
+
* @default ${baseDir}/dist/lambdas
|
12
|
+
*/
|
13
|
+
distDir?: string;
|
14
|
+
/**
|
15
|
+
* Pass the path to the tsconfig.json file if you want to use paths from the tsconfig.json file.
|
16
|
+
*/
|
17
|
+
tsConfigFilePath?: string;
|
18
|
+
}) => Promise<PackedLambda>;
|
@@ -1,15 +1,10 @@
|
|
1
1
|
import { mkdir } from 'node:fs/promises';
|
2
2
|
import path from 'node:path';
|
3
3
|
import { packLambda } from './packLambda.js';
|
4
|
-
export const packLambdaFromPath = async (id,
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
baseDir = process.cwd(),
|
9
|
-
/**
|
10
|
-
* @default ${baseDir}/dist/lambdas
|
11
|
-
*/
|
12
|
-
distDir = path.join(process.cwd(), 'dist', 'lambdas')) => {
|
4
|
+
export const packLambdaFromPath = async ({ id, sourceFilePath, handlerFunction: handlerFunctionArg, baseDir: baseDirArg, distDir: distDirArg, tsConfigFilePath, }) => {
|
5
|
+
const distDir = distDirArg ?? path.join(process.cwd(), 'dist', 'lambdas');
|
6
|
+
const baseDir = baseDirArg ?? process.cwd();
|
7
|
+
const handlerFunction = handlerFunctionArg ?? 'handler';
|
13
8
|
try {
|
14
9
|
await mkdir(distDir, {
|
15
10
|
recursive: true,
|
@@ -20,12 +15,13 @@ distDir = path.join(process.cwd(), 'dist', 'lambdas')) => {
|
|
20
15
|
}
|
21
16
|
const zipFile = path.join(distDir, `${id}.zip`);
|
22
17
|
const { handler, hash } = await packLambda({
|
23
|
-
|
24
|
-
zipFile,
|
18
|
+
sourceFilePath: path.join(baseDir, sourceFilePath),
|
19
|
+
zipFilePath: zipFile,
|
20
|
+
tsConfigFilePath,
|
25
21
|
});
|
26
22
|
return {
|
27
23
|
id,
|
28
|
-
zipFile,
|
24
|
+
zipFilePath: zipFile,
|
29
25
|
handler: handler.replace('.js', `.${handlerFunction}`),
|
30
26
|
hash,
|
31
27
|
};
|
package/dist/src/packLayer.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
export type PackedLayer = {
|
2
|
-
|
2
|
+
layerZipFilePath: string;
|
3
3
|
hash: string;
|
4
4
|
};
|
5
5
|
export declare const packLayer: ({ id, dependencies, baseDir, distDir, installCommand, }: {
|
@@ -17,7 +17,7 @@ export declare const packLayer: ({ id, dependencies, baseDir, distDir, installCo
|
|
17
17
|
* Returns the command to run, the first element is the command (e.g. `npm`) and the rest are its arguments.
|
18
18
|
*/
|
19
19
|
installCommand?: (args: {
|
20
|
-
|
21
|
-
|
20
|
+
packageFilePath: string;
|
21
|
+
packageLockFilePath: string;
|
22
22
|
}) => [string, ...Array<string>];
|
23
23
|
}) => Promise<PackedLayer>;
|
package/dist/src/packLayer.js
CHANGED
@@ -53,8 +53,8 @@ export const packLayer = async ({ id, dependencies, baseDir, distDir, installCom
|
|
53
53
|
}
|
54
54
|
await new Promise((resolve, reject) => {
|
55
55
|
const [cmd, ...args] = installCommand?.({
|
56
|
-
|
57
|
-
|
56
|
+
packageFilePath: packageJSON,
|
57
|
+
packageLockFilePath: packageLockJsonFile,
|
58
58
|
}) ?? [
|
59
59
|
'npm',
|
60
60
|
hasLockFile ? 'ci' : 'i',
|
@@ -91,7 +91,7 @@ export const packLayer = async ({ id, dependencies, baseDir, distDir, installCom
|
|
91
91
|
zipfile.end();
|
92
92
|
});
|
93
93
|
return {
|
94
|
-
|
94
|
+
layerZipFilePath: zipFileName,
|
95
95
|
hash: checkSumOfStrings([
|
96
96
|
JSON.stringify(dependencies),
|
97
97
|
await checkSumOfFiles(checkSumFiles),
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@bifravst/aws-cdk-lambda-helpers",
|
3
|
-
"version": "
|
3
|
+
"version": "2.0.1",
|
4
4
|
"description": "Helper functions which simplify working with TypeScript lambdas for AWS CDK.",
|
5
5
|
"exports": {
|
6
6
|
".": {
|
@@ -110,7 +110,7 @@
|
|
110
110
|
"@swc/core": "1.9.2",
|
111
111
|
"glob": "11.0.0",
|
112
112
|
"typescript": "5.6.3",
|
113
|
-
"yazl": "
|
113
|
+
"yazl": "3.3.0"
|
114
114
|
},
|
115
115
|
"peerDependencies": {
|
116
116
|
"@bifravst/aws-ssm-settings-helpers": "^1.2.61",
|
package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/acme/lib.d.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export declare const hello: () => string;
|
@@ -1 +0,0 @@
|
|
1
|
-
export const hello = () => 'Hello World!';
|
package/dist/src/test-data/module-folder-named-like-handler-bug/different-level/lambda/acme.d.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export declare const handler: () => string;
|
@@ -1 +0,0 @@
|
|
1
|
-
export declare const hello: () => string;
|
@@ -1 +0,0 @@
|
|
1
|
-
export const hello = () => 'Hello World!';
|
@@ -1 +0,0 @@
|
|
1
|
-
export declare const handler: () => string;
|