Skip to content

Commit 392c3e0

Browse files
committed
feat(package): include additional files into the final function package
1 parent 51128ae commit 392c3e0

File tree

7 files changed

+236
-160
lines changed

7 files changed

+236
-160
lines changed

README.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,18 @@ client
100100
});
101101
```
102102

103+
---
104+
103105
### Environment variable
104106

105107
Lambdas are executed in worker threads. Only variables declared in your `serverless.yml` are injected into `process.env` except `IS_LOCAL`, `LOCAL_PORT` and `NODE_ENV`.
106108

107-
### Disable functions
109+
---
110+
111+
### Extended properties
108112

109-
Adding the param `online: false` will omit the deployement of your Lambda.
113+
- `online`
114+
Adding the param `online: false` will omit the deployement of your Lambda.
110115

111116
```yaml
112117
functions:
@@ -115,9 +120,25 @@ functions:
115120
online: false
116121
```
117122

123+
- `files`
124+
include additionnal files into the package.
125+
126+
```yaml
127+
functions:
128+
myAwsomeLambda:
129+
handler: src/handlers/awsomeLambda.default
130+
files:
131+
- ./resources/some/file.png
132+
- ./resources/anotherFile.pdf
133+
```
134+
135+
- `virtualEnvs`
136+
on key-value object which will only be available inside [defineConfig](resources/defineConfig.md).
137+
by default virtualEnvs are inherited from custom > virtualEnvs if exists.
138+
118139
---
119140

120-
## Advanced configuration:
141+
### Advanced configuration:
121142

122143
To have more control over the plugin you can passe a config file via `configPath` param in plugin options:
123144

@@ -138,7 +159,7 @@ See [defineConfig](resources/defineConfig.md) for advanced configuration.
138159

139160
---
140161

141-
## Plugins:
162+
### Plugins:
142163

143164
- [AWS Local S3](resources/s3.md)
144165
- [AWS Local SNS](resources/sns.md)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-aws-lambda",
3-
"version": "4.3.0",
3+
"version": "4.3.1",
44
"description": "AWS Application Load Balancer and API Gateway - Lambda dev tool for Serverless. Allows Express synthax in handlers. Supports packaging, local invoking and offline real ALB and APG lambda server mocking.",
55
"author": "Inqnuam",
66
"license": "MIT",
@@ -46,7 +46,7 @@
4646
"dependencies": {
4747
"@types/serverless": "^3.12.10",
4848
"archiver": "^5.3.1",
49-
"esbuild": "0.17.10",
49+
"esbuild": "0.17.11",
5050
"serve-static": "^1.15.0"
5151
},
5252
"devDependencies": {

src/index.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ class ServerlessAwsLambda extends Daemon {
5050
// @ts-ignore
5151
this.isDeploying = this.serverless.processedInput.commands.includes("deploy");
5252

53-
this.nodeVersion = this.serverless.service.provider.runtime?.replace(/[^0-9]/g, "");
54-
if (!this.nodeVersion) {
55-
throw new Error("Please provide NodeJS runtime version inside your serverless.yml > provider > runtime");
53+
if (!this.serverless.service.provider.runtime) {
54+
throw new Error("Please provide 'runtime' inside your serverless.yml > provider > runtime");
55+
} else if (this.serverless.service.provider.runtime.startsWith("node")) {
56+
this.nodeVersion = this.serverless.service.provider.runtime?.replace(/[^0-9]/g, "");
57+
} else {
58+
this.nodeVersion = "14";
5659
}
5760
this.watch = !this.isPackaging && !this.isDeploying;
5861
if (!this.watch) {
@@ -64,6 +67,7 @@ class ServerlessAwsLambda extends Daemon {
6467
properties: {
6568
virtualEnvs: { type: "object" },
6669
online: { type: "boolean" },
70+
files: { type: "array" },
6771
},
6872
});
6973

@@ -223,8 +227,14 @@ class ServerlessAwsLambda extends Daemon {
223227
for (const l of packageLambdas.filter((x) => x.online)) {
224228
const slsDeclaration = this.serverless.service.getFunction(l.name) as Serverless.FunctionDefinitionHandler;
225229

230+
if (typeof slsDeclaration.package?.artifact == "string") {
231+
continue;
232+
}
233+
234+
// @ts-ignore
235+
const filesToInclude = slsDeclaration.files;
226236
const zipableBundledFilePath = l.esOutputPath.slice(0, -3);
227-
const zipOutputPath = await zip(zipableBundledFilePath, l.outName);
237+
const zipOutputPath = await zip(zipableBundledFilePath, l.outName, filesToInclude);
228238

229239
// @ts-ignore
230240
slsDeclaration.package = { ...slsDeclaration.package, disable: true, artifact: zipOutputPath };
@@ -278,16 +288,23 @@ class ServerlessAwsLambda extends Daemon {
278288
if (this.invokeName) {
279289
functionsNames = functionsNames.filter((x) => x == this.invokeName);
280290
}
291+
const defaultRuntime = this.serverless.service.provider.runtime;
281292
const resources = getResources(this.serverless);
282293
// @ts-ignore
283294
const Outputs = this.serverless.service.resources?.Outputs;
284295
const lambdas = functionsNames.reduce((accum: any[], funcName: string) => {
285296
const lambda = funcs[funcName];
286297

298+
if (lambda.runtime && !lambda.runtime.startsWith("node")) {
299+
return accum;
300+
} else if (!defaultRuntime?.startsWith("node") && (!lambda.runtime || !lambda.runtime.startsWith("node"))) {
301+
return accum;
302+
}
287303
const handlerPath = (lambda as Serverless.FunctionDefinitionHandler).handler;
288-
const lastPointIndex = handlerPath.lastIndexOf(".");
289-
const handlerName = handlerPath.slice(lastPointIndex + 1);
290-
const esEntryPoint = path.join(cwd, handlerPath.slice(0, lastPointIndex));
304+
const ext = path.extname(handlerPath);
305+
const handlerName = ext.slice(1);
306+
const esEntryPoint = path.posix.resolve(handlerPath.replace(ext, ""));
307+
291308
const region = this.runtimeConfig.environment.AWS_REGION ?? this.runtimeConfig.environment.REGION;
292309

293310
const slsDeclaration: any = this.serverless.service.getFunction(funcName);

src/lib/parseEvents/ddbStream.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { log } from "../utils/colorize";
22
import { parseDestination } from "./index";
33

4+
enum StreamProps {
5+
batchSize = 100,
6+
minRecordAge = 60,
7+
maxRecordAge = 604800,
8+
}
49
const getTableNameFromResources = (ddbStreamTables: any, Outputs: any, obj: any) => {
510
const [key, value] = Object.entries(obj)?.[0];
611

@@ -74,7 +79,31 @@ export const parseDdbStreamDefinitions = (Outputs: any, resources: any, event: a
7479
}
7580

7681
if (parsedEvent.TableName) {
77-
parsedEvent.batchSize = val.batchSize ?? 100;
82+
if (!isNaN(val.batchSize) && val.batchSize > 0) {
83+
parsedEvent.batchSize = val.batchSize;
84+
} else {
85+
parsedEvent.batchSize = StreamProps.batchSize;
86+
}
87+
88+
if (!isNaN(val.batchWindow) && val.batchWindow > 0) {
89+
parsedEvent.batchWindow = val.batchWindow;
90+
}
91+
92+
if (
93+
!isNaN(val.maximumRecordAgeInSeconds) &&
94+
parsedEvent.maximumRecordAgeInSeconds >= StreamProps.minRecordAge &&
95+
parsedEvent.maximumRecordAgeInSeconds <= StreamProps.maxRecordAge
96+
) {
97+
parsedEvent.maximumRecordAgeInSeconds = val.maximumRecordAgeInSeconds;
98+
}
99+
100+
if (!isNaN(val.maximumRetryAttempts) && val.maximumRetryAttempts > 0) {
101+
parsedEvent.maximumRetryAttempts = val.maximumRetryAttempts;
102+
}
103+
104+
if ("bisectBatchOnFunctionError" in val) {
105+
parsedEvent.bisectBatchOnFunctionError = val.bisectBatchOnFunctionError;
106+
}
78107

79108
if (val.functionResponseType) {
80109
parsedEvent.functionResponseType = val.functionResponseType;

src/lib/runtime/lambdaMock.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export interface IDdbEvent {
2929
StreamEnabled: boolean;
3030
StreamViewType?: string;
3131
batchSize?: number;
32+
batchWindow?: number;
33+
maximumRecordAgeInSeconds?: number;
34+
maximumRetryAttempts?: number;
35+
bisectBatchOnFunctionError?: boolean;
3236
functionResponseType?: string;
3337
filterPatterns?: any;
3438
onFailure?: IDestination;

src/lib/utils/zip.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,36 @@
11
import archiver from "archiver";
22
import { createReadStream, createWriteStream } from "fs";
33
import { access } from "fs/promises";
4-
import { basename, dirname } from "path";
4+
import path from "path";
55

6-
export const zip = (filePath: string, zipName: string) => {
7-
return new Promise((resolve) => {
6+
export const zip = (filePath: string, zipName: string, include?: string[]) => {
7+
return new Promise(async (resolve) => {
88
const archive = archiver("zip", {
99
zlib: { level: 9 },
1010
});
1111

12-
const zipOutputPath = `${dirname(filePath)}/${zipName}.zip`;
12+
const zipOutputPath = `${path.dirname(filePath)}/${zipName}.zip`;
1313
const output = createWriteStream(zipOutputPath);
1414

1515
archive.on("finish", () => {
1616
resolve(zipOutputPath);
1717
});
1818
archive.pipe(output);
1919

20-
const fileName = basename(filePath) + ".js";
20+
const fileName = path.basename(filePath) + ".js";
2121
archive.append(createReadStream(`${filePath}.js`), { name: fileName });
2222

23-
// NOTE: do we really need sourcemaps in AWS ?
24-
// const sourceMapPath = filePath + ".js.map";
25-
// try {
26-
// await access(sourceMapPath);
27-
// archive.append(createReadStream(`${filePath}.js.map`), { name: fileName + ".map" });
28-
// } catch (error) {}
23+
if (include && include.every((x) => typeof x == "string")) {
24+
for (const file of include) {
25+
const includPath = path.resolve(file);
26+
try {
27+
await access(includPath);
28+
archive.append(createReadStream(includPath), { name: file });
29+
} catch (error) {
30+
console.error(error);
31+
}
32+
}
33+
}
2934

3035
archive.finalize();
3136
});

0 commit comments

Comments
 (0)