FormidableLabs / FormidableLabs/serverless-jetpack
Feature: Automagically include other handlers that reuse package in `package.artifact` in trace mode.
- Dominant language
- JavaScript
- Stars
- 281
- Forks
- 14
- PR merge metrics
- No merged PRs in 30d
Description
## Overview
A typical use case is to do something like:
```yml
functions:
foo:
handler: dist/index.handler
# This is fine. `dist/index.js` is already traced.
reuse-same-handler
handler: dist/index.otherHandler
package:
artifact: .serverless/foo.zip
# This will break. `dist/a-different-file.js` is not included.
reuse-same-handler
handler: dist/a-different-file.handler
package:
artifact: .serverless/foo.zip
```
## Tasks
- [ ] Programmatically infer `package.artifact` that are implicated in other live-package functionsl
- [ ] Augment `function.{FN_NAME}.jetpack.trace.include` with these additional handlers to also trace and include them.
- [ ] Add tests
- [ ] Update docs on behavior.
## Starting point
Here's a sample script to accomplish what we want:
```js
"use strict";
/**
* Validate the serverless configs.
*/
const fs = require("fs").promises;
const path = require("path");
const globby = require("globby");
const yaml = require("js-yaml");
const cwd = "/PATH/TO/SLS_CONFIGS";
const main = async () => {
// Assumes multiple configs.
const cfgs = await globby(["serverless*.yml"], { cwd });
await Promise.all(cfgs.map(async (cfg) => {
const parsed = yaml.load(await fs.readFile(path.resolve(cwd, cfg)));
const fns = parsed.functions || {};
// Mapping of bundled handlers
const bundled = {};
Object.entries(fns)
.filter(([, obj]) => !(obj.package || {}).artifact)
.forEach(([name, { handler, jetpack = {} }]) => {
const artifact = `.serverless/${name}.zip`;
bundled[artifact] = bundled[artifact] || new Set();
// Add handler and jetpack includes
bundled[artifact].add(handler.replace(/\.[^\.\/]+$/, ".js"));
((jetpack.trace || {}).include || []).forEach((include) => {
bundled[artifact].add(include);
});
});
// Mapping of artifacts to handlers
const artifacts = {};
Object.entries(fns)
.filter(([, obj]) => (obj.package || {}).artifact)
.forEach(([, { handler, package: { artifact } }]) => {
artifacts[artifact] = artifacts[artifact] || new Set();
// Add the handler _file_.
artifacts[artifact].add(handler.replace(/\.[^\.\/]+$/, ".js"));
});
// Verify each artifact has handler
let missingNum = 0;
Object.entries(artifacts).forEach(([artifact, handlers]) => {
const provided = bundled[artifact];
const needed = artifacts[artifact];
// Find the needed things not provided.
const missing = new Set([...needed].filter((f) => !provided.has(f)));
if (missing.size) {
console.error(`${artifact} is missing ${JSON.stringify([...missing])}`);
missingNum++;
}
});
// Error out.
if (missingNum) {
throw new Error(`Found ${missingNum} artifacts with missing handlers`);
}
}));
};
if (require.main === module) {
main()
.then(() => {
console.log("Finished")
})
.catch((err) => {
console.error(err);
process.exit(1);
});
}
```
Contributor guide
Assessment
This issue has not been assessed yet.