@bifravst/aws-cdk-lambda-helpers 2.0.0 → 2.1.0

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.
@@ -1,9 +1,17 @@
1
1
  /**
2
2
  * Resolve project-level dependencies for the given file using TypeScript compiler API
3
3
  */
4
- export declare const findDependencies: ({ sourceFilePath, tsConfigFilePath, imports: importsArg, visited: visitedArg, }: {
4
+ export declare const findDependencies: (args: {
5
5
  sourceFilePath: string;
6
- tsConfigFilePath?: string;
7
6
  imports?: string[];
8
7
  visited?: string[];
9
- }) => string[];
8
+ tsConfigFilePath?: string;
9
+ importsSubpathPatterns?: Record<string, string>;
10
+ }) => {
11
+ dependencies: string[];
12
+ /**
13
+ * A map of import subpath patterns to their resolved paths
14
+ * @see https://nodejs.org/api/packages.html#subpath-patterns
15
+ */
16
+ importsSubpathPatterns: Record<string, string>;
17
+ };
@@ -4,30 +4,36 @@ 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 = ({ sourceFilePath, tsConfigFilePath, imports: importsArg, visited: visitedArg, }) => {
8
- const visited = visitedArg ?? [];
9
- const imports = importsArg ?? [];
7
+ export const findDependencies = (args) => {
8
+ const sourceFilePath = args.sourceFilePath;
9
+ const visited = args.visited ?? [];
10
+ const dependencies = args.imports ?? [];
11
+ let importsSubpathPatterns = args.importsSubpathPatterns ?? {};
10
12
  if (visited.includes(sourceFilePath))
11
- return imports;
13
+ return { dependencies, importsSubpathPatterns };
14
+ const tsConfigFilePath = args.tsConfigFilePath;
12
15
  const tsConfig = tsConfigFilePath !== undefined
13
16
  ? JSON.parse(readFileSync(tsConfigFilePath, 'utf-8').toString())
14
17
  : undefined;
15
18
  const fileNode = ts.createSourceFile(sourceFilePath, readFileSync(sourceFilePath, 'utf-8').toString(), ts.ScriptTarget.ES2022,
16
19
  /*setParentNodes */ true);
17
20
  const parseChild = (node) => {
18
- if (node.kind !== ts.SyntaxKind.ImportDeclaration)
21
+ if (node.kind !== ts.SyntaxKind.ImportDeclaration &&
22
+ node.kind !== ts.SyntaxKind.ExportDeclaration)
19
23
  return;
20
24
  const moduleSpecifier = node.moduleSpecifier.text;
21
- const file = resolve({
25
+ const { resolvedPath: file, importsSubpathPatterns: updatedImportsSubpathPatterns, } = resolve({
22
26
  moduleSpecifier,
23
27
  sourceFilePath,
24
28
  tsConfigFilePath,
25
29
  tsConfig,
30
+ importsSubpathPatterns,
26
31
  });
32
+ importsSubpathPatterns = updatedImportsSubpathPatterns;
27
33
  try {
28
34
  const s = statSync(file);
29
35
  if (!s.isDirectory())
30
- imports.push(file);
36
+ dependencies.push(file);
31
37
  }
32
38
  catch {
33
39
  // Module or file not found
@@ -36,26 +42,30 @@ export const findDependencies = ({ sourceFilePath, tsConfigFilePath, imports: im
36
42
  };
37
43
  ts.forEachChild(fileNode, parseChild);
38
44
  visited.push(sourceFilePath);
39
- for (const file of imports) {
45
+ for (const file of dependencies) {
40
46
  findDependencies({
41
47
  sourceFilePath: file,
42
- imports,
48
+ imports: dependencies,
43
49
  visited,
44
50
  tsConfigFilePath,
51
+ importsSubpathPatterns,
45
52
  });
46
53
  }
47
- return imports;
54
+ return { dependencies, importsSubpathPatterns };
48
55
  };
49
- const resolve = ({ moduleSpecifier, sourceFilePath, tsConfigFilePath, tsConfig, }) => {
56
+ const resolve = ({ moduleSpecifier, sourceFilePath, tsConfigFilePath, tsConfig, importsSubpathPatterns, }) => {
50
57
  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'));
58
+ return {
59
+ resolvedPath: path
60
+ .resolve(path.parse(sourceFilePath).dir, moduleSpecifier)
61
+ // In ECMA Script modules, all imports from local files must have an extension.
62
+ // See https://nodejs.org/api/esm.html#mandatory-file-extensions
63
+ // So we need to replace the `.js` in the import specification to find the TypeScript source for the file.
64
+ // Example: import { Network, notifyClients } from './notifyClients.js'
65
+ // The source file for that is actually in './notifyClients.ts'
66
+ .replace(/\.js$/, '.ts'),
67
+ importsSubpathPatterns,
68
+ };
59
69
  if (tsConfigFilePath !== undefined &&
60
70
  tsConfig?.compilerOptions?.paths !== undefined) {
61
71
  for (const [key, value] of Object.entries(tsConfig.compilerOptions.paths)) {
@@ -64,7 +74,18 @@ const resolve = ({ moduleSpecifier, sourceFilePath, tsConfigFilePath, tsConfig,
64
74
  continue;
65
75
  // Exact match
66
76
  if (moduleSpecifier === key) {
67
- return path.join(path.parse(tsConfigFilePath).dir, tsConfig.compilerOptions.baseUrl, resolvedPath);
77
+ const fullResolvedPath = path.join(path.parse(tsConfigFilePath).dir, tsConfig.compilerOptions.baseUrl, resolvedPath);
78
+ return {
79
+ resolvedPath: fullResolvedPath,
80
+ importsSubpathPatterns: {
81
+ ...importsSubpathPatterns,
82
+ [key]: [
83
+ tsConfig.compilerOptions.baseUrl,
84
+ path.sep,
85
+ resolvedPath.replace(/\.ts$/, '.js'),
86
+ ].join(''),
87
+ },
88
+ };
68
89
  }
69
90
  // Wildcard match
70
91
  if (!key.includes('*'))
@@ -73,11 +94,24 @@ const resolve = ({ moduleSpecifier, sourceFilePath, tsConfigFilePath, tsConfig,
73
94
  const maybeMatch = rx.exec(moduleSpecifier);
74
95
  if (maybeMatch?.groups?.wildcard === undefined)
75
96
  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'));
97
+ return {
98
+ resolvedPath: path
99
+ .resolve(path.parse(tsConfigFilePath).dir, tsConfig.compilerOptions.baseUrl, resolvedPath.replace('*', maybeMatch.groups.wildcard))
100
+ // Same as above, replace `.js` with `.ts`
101
+ .replace(/\.js$/, '.ts'),
102
+ importsSubpathPatterns: {
103
+ ...importsSubpathPatterns,
104
+ [key]: [
105
+ tsConfig.compilerOptions.baseUrl,
106
+ path.sep,
107
+ resolvedPath.replace(/\.ts$/, '.js'),
108
+ ].join(''),
109
+ },
110
+ };
80
111
  }
81
112
  }
82
- return moduleSpecifier;
113
+ return {
114
+ resolvedPath: moduleSpecifier,
115
+ importsSubpathPatterns,
116
+ };
83
117
  };
@@ -6,11 +6,22 @@ import { findDependencies } from './findDependencies.js';
6
6
  const __dirname = new URL('.', import.meta.url).pathname;
7
7
  void describe('findDependencies()', () => {
8
8
  void it('should honor tsconfig.json paths', () => {
9
- const dependencies = findDependencies({
9
+ const { dependencies } = findDependencies({
10
10
  sourceFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'lambda.ts'),
11
11
  tsConfigFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'tsconfig.json'),
12
12
  });
13
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', '1.ts')), true, 'Should include the module referenced in the index.ts file');
14
15
  assert.equal(dependencies.includes(path.join(__dirname, 'test-data', 'resolve-paths', 'foo', '2.ts')), true, 'Should include the module file');
15
16
  });
17
+ void it('should return an import map', () => {
18
+ const { importsSubpathPatterns } = findDependencies({
19
+ sourceFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'lambda.ts'),
20
+ tsConfigFilePath: path.join(__dirname, 'test-data', 'resolve-paths', 'tsconfig.json'),
21
+ });
22
+ assert.deepEqual(importsSubpathPatterns, {
23
+ '#foo': './foo/index.js',
24
+ '#foo/*': './foo/*',
25
+ });
26
+ });
16
27
  });
@@ -22,7 +22,7 @@ const removeCommonAncestor = (parentDir) => (filePath) => {
22
22
  * In the bundle we only include code that's not in the layer.
23
23
  */
24
24
  export const packLambda = async ({ sourceFilePath, zipFilePath, tsConfigFilePath, debug, progress, }) => {
25
- const deps = findDependencies({
25
+ const { dependencies: deps, importsSubpathPatterns } = findDependencies({
26
26
  sourceFilePath,
27
27
  tsConfigFilePath,
28
28
  });
@@ -61,6 +61,7 @@ export const packLambda = async ({ sourceFilePath, zipFilePath, tsConfigFilePath
61
61
  // Mark it as ES module
62
62
  zipfile.addBuffer(Buffer.from(JSON.stringify({
63
63
  type: 'module',
64
+ imports: importsSubpathPatterns,
64
65
  }), 'utf-8'), 'package.json');
65
66
  progress?.(`added`, 'package.json');
66
67
  await new Promise((resolve) => {
@@ -1,2 +1,18 @@
1
1
  import { type PackedLambda } from './packLambda.js';
2
- export declare const packLambdaFromPath: (id: string, sourceFilePath: string, handlerFunction?: string, baseDir?: string, distDir?: string) => Promise<PackedLambda>;
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, sourceFilePath, handlerFunction = 'handler',
5
- /**
6
- * @default process.cwd()
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,
@@ -22,6 +17,7 @@ distDir = path.join(process.cwd(), 'dist', 'lambdas')) => {
22
17
  const { handler, hash } = await packLambda({
23
18
  sourceFilePath: path.join(baseDir, sourceFilePath),
24
19
  zipFilePath: zipFile,
20
+ tsConfigFilePath,
25
21
  });
26
22
  return {
27
23
  id,
@@ -0,0 +1 @@
1
+ export declare const foo: () => number;
@@ -0,0 +1 @@
1
+ export const foo = () => 42;
@@ -0,0 +1 @@
1
+ export declare const foo2: () => number;
@@ -0,0 +1 @@
1
+ export const foo2 = () => 17;
@@ -0,0 +1 @@
1
+ export { foo } from './1.js';
@@ -0,0 +1 @@
1
+ export { foo } from './1.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifravst/aws-cdk-lambda-helpers",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Helper functions which simplify working with TypeScript lambdas for AWS CDK.",
5
5
  "exports": {
6
6
  ".": {
@@ -51,7 +51,7 @@
51
51
  "author": "Nordic Semiconductor ASA | nordicsemi.no",
52
52
  "license": "BSD-3-Clause",
53
53
  "devDependencies": {
54
- "@aws-sdk/client-cloudformation": "3.691.0",
54
+ "@aws-sdk/client-cloudformation": "3.692.0",
55
55
  "@bifravst/cloudformation-helpers": "9.1.1",
56
56
  "@bifravst/eslint-config-typescript": "6.1.18",
57
57
  "@bifravst/from-env": "3.0.2",